roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @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     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <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,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing && this.store){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * 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
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             // show a message if no listener is registered.
5064             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5065                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5066             }
5067             // loadmask wil be hooked into this..
5068             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5069             return;
5070         }
5071         var r = o.records, t = o.totalRecords || r.length;
5072         if(!options || options.add !== true){
5073             if(this.pruneModifiedRecords){
5074                 this.modified = [];
5075             }
5076             for(var i = 0, len = r.length; i < len; i++){
5077                 r[i].join(this);
5078             }
5079             if(this.snapshot){
5080                 this.data = this.snapshot;
5081                 delete this.snapshot;
5082             }
5083             this.data.clear();
5084             this.data.addAll(r);
5085             this.totalLength = t;
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.totalLength = Math.max(t, this.data.length+r.length);
5090             this.add(r);
5091         }
5092         this.fireEvent("load", this, r, options);
5093         if(options.callback){
5094             options.callback.call(options.scope || this, r, options, true);
5095         }
5096     },
5097
5098
5099     /**
5100      * Loads data from a passed data block. A Reader which understands the format of the data
5101      * must have been configured in the constructor.
5102      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5103      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5104      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5105      */
5106     loadData : function(o, append){
5107         var r = this.reader.readRecords(o);
5108         this.loadRecords(r, {add: append}, true);
5109     },
5110
5111     /**
5112      * Gets the number of cached records.
5113      * <p>
5114      * <em>If using paging, this may not be the total size of the dataset. If the data object
5115      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5116      * the data set size</em>
5117      */
5118     getCount : function(){
5119         return this.data.length || 0;
5120     },
5121
5122     /**
5123      * Gets the total number of records in the dataset as returned by the server.
5124      * <p>
5125      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5126      * the dataset size</em>
5127      */
5128     getTotalCount : function(){
5129         return this.totalLength || 0;
5130     },
5131
5132     /**
5133      * Returns the sort state of the Store as an object with two properties:
5134      * <pre><code>
5135  field {String} The name of the field by which the Records are sorted
5136  direction {String} The sort order, "ASC" or "DESC"
5137      * </code></pre>
5138      */
5139     getSortState : function(){
5140         return this.sortInfo;
5141     },
5142
5143     // private
5144     applySort : function(){
5145         if(this.sortInfo && !this.remoteSort){
5146             var s = this.sortInfo, f = s.field;
5147             var st = this.fields.get(f).sortType;
5148             var fn = function(r1, r2){
5149                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5150                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5151             };
5152             this.data.sort(s.direction, fn);
5153             if(this.snapshot && this.snapshot != this.data){
5154                 this.snapshot.sort(s.direction, fn);
5155             }
5156         }
5157     },
5158
5159     /**
5160      * Sets the default sort column and order to be used by the next load operation.
5161      * @param {String} fieldName The name of the field to sort by.
5162      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5163      */
5164     setDefaultSort : function(field, dir){
5165         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5166     },
5167
5168     /**
5169      * Sort the Records.
5170      * If remote sorting is used, the sort is performed on the server, and the cache is
5171      * reloaded. If local sorting is used, the cache is sorted internally.
5172      * @param {String} fieldName The name of the field to sort by.
5173      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5174      */
5175     sort : function(fieldName, dir){
5176         var f = this.fields.get(fieldName);
5177         if(!dir){
5178             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5179             
5180             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5181                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5182             }else{
5183                 dir = f.sortDir;
5184             }
5185         }
5186         this.sortToggle[f.name] = dir;
5187         this.sortInfo = {field: f.name, direction: dir};
5188         if(!this.remoteSort){
5189             this.applySort();
5190             this.fireEvent("datachanged", this);
5191         }else{
5192             this.load(this.lastOptions);
5193         }
5194     },
5195
5196     /**
5197      * Calls the specified function for each of the Records in the cache.
5198      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5199      * Returning <em>false</em> aborts and exits the iteration.
5200      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5201      */
5202     each : function(fn, scope){
5203         this.data.each(fn, scope);
5204     },
5205
5206     /**
5207      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5208      * (e.g., during paging).
5209      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5210      */
5211     getModifiedRecords : function(){
5212         return this.modified;
5213     },
5214
5215     // private
5216     createFilterFn : function(property, value, anyMatch){
5217         if(!value.exec){ // not a regex
5218             value = String(value);
5219             if(value.length == 0){
5220                 return false;
5221             }
5222             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5223         }
5224         return function(r){
5225             return value.test(r.data[property]);
5226         };
5227     },
5228
5229     /**
5230      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5231      * @param {String} property A field on your records
5232      * @param {Number} start The record index to start at (defaults to 0)
5233      * @param {Number} end The last record index to include (defaults to length - 1)
5234      * @return {Number} The sum
5235      */
5236     sum : function(property, start, end){
5237         var rs = this.data.items, v = 0;
5238         start = start || 0;
5239         end = (end || end === 0) ? end : rs.length-1;
5240
5241         for(var i = start; i <= end; i++){
5242             v += (rs[i].data[property] || 0);
5243         }
5244         return v;
5245     },
5246
5247     /**
5248      * Filter the records by a specified property.
5249      * @param {String} field A field on your records
5250      * @param {String/RegExp} value Either a string that the field
5251      * should start with or a RegExp to test against the field
5252      * @param {Boolean} anyMatch True to match any part not just the beginning
5253      */
5254     filter : function(property, value, anyMatch){
5255         var fn = this.createFilterFn(property, value, anyMatch);
5256         return fn ? this.filterBy(fn) : this.clearFilter();
5257     },
5258
5259     /**
5260      * Filter by a function. The specified function will be called with each
5261      * record in this data source. If the function returns true the record is included,
5262      * otherwise it is filtered.
5263      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5264      * @param {Object} scope (optional) The scope of the function (defaults to this)
5265      */
5266     filterBy : function(fn, scope){
5267         this.snapshot = this.snapshot || this.data;
5268         this.data = this.queryBy(fn, scope||this);
5269         this.fireEvent("datachanged", this);
5270     },
5271
5272     /**
5273      * Query the records by a specified property.
5274      * @param {String} field A field on your records
5275      * @param {String/RegExp} value Either a string that the field
5276      * should start with or a RegExp to test against the field
5277      * @param {Boolean} anyMatch True to match any part not just the beginning
5278      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5279      */
5280     query : function(property, value, anyMatch){
5281         var fn = this.createFilterFn(property, value, anyMatch);
5282         return fn ? this.queryBy(fn) : this.data.clone();
5283     },
5284
5285     /**
5286      * Query by a function. The specified function will be called with each
5287      * record in this data source. If the function returns true the record is included
5288      * in the results.
5289      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5290      * @param {Object} scope (optional) The scope of the function (defaults to this)
5291       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5292      **/
5293     queryBy : function(fn, scope){
5294         var data = this.snapshot || this.data;
5295         return data.filterBy(fn, scope||this);
5296     },
5297
5298     /**
5299      * Collects unique values for a particular dataIndex from this store.
5300      * @param {String} dataIndex The property to collect
5301      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5302      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5303      * @return {Array} An array of the unique values
5304      **/
5305     collect : function(dataIndex, allowNull, bypassFilter){
5306         var d = (bypassFilter === true && this.snapshot) ?
5307                 this.snapshot.items : this.data.items;
5308         var v, sv, r = [], l = {};
5309         for(var i = 0, len = d.length; i < len; i++){
5310             v = d[i].data[dataIndex];
5311             sv = String(v);
5312             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5313                 l[sv] = true;
5314                 r[r.length] = v;
5315             }
5316         }
5317         return r;
5318     },
5319
5320     /**
5321      * Revert to a view of the Record cache with no filtering applied.
5322      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5323      */
5324     clearFilter : function(suppressEvent){
5325         if(this.snapshot && this.snapshot != this.data){
5326             this.data = this.snapshot;
5327             delete this.snapshot;
5328             if(suppressEvent !== true){
5329                 this.fireEvent("datachanged", this);
5330             }
5331         }
5332     },
5333
5334     // private
5335     afterEdit : function(record){
5336         if(this.modified.indexOf(record) == -1){
5337             this.modified.push(record);
5338         }
5339         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5340     },
5341     
5342     // private
5343     afterReject : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5346     },
5347
5348     // private
5349     afterCommit : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5352     },
5353
5354     /**
5355      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5356      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5357      */
5358     commitChanges : function(){
5359         var m = this.modified.slice(0);
5360         this.modified = [];
5361         for(var i = 0, len = m.length; i < len; i++){
5362             m[i].commit();
5363         }
5364     },
5365
5366     /**
5367      * Cancel outstanding changes on all changed records.
5368      */
5369     rejectChanges : function(){
5370         var m = this.modified.slice(0);
5371         this.modified = [];
5372         for(var i = 0, len = m.length; i < len; i++){
5373             m[i].reject();
5374         }
5375     },
5376
5377     onMetaChange : function(meta, rtype, o){
5378         this.recordType = rtype;
5379         this.fields = rtype.prototype.fields;
5380         delete this.snapshot;
5381         this.sortInfo = meta.sortInfo || this.sortInfo;
5382         this.modified = [];
5383         this.fireEvent('metachange', this, this.reader.meta);
5384     }
5385 });/*
5386  * Based on:
5387  * Ext JS Library 1.1.1
5388  * Copyright(c) 2006-2007, Ext JS, LLC.
5389  *
5390  * Originally Released Under LGPL - original licence link has changed is not relivant.
5391  *
5392  * Fork - LGPL
5393  * <script type="text/javascript">
5394  */
5395
5396 /**
5397  * @class Roo.data.SimpleStore
5398  * @extends Roo.data.Store
5399  * Small helper class to make creating Stores from Array data easier.
5400  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5401  * @cfg {Array} fields An array of field definition objects, or field name strings.
5402  * @cfg {Array} data The multi-dimensional array of data
5403  * @constructor
5404  * @param {Object} config
5405  */
5406 Roo.data.SimpleStore = function(config){
5407     Roo.data.SimpleStore.superclass.constructor.call(this, {
5408         isLocal : true,
5409         reader: new Roo.data.ArrayReader({
5410                 id: config.id
5411             },
5412             Roo.data.Record.create(config.fields)
5413         ),
5414         proxy : new Roo.data.MemoryProxy(config.data)
5415     });
5416     this.load();
5417 };
5418 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429 /**
5430 /**
5431  * @extends Roo.data.Store
5432  * @class Roo.data.JsonStore
5433  * Small helper class to make creating Stores for JSON data easier. <br/>
5434 <pre><code>
5435 var store = new Roo.data.JsonStore({
5436     url: 'get-images.php',
5437     root: 'images',
5438     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5439 });
5440 </code></pre>
5441  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5442  * JsonReader and HttpProxy (unless inline data is provided).</b>
5443  * @cfg {Array} fields An array of field definition objects, or field name strings.
5444  * @constructor
5445  * @param {Object} config
5446  */
5447 Roo.data.JsonStore = function(c){
5448     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5449         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5450         reader: new Roo.data.JsonReader(c, c.fields)
5451     }));
5452 };
5453 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5454  * Based on:
5455  * Ext JS Library 1.1.1
5456  * Copyright(c) 2006-2007, Ext JS, LLC.
5457  *
5458  * Originally Released Under LGPL - original licence link has changed is not relivant.
5459  *
5460  * Fork - LGPL
5461  * <script type="text/javascript">
5462  */
5463
5464  
5465 Roo.data.Field = function(config){
5466     if(typeof config == "string"){
5467         config = {name: config};
5468     }
5469     Roo.apply(this, config);
5470     
5471     if(!this.type){
5472         this.type = "auto";
5473     }
5474     
5475     var st = Roo.data.SortTypes;
5476     // named sortTypes are supported, here we look them up
5477     if(typeof this.sortType == "string"){
5478         this.sortType = st[this.sortType];
5479     }
5480     
5481     // set default sortType for strings and dates
5482     if(!this.sortType){
5483         switch(this.type){
5484             case "string":
5485                 this.sortType = st.asUCString;
5486                 break;
5487             case "date":
5488                 this.sortType = st.asDate;
5489                 break;
5490             default:
5491                 this.sortType = st.none;
5492         }
5493     }
5494
5495     // define once
5496     var stripRe = /[\$,%]/g;
5497
5498     // prebuilt conversion function for this field, instead of
5499     // switching every time we're reading a value
5500     if(!this.convert){
5501         var cv, dateFormat = this.dateFormat;
5502         switch(this.type){
5503             case "":
5504             case "auto":
5505             case undefined:
5506                 cv = function(v){ return v; };
5507                 break;
5508             case "string":
5509                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5510                 break;
5511             case "int":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5515                     };
5516                 break;
5517             case "float":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5521                     };
5522                 break;
5523             case "bool":
5524             case "boolean":
5525                 cv = function(v){ return v === true || v === "true" || v == 1; };
5526                 break;
5527             case "date":
5528                 cv = function(v){
5529                     if(!v){
5530                         return '';
5531                     }
5532                     if(v instanceof Date){
5533                         return v;
5534                     }
5535                     if(dateFormat){
5536                         if(dateFormat == "timestamp"){
5537                             return new Date(v*1000);
5538                         }
5539                         return Date.parseDate(v, dateFormat);
5540                     }
5541                     var parsed = Date.parse(v);
5542                     return parsed ? new Date(parsed) : null;
5543                 };
5544              break;
5545             
5546         }
5547         this.convert = cv;
5548     }
5549 };
5550
5551 Roo.data.Field.prototype = {
5552     dateFormat: null,
5553     defaultValue: "",
5554     mapping: null,
5555     sortType : null,
5556     sortDir : "ASC"
5557 };/*
5558  * Based on:
5559  * Ext JS Library 1.1.1
5560  * Copyright(c) 2006-2007, Ext JS, LLC.
5561  *
5562  * Originally Released Under LGPL - original licence link has changed is not relivant.
5563  *
5564  * Fork - LGPL
5565  * <script type="text/javascript">
5566  */
5567  
5568 // Base class for reading structured data from a data source.  This class is intended to be
5569 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5570
5571 /**
5572  * @class Roo.data.DataReader
5573  * Base class for reading structured data from a data source.  This class is intended to be
5574  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5575  */
5576
5577 Roo.data.DataReader = function(meta, recordType){
5578     
5579     this.meta = meta;
5580     
5581     this.recordType = recordType instanceof Array ? 
5582         Roo.data.Record.create(recordType) : recordType;
5583 };
5584
5585 Roo.data.DataReader.prototype = {
5586      /**
5587      * Create an empty record
5588      * @param {Object} data (optional) - overlay some values
5589      * @return {Roo.data.Record} record created.
5590      */
5591     newRow :  function(d) {
5592         var da =  {};
5593         this.recordType.prototype.fields.each(function(c) {
5594             switch( c.type) {
5595                 case 'int' : da[c.name] = 0; break;
5596                 case 'date' : da[c.name] = new Date(); break;
5597                 case 'float' : da[c.name] = 0.0; break;
5598                 case 'boolean' : da[c.name] = false; break;
5599                 default : da[c.name] = ""; break;
5600             }
5601             
5602         });
5603         return new this.recordType(Roo.apply(da, d));
5604     }
5605     
5606 };/*
5607  * Based on:
5608  * Ext JS Library 1.1.1
5609  * Copyright(c) 2006-2007, Ext JS, LLC.
5610  *
5611  * Originally Released Under LGPL - original licence link has changed is not relivant.
5612  *
5613  * Fork - LGPL
5614  * <script type="text/javascript">
5615  */
5616
5617 /**
5618  * @class Roo.data.DataProxy
5619  * @extends Roo.data.Observable
5620  * This class is an abstract base class for implementations which provide retrieval of
5621  * unformatted data objects.<br>
5622  * <p>
5623  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5624  * (of the appropriate type which knows how to parse the data object) to provide a block of
5625  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5626  * <p>
5627  * Custom implementations must implement the load method as described in
5628  * {@link Roo.data.HttpProxy#load}.
5629  */
5630 Roo.data.DataProxy = function(){
5631     this.addEvents({
5632         /**
5633          * @event beforeload
5634          * Fires before a network request is made to retrieve a data object.
5635          * @param {Object} This DataProxy object.
5636          * @param {Object} params The params parameter to the load function.
5637          */
5638         beforeload : true,
5639         /**
5640          * @event load
5641          * Fires before the load method's callback is called.
5642          * @param {Object} This DataProxy object.
5643          * @param {Object} o The data object.
5644          * @param {Object} arg The callback argument object passed to the load function.
5645          */
5646         load : true,
5647         /**
5648          * @event loadexception
5649          * Fires if an Exception occurs during data retrieval.
5650          * @param {Object} This DataProxy object.
5651          * @param {Object} o The data object.
5652          * @param {Object} arg The callback argument object passed to the load function.
5653          * @param {Object} e The Exception.
5654          */
5655         loadexception : true
5656     });
5657     Roo.data.DataProxy.superclass.constructor.call(this);
5658 };
5659
5660 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5661
5662     /**
5663      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5664      */
5665 /*
5666  * Based on:
5667  * Ext JS Library 1.1.1
5668  * Copyright(c) 2006-2007, Ext JS, LLC.
5669  *
5670  * Originally Released Under LGPL - original licence link has changed is not relivant.
5671  *
5672  * Fork - LGPL
5673  * <script type="text/javascript">
5674  */
5675 /**
5676  * @class Roo.data.MemoryProxy
5677  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5678  * to the Reader when its load method is called.
5679  * @constructor
5680  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5681  */
5682 Roo.data.MemoryProxy = function(data){
5683     if (data.data) {
5684         data = data.data;
5685     }
5686     Roo.data.MemoryProxy.superclass.constructor.call(this);
5687     this.data = data;
5688 };
5689
5690 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5691     /**
5692      * Load data from the requested source (in this case an in-memory
5693      * data object passed to the constructor), read the data object into
5694      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5695      * process that block using the passed callback.
5696      * @param {Object} params This parameter is not used by the MemoryProxy class.
5697      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5698      * object into a block of Roo.data.Records.
5699      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5700      * The function must be passed <ul>
5701      * <li>The Record block object</li>
5702      * <li>The "arg" argument from the load function</li>
5703      * <li>A boolean success indicator</li>
5704      * </ul>
5705      * @param {Object} scope The scope in which to call the callback
5706      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5707      */
5708     load : function(params, reader, callback, scope, arg){
5709         params = params || {};
5710         var result;
5711         try {
5712             result = reader.readRecords(this.data);
5713         }catch(e){
5714             this.fireEvent("loadexception", this, arg, null, e);
5715             callback.call(scope, null, arg, false);
5716             return;
5717         }
5718         callback.call(scope, result, arg, true);
5719     },
5720     
5721     // private
5722     update : function(params, records){
5723         
5724     }
5725 });/*
5726  * Based on:
5727  * Ext JS Library 1.1.1
5728  * Copyright(c) 2006-2007, Ext JS, LLC.
5729  *
5730  * Originally Released Under LGPL - original licence link has changed is not relivant.
5731  *
5732  * Fork - LGPL
5733  * <script type="text/javascript">
5734  */
5735 /**
5736  * @class Roo.data.HttpProxy
5737  * @extends Roo.data.DataProxy
5738  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5739  * configured to reference a certain URL.<br><br>
5740  * <p>
5741  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5742  * from which the running page was served.<br><br>
5743  * <p>
5744  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5745  * <p>
5746  * Be aware that to enable the browser to parse an XML document, the server must set
5747  * the Content-Type header in the HTTP response to "text/xml".
5748  * @constructor
5749  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5750  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5751  * will be used to make the request.
5752  */
5753 Roo.data.HttpProxy = function(conn){
5754     Roo.data.HttpProxy.superclass.constructor.call(this);
5755     // is conn a conn config or a real conn?
5756     this.conn = conn;
5757     this.useAjax = !conn || !conn.events;
5758   
5759 };
5760
5761 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5762     // thse are take from connection...
5763     
5764     /**
5765      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5766      */
5767     /**
5768      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5769      * extra parameters to each request made by this object. (defaults to undefined)
5770      */
5771     /**
5772      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5773      *  to each request made by this object. (defaults to undefined)
5774      */
5775     /**
5776      * @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)
5777      */
5778     /**
5779      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5780      */
5781      /**
5782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5783      * @type Boolean
5784      */
5785   
5786
5787     /**
5788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5789      * @type Boolean
5790      */
5791     /**
5792      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5793      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5794      * a finer-grained basis than the DataProxy events.
5795      */
5796     getConnection : function(){
5797         return this.useAjax ? Roo.Ajax : this.conn;
5798     },
5799
5800     /**
5801      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5802      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5803      * process that block using the passed callback.
5804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5805      * for the request to the remote server.
5806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5807      * object into a block of Roo.data.Records.
5808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5809      * The function must be passed <ul>
5810      * <li>The Record block object</li>
5811      * <li>The "arg" argument from the load function</li>
5812      * <li>A boolean success indicator</li>
5813      * </ul>
5814      * @param {Object} scope The scope in which to call the callback
5815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5816      */
5817     load : function(params, reader, callback, scope, arg){
5818         if(this.fireEvent("beforeload", this, params) !== false){
5819             var  o = {
5820                 params : params || {},
5821                 request: {
5822                     callback : callback,
5823                     scope : scope,
5824                     arg : arg
5825                 },
5826                 reader: reader,
5827                 callback : this.loadResponse,
5828                 scope: this
5829             };
5830             if(this.useAjax){
5831                 Roo.applyIf(o, this.conn);
5832                 if(this.activeRequest){
5833                     Roo.Ajax.abort(this.activeRequest);
5834                 }
5835                 this.activeRequest = Roo.Ajax.request(o);
5836             }else{
5837                 this.conn.request(o);
5838             }
5839         }else{
5840             callback.call(scope||this, null, arg, false);
5841         }
5842     },
5843
5844     // private
5845     loadResponse : function(o, success, response){
5846         delete this.activeRequest;
5847         if(!success){
5848             this.fireEvent("loadexception", this, o, response);
5849             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5850             return;
5851         }
5852         var result;
5853         try {
5854             result = o.reader.read(response);
5855         }catch(e){
5856             this.fireEvent("loadexception", this, o, response, e);
5857             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5858             return;
5859         }
5860         
5861         this.fireEvent("load", this, o, o.request.arg);
5862         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5863     },
5864
5865     // private
5866     update : function(dataSet){
5867
5868     },
5869
5870     // private
5871     updateResponse : function(dataSet){
5872
5873     }
5874 });/*
5875  * Based on:
5876  * Ext JS Library 1.1.1
5877  * Copyright(c) 2006-2007, Ext JS, LLC.
5878  *
5879  * Originally Released Under LGPL - original licence link has changed is not relivant.
5880  *
5881  * Fork - LGPL
5882  * <script type="text/javascript">
5883  */
5884
5885 /**
5886  * @class Roo.data.ScriptTagProxy
5887  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5888  * other than the originating domain of the running page.<br><br>
5889  * <p>
5890  * <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
5891  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5892  * <p>
5893  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5894  * source code that is used as the source inside a &lt;script> tag.<br><br>
5895  * <p>
5896  * In order for the browser to process the returned data, the server must wrap the data object
5897  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5898  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5899  * depending on whether the callback name was passed:
5900  * <p>
5901  * <pre><code>
5902 boolean scriptTag = false;
5903 String cb = request.getParameter("callback");
5904 if (cb != null) {
5905     scriptTag = true;
5906     response.setContentType("text/javascript");
5907 } else {
5908     response.setContentType("application/x-json");
5909 }
5910 Writer out = response.getWriter();
5911 if (scriptTag) {
5912     out.write(cb + "(");
5913 }
5914 out.print(dataBlock.toJsonString());
5915 if (scriptTag) {
5916     out.write(");");
5917 }
5918 </pre></code>
5919  *
5920  * @constructor
5921  * @param {Object} config A configuration object.
5922  */
5923 Roo.data.ScriptTagProxy = function(config){
5924     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5925     Roo.apply(this, config);
5926     this.head = document.getElementsByTagName("head")[0];
5927 };
5928
5929 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5930
5931 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5932     /**
5933      * @cfg {String} url The URL from which to request the data object.
5934      */
5935     /**
5936      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5937      */
5938     timeout : 30000,
5939     /**
5940      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5941      * the server the name of the callback function set up by the load call to process the returned data object.
5942      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5943      * javascript output which calls this named function passing the data object as its only parameter.
5944      */
5945     callbackParam : "callback",
5946     /**
5947      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5948      * name to the request.
5949      */
5950     nocache : true,
5951
5952     /**
5953      * Load data from the configured URL, read the data object into
5954      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5955      * process that block using the passed callback.
5956      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5957      * for the request to the remote server.
5958      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5959      * object into a block of Roo.data.Records.
5960      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5961      * The function must be passed <ul>
5962      * <li>The Record block object</li>
5963      * <li>The "arg" argument from the load function</li>
5964      * <li>A boolean success indicator</li>
5965      * </ul>
5966      * @param {Object} scope The scope in which to call the callback
5967      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5968      */
5969     load : function(params, reader, callback, scope, arg){
5970         if(this.fireEvent("beforeload", this, params) !== false){
5971
5972             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5973
5974             var url = this.url;
5975             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5976             if(this.nocache){
5977                 url += "&_dc=" + (new Date().getTime());
5978             }
5979             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5980             var trans = {
5981                 id : transId,
5982                 cb : "stcCallback"+transId,
5983                 scriptId : "stcScript"+transId,
5984                 params : params,
5985                 arg : arg,
5986                 url : url,
5987                 callback : callback,
5988                 scope : scope,
5989                 reader : reader
5990             };
5991             var conn = this;
5992
5993             window[trans.cb] = function(o){
5994                 conn.handleResponse(o, trans);
5995             };
5996
5997             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5998
5999             if(this.autoAbort !== false){
6000                 this.abort();
6001             }
6002
6003             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6004
6005             var script = document.createElement("script");
6006             script.setAttribute("src", url);
6007             script.setAttribute("type", "text/javascript");
6008             script.setAttribute("id", trans.scriptId);
6009             this.head.appendChild(script);
6010
6011             this.trans = trans;
6012         }else{
6013             callback.call(scope||this, null, arg, false);
6014         }
6015     },
6016
6017     // private
6018     isLoading : function(){
6019         return this.trans ? true : false;
6020     },
6021
6022     /**
6023      * Abort the current server request.
6024      */
6025     abort : function(){
6026         if(this.isLoading()){
6027             this.destroyTrans(this.trans);
6028         }
6029     },
6030
6031     // private
6032     destroyTrans : function(trans, isLoaded){
6033         this.head.removeChild(document.getElementById(trans.scriptId));
6034         clearTimeout(trans.timeoutId);
6035         if(isLoaded){
6036             window[trans.cb] = undefined;
6037             try{
6038                 delete window[trans.cb];
6039             }catch(e){}
6040         }else{
6041             // if hasn't been loaded, wait for load to remove it to prevent script error
6042             window[trans.cb] = function(){
6043                 window[trans.cb] = undefined;
6044                 try{
6045                     delete window[trans.cb];
6046                 }catch(e){}
6047             };
6048         }
6049     },
6050
6051     // private
6052     handleResponse : function(o, trans){
6053         this.trans = false;
6054         this.destroyTrans(trans, true);
6055         var result;
6056         try {
6057             result = trans.reader.readRecords(o);
6058         }catch(e){
6059             this.fireEvent("loadexception", this, o, trans.arg, e);
6060             trans.callback.call(trans.scope||window, null, trans.arg, false);
6061             return;
6062         }
6063         this.fireEvent("load", this, o, trans.arg);
6064         trans.callback.call(trans.scope||window, result, trans.arg, true);
6065     },
6066
6067     // private
6068     handleFailure : function(trans){
6069         this.trans = false;
6070         this.destroyTrans(trans, false);
6071         this.fireEvent("loadexception", this, null, trans.arg);
6072         trans.callback.call(trans.scope||window, null, trans.arg, false);
6073     }
6074 });/*
6075  * Based on:
6076  * Ext JS Library 1.1.1
6077  * Copyright(c) 2006-2007, Ext JS, LLC.
6078  *
6079  * Originally Released Under LGPL - original licence link has changed is not relivant.
6080  *
6081  * Fork - LGPL
6082  * <script type="text/javascript">
6083  */
6084
6085 /**
6086  * @class Roo.data.JsonReader
6087  * @extends Roo.data.DataReader
6088  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6089  * based on mappings in a provided Roo.data.Record constructor.
6090  * 
6091  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6092  * in the reply previously. 
6093  * 
6094  * <p>
6095  * Example code:
6096  * <pre><code>
6097 var RecordDef = Roo.data.Record.create([
6098     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6099     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6100 ]);
6101 var myReader = new Roo.data.JsonReader({
6102     totalProperty: "results",    // The property which contains the total dataset size (optional)
6103     root: "rows",                // The property which contains an Array of row objects
6104     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6105 }, RecordDef);
6106 </code></pre>
6107  * <p>
6108  * This would consume a JSON file like this:
6109  * <pre><code>
6110 { 'results': 2, 'rows': [
6111     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6112     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6113 }
6114 </code></pre>
6115  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6116  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6117  * paged from the remote server.
6118  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6119  * @cfg {String} root name of the property which contains the Array of row objects.
6120  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6121  * @constructor
6122  * Create a new JsonReader
6123  * @param {Object} meta Metadata configuration options
6124  * @param {Object} recordType Either an Array of field definition objects,
6125  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6126  */
6127 Roo.data.JsonReader = function(meta, recordType){
6128     
6129     meta = meta || {};
6130     // set some defaults:
6131     Roo.applyIf(meta, {
6132         totalProperty: 'total',
6133         successProperty : 'success',
6134         root : 'data',
6135         id : 'id'
6136     });
6137     
6138     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6139 };
6140 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6141     
6142     /**
6143      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6144      * Used by Store query builder to append _requestMeta to params.
6145      * 
6146      */
6147     metaFromRemote : false,
6148     /**
6149      * This method is only used by a DataProxy which has retrieved data from a remote server.
6150      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6151      * @return {Object} data A data block which is used by an Roo.data.Store object as
6152      * a cache of Roo.data.Records.
6153      */
6154     read : function(response){
6155         var json = response.responseText;
6156        
6157         var o = /* eval:var:o */ eval("("+json+")");
6158         if(!o) {
6159             throw {message: "JsonReader.read: Json object not found"};
6160         }
6161         
6162         if(o.metaData){
6163             
6164             delete this.ef;
6165             this.metaFromRemote = true;
6166             this.meta = o.metaData;
6167             this.recordType = Roo.data.Record.create(o.metaData.fields);
6168             this.onMetaChange(this.meta, this.recordType, o);
6169         }
6170         return this.readRecords(o);
6171     },
6172
6173     // private function a store will implement
6174     onMetaChange : function(meta, recordType, o){
6175
6176     },
6177
6178     /**
6179          * @ignore
6180          */
6181     simpleAccess: function(obj, subsc) {
6182         return obj[subsc];
6183     },
6184
6185         /**
6186          * @ignore
6187          */
6188     getJsonAccessor: function(){
6189         var re = /[\[\.]/;
6190         return function(expr) {
6191             try {
6192                 return(re.test(expr))
6193                     ? new Function("obj", "return obj." + expr)
6194                     : function(obj){
6195                         return obj[expr];
6196                     };
6197             } catch(e){}
6198             return Roo.emptyFn;
6199         };
6200     }(),
6201
6202     /**
6203      * Create a data block containing Roo.data.Records from an XML document.
6204      * @param {Object} o An object which contains an Array of row objects in the property specified
6205      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6206      * which contains the total size of the dataset.
6207      * @return {Object} data A data block which is used by an Roo.data.Store object as
6208      * a cache of Roo.data.Records.
6209      */
6210     readRecords : function(o){
6211         /**
6212          * After any data loads, the raw JSON data is available for further custom processing.
6213          * @type Object
6214          */
6215         this.jsonData = o;
6216         var s = this.meta, Record = this.recordType,
6217             f = Record.prototype.fields, fi = f.items, fl = f.length;
6218
6219 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6220         if (!this.ef) {
6221             if(s.totalProperty) {
6222                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6223                 }
6224                 if(s.successProperty) {
6225                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6226                 }
6227                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6228                 if (s.id) {
6229                         var g = this.getJsonAccessor(s.id);
6230                         this.getId = function(rec) {
6231                                 var r = g(rec);
6232                                 return (r === undefined || r === "") ? null : r;
6233                         };
6234                 } else {
6235                         this.getId = function(){return null;};
6236                 }
6237             this.ef = [];
6238             for(var jj = 0; jj < fl; jj++){
6239                 f = fi[jj];
6240                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6241                 this.ef[jj] = this.getJsonAccessor(map);
6242             }
6243         }
6244
6245         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6246         if(s.totalProperty){
6247             var vt = parseInt(this.getTotal(o), 10);
6248             if(!isNaN(vt)){
6249                 totalRecords = vt;
6250             }
6251         }
6252         if(s.successProperty){
6253             var vs = this.getSuccess(o);
6254             if(vs === false || vs === 'false'){
6255                 success = false;
6256             }
6257         }
6258         var records = [];
6259             for(var i = 0; i < c; i++){
6260                     var n = root[i];
6261                 var values = {};
6262                 var id = this.getId(n);
6263                 for(var j = 0; j < fl; j++){
6264                     f = fi[j];
6265                 var v = this.ef[j](n);
6266                 if (!f.convert) {
6267                     Roo.log('missing convert for ' + f.name);
6268                     Roo.log(f);
6269                     continue;
6270                 }
6271                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6272                 }
6273                 var record = new Record(values, id);
6274                 record.json = n;
6275                 records[i] = record;
6276             }
6277             return {
6278                 success : success,
6279                 records : records,
6280                 totalRecords : totalRecords
6281             };
6282     }
6283 });/*
6284  * Based on:
6285  * Ext JS Library 1.1.1
6286  * Copyright(c) 2006-2007, Ext JS, LLC.
6287  *
6288  * Originally Released Under LGPL - original licence link has changed is not relivant.
6289  *
6290  * Fork - LGPL
6291  * <script type="text/javascript">
6292  */
6293
6294 /**
6295  * @class Roo.data.XmlReader
6296  * @extends Roo.data.DataReader
6297  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6298  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6299  * <p>
6300  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6301  * header in the HTTP response must be set to "text/xml".</em>
6302  * <p>
6303  * Example code:
6304  * <pre><code>
6305 var RecordDef = Roo.data.Record.create([
6306    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6307    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6308 ]);
6309 var myReader = new Roo.data.XmlReader({
6310    totalRecords: "results", // The element which contains the total dataset size (optional)
6311    record: "row",           // The repeated element which contains row information
6312    id: "id"                 // The element within the row that provides an ID for the record (optional)
6313 }, RecordDef);
6314 </code></pre>
6315  * <p>
6316  * This would consume an XML file like this:
6317  * <pre><code>
6318 &lt;?xml?>
6319 &lt;dataset>
6320  &lt;results>2&lt;/results>
6321  &lt;row>
6322    &lt;id>1&lt;/id>
6323    &lt;name>Bill&lt;/name>
6324    &lt;occupation>Gardener&lt;/occupation>
6325  &lt;/row>
6326  &lt;row>
6327    &lt;id>2&lt;/id>
6328    &lt;name>Ben&lt;/name>
6329    &lt;occupation>Horticulturalist&lt;/occupation>
6330  &lt;/row>
6331 &lt;/dataset>
6332 </code></pre>
6333  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6334  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6335  * paged from the remote server.
6336  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6337  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6338  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6339  * a record identifier value.
6340  * @constructor
6341  * Create a new XmlReader
6342  * @param {Object} meta Metadata configuration options
6343  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6344  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6345  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6346  */
6347 Roo.data.XmlReader = function(meta, recordType){
6348     meta = meta || {};
6349     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6350 };
6351 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6352     /**
6353      * This method is only used by a DataProxy which has retrieved data from a remote server.
6354          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6355          * to contain a method called 'responseXML' that returns an XML document object.
6356      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6357      * a cache of Roo.data.Records.
6358      */
6359     read : function(response){
6360         var doc = response.responseXML;
6361         if(!doc) {
6362             throw {message: "XmlReader.read: XML Document not available"};
6363         }
6364         return this.readRecords(doc);
6365     },
6366
6367     /**
6368      * Create a data block containing Roo.data.Records from an XML document.
6369          * @param {Object} doc A parsed XML document.
6370      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6371      * a cache of Roo.data.Records.
6372      */
6373     readRecords : function(doc){
6374         /**
6375          * After any data loads/reads, the raw XML Document is available for further custom processing.
6376          * @type XMLDocument
6377          */
6378         this.xmlData = doc;
6379         var root = doc.documentElement || doc;
6380         var q = Roo.DomQuery;
6381         var recordType = this.recordType, fields = recordType.prototype.fields;
6382         var sid = this.meta.id;
6383         var totalRecords = 0, success = true;
6384         if(this.meta.totalRecords){
6385             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6386         }
6387         
6388         if(this.meta.success){
6389             var sv = q.selectValue(this.meta.success, root, true);
6390             success = sv !== false && sv !== 'false';
6391         }
6392         var records = [];
6393         var ns = q.select(this.meta.record, root);
6394         for(var i = 0, len = ns.length; i < len; i++) {
6395                 var n = ns[i];
6396                 var values = {};
6397                 var id = sid ? q.selectValue(sid, n) : undefined;
6398                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6399                     var f = fields.items[j];
6400                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6401                     v = f.convert(v);
6402                     values[f.name] = v;
6403                 }
6404                 var record = new recordType(values, id);
6405                 record.node = n;
6406                 records[records.length] = record;
6407             }
6408
6409             return {
6410                 success : success,
6411                 records : records,
6412                 totalRecords : totalRecords || records.length
6413             };
6414     }
6415 });/*
6416  * Based on:
6417  * Ext JS Library 1.1.1
6418  * Copyright(c) 2006-2007, Ext JS, LLC.
6419  *
6420  * Originally Released Under LGPL - original licence link has changed is not relivant.
6421  *
6422  * Fork - LGPL
6423  * <script type="text/javascript">
6424  */
6425
6426 /**
6427  * @class Roo.data.ArrayReader
6428  * @extends Roo.data.DataReader
6429  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6430  * Each element of that Array represents a row of data fields. The
6431  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6432  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6433  * <p>
6434  * Example code:.
6435  * <pre><code>
6436 var RecordDef = Roo.data.Record.create([
6437     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6438     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6439 ]);
6440 var myReader = new Roo.data.ArrayReader({
6441     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6442 }, RecordDef);
6443 </code></pre>
6444  * <p>
6445  * This would consume an Array like this:
6446  * <pre><code>
6447 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6448   </code></pre>
6449  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6450  * @constructor
6451  * Create a new JsonReader
6452  * @param {Object} meta Metadata configuration options.
6453  * @param {Object} recordType Either an Array of field definition objects
6454  * as specified to {@link Roo.data.Record#create},
6455  * or an {@link Roo.data.Record} object
6456  * created using {@link Roo.data.Record#create}.
6457  */
6458 Roo.data.ArrayReader = function(meta, recordType){
6459     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6460 };
6461
6462 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6463     /**
6464      * Create a data block containing Roo.data.Records from an XML document.
6465      * @param {Object} o An Array of row objects which represents the dataset.
6466      * @return {Object} data A data block which is used by an Roo.data.Store object as
6467      * a cache of Roo.data.Records.
6468      */
6469     readRecords : function(o){
6470         var sid = this.meta ? this.meta.id : null;
6471         var recordType = this.recordType, fields = recordType.prototype.fields;
6472         var records = [];
6473         var root = o;
6474             for(var i = 0; i < root.length; i++){
6475                     var n = root[i];
6476                 var values = {};
6477                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6478                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6479                 var f = fields.items[j];
6480                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6481                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6482                 v = f.convert(v);
6483                 values[f.name] = v;
6484             }
6485                 var record = new recordType(values, id);
6486                 record.json = n;
6487                 records[records.length] = record;
6488             }
6489             return {
6490                 records : records,
6491                 totalRecords : records.length
6492             };
6493     }
6494 });/*
6495  * Based on:
6496  * Ext JS Library 1.1.1
6497  * Copyright(c) 2006-2007, Ext JS, LLC.
6498  *
6499  * Originally Released Under LGPL - original licence link has changed is not relivant.
6500  *
6501  * Fork - LGPL
6502  * <script type="text/javascript">
6503  */
6504
6505
6506 /**
6507  * @class Roo.data.Tree
6508  * @extends Roo.util.Observable
6509  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6510  * in the tree have most standard DOM functionality.
6511  * @constructor
6512  * @param {Node} root (optional) The root node
6513  */
6514 Roo.data.Tree = function(root){
6515    this.nodeHash = {};
6516    /**
6517     * The root node for this tree
6518     * @type Node
6519     */
6520    this.root = null;
6521    if(root){
6522        this.setRootNode(root);
6523    }
6524    this.addEvents({
6525        /**
6526         * @event append
6527         * Fires when a new child node is appended to a node in this tree.
6528         * @param {Tree} tree The owner tree
6529         * @param {Node} parent The parent node
6530         * @param {Node} node The newly appended node
6531         * @param {Number} index The index of the newly appended node
6532         */
6533        "append" : true,
6534        /**
6535         * @event remove
6536         * Fires when a child node is removed from a node in this tree.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node removed
6540         */
6541        "remove" : true,
6542        /**
6543         * @event move
6544         * Fires when a node is moved to a new location in the tree
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node moved
6547         * @param {Node} oldParent The old parent of this node
6548         * @param {Node} newParent The new parent of this node
6549         * @param {Number} index The index it was moved to
6550         */
6551        "move" : true,
6552        /**
6553         * @event insert
6554         * Fires when a new child node is inserted in a node in this tree.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node inserted
6558         * @param {Node} refNode The child node the node was inserted before
6559         */
6560        "insert" : true,
6561        /**
6562         * @event beforeappend
6563         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6564         * @param {Tree} tree The owner tree
6565         * @param {Node} parent The parent node
6566         * @param {Node} node The child node to be appended
6567         */
6568        "beforeappend" : true,
6569        /**
6570         * @event beforeremove
6571         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6572         * @param {Tree} tree The owner tree
6573         * @param {Node} parent The parent node
6574         * @param {Node} node The child node to be removed
6575         */
6576        "beforeremove" : true,
6577        /**
6578         * @event beforemove
6579         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} node The node being moved
6582         * @param {Node} oldParent The parent of the node
6583         * @param {Node} newParent The new parent the node is moving to
6584         * @param {Number} index The index it is being moved to
6585         */
6586        "beforemove" : true,
6587        /**
6588         * @event beforeinsert
6589         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6590         * @param {Tree} tree The owner tree
6591         * @param {Node} parent The parent node
6592         * @param {Node} node The child node to be inserted
6593         * @param {Node} refNode The child node the node is being inserted before
6594         */
6595        "beforeinsert" : true
6596    });
6597
6598     Roo.data.Tree.superclass.constructor.call(this);
6599 };
6600
6601 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6602     pathSeparator: "/",
6603
6604     proxyNodeEvent : function(){
6605         return this.fireEvent.apply(this, arguments);
6606     },
6607
6608     /**
6609      * Returns the root node for this tree.
6610      * @return {Node}
6611      */
6612     getRootNode : function(){
6613         return this.root;
6614     },
6615
6616     /**
6617      * Sets the root node for this tree.
6618      * @param {Node} node
6619      * @return {Node}
6620      */
6621     setRootNode : function(node){
6622         this.root = node;
6623         node.ownerTree = this;
6624         node.isRoot = true;
6625         this.registerNode(node);
6626         return node;
6627     },
6628
6629     /**
6630      * Gets a node in this tree by its id.
6631      * @param {String} id
6632      * @return {Node}
6633      */
6634     getNodeById : function(id){
6635         return this.nodeHash[id];
6636     },
6637
6638     registerNode : function(node){
6639         this.nodeHash[node.id] = node;
6640     },
6641
6642     unregisterNode : function(node){
6643         delete this.nodeHash[node.id];
6644     },
6645
6646     toString : function(){
6647         return "[Tree"+(this.id?" "+this.id:"")+"]";
6648     }
6649 });
6650
6651 /**
6652  * @class Roo.data.Node
6653  * @extends Roo.util.Observable
6654  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6655  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6656  * @constructor
6657  * @param {Object} attributes The attributes/config for the node
6658  */
6659 Roo.data.Node = function(attributes){
6660     /**
6661      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6662      * @type {Object}
6663      */
6664     this.attributes = attributes || {};
6665     this.leaf = this.attributes.leaf;
6666     /**
6667      * The node id. @type String
6668      */
6669     this.id = this.attributes.id;
6670     if(!this.id){
6671         this.id = Roo.id(null, "ynode-");
6672         this.attributes.id = this.id;
6673     }
6674      
6675     
6676     /**
6677      * All child nodes of this node. @type Array
6678      */
6679     this.childNodes = [];
6680     if(!this.childNodes.indexOf){ // indexOf is a must
6681         this.childNodes.indexOf = function(o){
6682             for(var i = 0, len = this.length; i < len; i++){
6683                 if(this[i] == o) {
6684                     return i;
6685                 }
6686             }
6687             return -1;
6688         };
6689     }
6690     /**
6691      * The parent node for this node. @type Node
6692      */
6693     this.parentNode = null;
6694     /**
6695      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6696      */
6697     this.firstChild = null;
6698     /**
6699      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6700      */
6701     this.lastChild = null;
6702     /**
6703      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6704      */
6705     this.previousSibling = null;
6706     /**
6707      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6708      */
6709     this.nextSibling = null;
6710
6711     this.addEvents({
6712        /**
6713         * @event append
6714         * Fires when a new child node is appended
6715         * @param {Tree} tree The owner tree
6716         * @param {Node} this This node
6717         * @param {Node} node The newly appended node
6718         * @param {Number} index The index of the newly appended node
6719         */
6720        "append" : true,
6721        /**
6722         * @event remove
6723         * Fires when a child node is removed
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} node The removed node
6727         */
6728        "remove" : true,
6729        /**
6730         * @event move
6731         * Fires when this node is moved to a new location in the tree
6732         * @param {Tree} tree The owner tree
6733         * @param {Node} this This node
6734         * @param {Node} oldParent The old parent of this node
6735         * @param {Node} newParent The new parent of this node
6736         * @param {Number} index The index it was moved to
6737         */
6738        "move" : true,
6739        /**
6740         * @event insert
6741         * Fires when a new child node is inserted.
6742         * @param {Tree} tree The owner tree
6743         * @param {Node} this This node
6744         * @param {Node} node The child node inserted
6745         * @param {Node} refNode The child node the node was inserted before
6746         */
6747        "insert" : true,
6748        /**
6749         * @event beforeappend
6750         * Fires before a new child is appended, return false to cancel the append.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be appended
6754         */
6755        "beforeappend" : true,
6756        /**
6757         * @event beforeremove
6758         * Fires before a child is removed, return false to cancel the remove.
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} node The child node to be removed
6762         */
6763        "beforeremove" : true,
6764        /**
6765         * @event beforemove
6766         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6767         * @param {Tree} tree The owner tree
6768         * @param {Node} this This node
6769         * @param {Node} oldParent The parent of this node
6770         * @param {Node} newParent The new parent this node is moving to
6771         * @param {Number} index The index it is being moved to
6772         */
6773        "beforemove" : true,
6774        /**
6775         * @event beforeinsert
6776         * Fires before a new child is inserted, return false to cancel the insert.
6777         * @param {Tree} tree The owner tree
6778         * @param {Node} this This node
6779         * @param {Node} node The child node to be inserted
6780         * @param {Node} refNode The child node the node is being inserted before
6781         */
6782        "beforeinsert" : true
6783    });
6784     this.listeners = this.attributes.listeners;
6785     Roo.data.Node.superclass.constructor.call(this);
6786 };
6787
6788 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6789     fireEvent : function(evtName){
6790         // first do standard event for this node
6791         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6792             return false;
6793         }
6794         // then bubble it up to the tree if the event wasn't cancelled
6795         var ot = this.getOwnerTree();
6796         if(ot){
6797             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6798                 return false;
6799             }
6800         }
6801         return true;
6802     },
6803
6804     /**
6805      * Returns true if this node is a leaf
6806      * @return {Boolean}
6807      */
6808     isLeaf : function(){
6809         return this.leaf === true;
6810     },
6811
6812     // private
6813     setFirstChild : function(node){
6814         this.firstChild = node;
6815     },
6816
6817     //private
6818     setLastChild : function(node){
6819         this.lastChild = node;
6820     },
6821
6822
6823     /**
6824      * Returns true if this node is the last child of its parent
6825      * @return {Boolean}
6826      */
6827     isLast : function(){
6828        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6829     },
6830
6831     /**
6832      * Returns true if this node is the first child of its parent
6833      * @return {Boolean}
6834      */
6835     isFirst : function(){
6836        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6837     },
6838
6839     hasChildNodes : function(){
6840         return !this.isLeaf() && this.childNodes.length > 0;
6841     },
6842
6843     /**
6844      * Insert node(s) as the last child node of this node.
6845      * @param {Node/Array} node The node or Array of nodes to append
6846      * @return {Node} The appended node if single append, or null if an array was passed
6847      */
6848     appendChild : function(node){
6849         var multi = false;
6850         if(node instanceof Array){
6851             multi = node;
6852         }else if(arguments.length > 1){
6853             multi = arguments;
6854         }
6855         // if passed an array or multiple args do them one by one
6856         if(multi){
6857             for(var i = 0, len = multi.length; i < len; i++) {
6858                 this.appendChild(multi[i]);
6859             }
6860         }else{
6861             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6862                 return false;
6863             }
6864             var index = this.childNodes.length;
6865             var oldParent = node.parentNode;
6866             // it's a move, make sure we move it cleanly
6867             if(oldParent){
6868                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6869                     return false;
6870                 }
6871                 oldParent.removeChild(node);
6872             }
6873             index = this.childNodes.length;
6874             if(index == 0){
6875                 this.setFirstChild(node);
6876             }
6877             this.childNodes.push(node);
6878             node.parentNode = this;
6879             var ps = this.childNodes[index-1];
6880             if(ps){
6881                 node.previousSibling = ps;
6882                 ps.nextSibling = node;
6883             }else{
6884                 node.previousSibling = null;
6885             }
6886             node.nextSibling = null;
6887             this.setLastChild(node);
6888             node.setOwnerTree(this.getOwnerTree());
6889             this.fireEvent("append", this.ownerTree, this, node, index);
6890             if(oldParent){
6891                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6892             }
6893             return node;
6894         }
6895     },
6896
6897     /**
6898      * Removes a child node from this node.
6899      * @param {Node} node The node to remove
6900      * @return {Node} The removed node
6901      */
6902     removeChild : function(node){
6903         var index = this.childNodes.indexOf(node);
6904         if(index == -1){
6905             return false;
6906         }
6907         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6908             return false;
6909         }
6910
6911         // remove it from childNodes collection
6912         this.childNodes.splice(index, 1);
6913
6914         // update siblings
6915         if(node.previousSibling){
6916             node.previousSibling.nextSibling = node.nextSibling;
6917         }
6918         if(node.nextSibling){
6919             node.nextSibling.previousSibling = node.previousSibling;
6920         }
6921
6922         // update child refs
6923         if(this.firstChild == node){
6924             this.setFirstChild(node.nextSibling);
6925         }
6926         if(this.lastChild == node){
6927             this.setLastChild(node.previousSibling);
6928         }
6929
6930         node.setOwnerTree(null);
6931         // clear any references from the node
6932         node.parentNode = null;
6933         node.previousSibling = null;
6934         node.nextSibling = null;
6935         this.fireEvent("remove", this.ownerTree, this, node);
6936         return node;
6937     },
6938
6939     /**
6940      * Inserts the first node before the second node in this nodes childNodes collection.
6941      * @param {Node} node The node to insert
6942      * @param {Node} refNode The node to insert before (if null the node is appended)
6943      * @return {Node} The inserted node
6944      */
6945     insertBefore : function(node, refNode){
6946         if(!refNode){ // like standard Dom, refNode can be null for append
6947             return this.appendChild(node);
6948         }
6949         // nothing to do
6950         if(node == refNode){
6951             return false;
6952         }
6953
6954         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6955             return false;
6956         }
6957         var index = this.childNodes.indexOf(refNode);
6958         var oldParent = node.parentNode;
6959         var refIndex = index;
6960
6961         // when moving internally, indexes will change after remove
6962         if(oldParent == this && this.childNodes.indexOf(node) < index){
6963             refIndex--;
6964         }
6965
6966         // it's a move, make sure we move it cleanly
6967         if(oldParent){
6968             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6969                 return false;
6970             }
6971             oldParent.removeChild(node);
6972         }
6973         if(refIndex == 0){
6974             this.setFirstChild(node);
6975         }
6976         this.childNodes.splice(refIndex, 0, node);
6977         node.parentNode = this;
6978         var ps = this.childNodes[refIndex-1];
6979         if(ps){
6980             node.previousSibling = ps;
6981             ps.nextSibling = node;
6982         }else{
6983             node.previousSibling = null;
6984         }
6985         node.nextSibling = refNode;
6986         refNode.previousSibling = node;
6987         node.setOwnerTree(this.getOwnerTree());
6988         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6989         if(oldParent){
6990             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6991         }
6992         return node;
6993     },
6994
6995     /**
6996      * Returns the child node at the specified index.
6997      * @param {Number} index
6998      * @return {Node}
6999      */
7000     item : function(index){
7001         return this.childNodes[index];
7002     },
7003
7004     /**
7005      * Replaces one child node in this node with another.
7006      * @param {Node} newChild The replacement node
7007      * @param {Node} oldChild The node to replace
7008      * @return {Node} The replaced node
7009      */
7010     replaceChild : function(newChild, oldChild){
7011         this.insertBefore(newChild, oldChild);
7012         this.removeChild(oldChild);
7013         return oldChild;
7014     },
7015
7016     /**
7017      * Returns the index of a child node
7018      * @param {Node} node
7019      * @return {Number} The index of the node or -1 if it was not found
7020      */
7021     indexOf : function(child){
7022         return this.childNodes.indexOf(child);
7023     },
7024
7025     /**
7026      * Returns the tree this node is in.
7027      * @return {Tree}
7028      */
7029     getOwnerTree : function(){
7030         // if it doesn't have one, look for one
7031         if(!this.ownerTree){
7032             var p = this;
7033             while(p){
7034                 if(p.ownerTree){
7035                     this.ownerTree = p.ownerTree;
7036                     break;
7037                 }
7038                 p = p.parentNode;
7039             }
7040         }
7041         return this.ownerTree;
7042     },
7043
7044     /**
7045      * Returns depth of this node (the root node has a depth of 0)
7046      * @return {Number}
7047      */
7048     getDepth : function(){
7049         var depth = 0;
7050         var p = this;
7051         while(p.parentNode){
7052             ++depth;
7053             p = p.parentNode;
7054         }
7055         return depth;
7056     },
7057
7058     // private
7059     setOwnerTree : function(tree){
7060         // if it's move, we need to update everyone
7061         if(tree != this.ownerTree){
7062             if(this.ownerTree){
7063                 this.ownerTree.unregisterNode(this);
7064             }
7065             this.ownerTree = tree;
7066             var cs = this.childNodes;
7067             for(var i = 0, len = cs.length; i < len; i++) {
7068                 cs[i].setOwnerTree(tree);
7069             }
7070             if(tree){
7071                 tree.registerNode(this);
7072             }
7073         }
7074     },
7075
7076     /**
7077      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7078      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7079      * @return {String} The path
7080      */
7081     getPath : function(attr){
7082         attr = attr || "id";
7083         var p = this.parentNode;
7084         var b = [this.attributes[attr]];
7085         while(p){
7086             b.unshift(p.attributes[attr]);
7087             p = p.parentNode;
7088         }
7089         var sep = this.getOwnerTree().pathSeparator;
7090         return sep + b.join(sep);
7091     },
7092
7093     /**
7094      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7095      * function call will be the scope provided or the current node. The arguments to the function
7096      * will be the args provided or the current node. If the function returns false at any point,
7097      * the bubble is stopped.
7098      * @param {Function} fn The function to call
7099      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7100      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7101      */
7102     bubble : function(fn, scope, args){
7103         var p = this;
7104         while(p){
7105             if(fn.call(scope || p, args || p) === false){
7106                 break;
7107             }
7108             p = p.parentNode;
7109         }
7110     },
7111
7112     /**
7113      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7114      * function call will be the scope provided or the current node. The arguments to the function
7115      * will be the args provided or the current node. If the function returns false at any point,
7116      * the cascade is stopped on that branch.
7117      * @param {Function} fn The function to call
7118      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7119      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7120      */
7121     cascade : function(fn, scope, args){
7122         if(fn.call(scope || this, args || this) !== false){
7123             var cs = this.childNodes;
7124             for(var i = 0, len = cs.length; i < len; i++) {
7125                 cs[i].cascade(fn, scope, args);
7126             }
7127         }
7128     },
7129
7130     /**
7131      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7132      * function call will be the scope provided or the current node. The arguments to the function
7133      * will be the args provided or the current node. If the function returns false at any point,
7134      * the iteration stops.
7135      * @param {Function} fn The function to call
7136      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7137      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7138      */
7139     eachChild : function(fn, scope, args){
7140         var cs = this.childNodes;
7141         for(var i = 0, len = cs.length; i < len; i++) {
7142                 if(fn.call(scope || this, args || cs[i]) === false){
7143                     break;
7144                 }
7145         }
7146     },
7147
7148     /**
7149      * Finds the first child that has the attribute with the specified value.
7150      * @param {String} attribute The attribute name
7151      * @param {Mixed} value The value to search for
7152      * @return {Node} The found child or null if none was found
7153      */
7154     findChild : function(attribute, value){
7155         var cs = this.childNodes;
7156         for(var i = 0, len = cs.length; i < len; i++) {
7157                 if(cs[i].attributes[attribute] == value){
7158                     return cs[i];
7159                 }
7160         }
7161         return null;
7162     },
7163
7164     /**
7165      * Finds the first child by a custom function. The child matches if the function passed
7166      * returns true.
7167      * @param {Function} fn
7168      * @param {Object} scope (optional)
7169      * @return {Node} The found child or null if none was found
7170      */
7171     findChildBy : function(fn, scope){
7172         var cs = this.childNodes;
7173         for(var i = 0, len = cs.length; i < len; i++) {
7174                 if(fn.call(scope||cs[i], cs[i]) === true){
7175                     return cs[i];
7176                 }
7177         }
7178         return null;
7179     },
7180
7181     /**
7182      * Sorts this nodes children using the supplied sort function
7183      * @param {Function} fn
7184      * @param {Object} scope (optional)
7185      */
7186     sort : function(fn, scope){
7187         var cs = this.childNodes;
7188         var len = cs.length;
7189         if(len > 0){
7190             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7191             cs.sort(sortFn);
7192             for(var i = 0; i < len; i++){
7193                 var n = cs[i];
7194                 n.previousSibling = cs[i-1];
7195                 n.nextSibling = cs[i+1];
7196                 if(i == 0){
7197                     this.setFirstChild(n);
7198                 }
7199                 if(i == len-1){
7200                     this.setLastChild(n);
7201                 }
7202             }
7203         }
7204     },
7205
7206     /**
7207      * Returns true if this node is an ancestor (at any point) of the passed node.
7208      * @param {Node} node
7209      * @return {Boolean}
7210      */
7211     contains : function(node){
7212         return node.isAncestor(this);
7213     },
7214
7215     /**
7216      * Returns true if the passed node is an ancestor (at any point) of this node.
7217      * @param {Node} node
7218      * @return {Boolean}
7219      */
7220     isAncestor : function(node){
7221         var p = this.parentNode;
7222         while(p){
7223             if(p == node){
7224                 return true;
7225             }
7226             p = p.parentNode;
7227         }
7228         return false;
7229     },
7230
7231     toString : function(){
7232         return "[Node"+(this.id?" "+this.id:"")+"]";
7233     }
7234 });/*
7235  * Based on:
7236  * Ext JS Library 1.1.1
7237  * Copyright(c) 2006-2007, Ext JS, LLC.
7238  *
7239  * Originally Released Under LGPL - original licence link has changed is not relivant.
7240  *
7241  * Fork - LGPL
7242  * <script type="text/javascript">
7243  */
7244  
7245
7246 /**
7247  * @class Roo.ComponentMgr
7248  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7249  * @singleton
7250  */
7251 Roo.ComponentMgr = function(){
7252     var all = new Roo.util.MixedCollection();
7253
7254     return {
7255         /**
7256          * Registers a component.
7257          * @param {Roo.Component} c The component
7258          */
7259         register : function(c){
7260             all.add(c);
7261         },
7262
7263         /**
7264          * Unregisters a component.
7265          * @param {Roo.Component} c The component
7266          */
7267         unregister : function(c){
7268             all.remove(c);
7269         },
7270
7271         /**
7272          * Returns a component by id
7273          * @param {String} id The component id
7274          */
7275         get : function(id){
7276             return all.get(id);
7277         },
7278
7279         /**
7280          * Registers a function that will be called when a specified component is added to ComponentMgr
7281          * @param {String} id The component id
7282          * @param {Funtction} fn The callback function
7283          * @param {Object} scope The scope of the callback
7284          */
7285         onAvailable : function(id, fn, scope){
7286             all.on("add", function(index, o){
7287                 if(o.id == id){
7288                     fn.call(scope || o, o);
7289                     all.un("add", fn, scope);
7290                 }
7291             });
7292         }
7293     };
7294 }();/*
7295  * Based on:
7296  * Ext JS Library 1.1.1
7297  * Copyright(c) 2006-2007, Ext JS, LLC.
7298  *
7299  * Originally Released Under LGPL - original licence link has changed is not relivant.
7300  *
7301  * Fork - LGPL
7302  * <script type="text/javascript">
7303  */
7304  
7305 /**
7306  * @class Roo.Component
7307  * @extends Roo.util.Observable
7308  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7309  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7310  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7311  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7312  * All visual components (widgets) that require rendering into a layout should subclass Component.
7313  * @constructor
7314  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7315  * 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
7316  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7317  */
7318 Roo.Component = function(config){
7319     config = config || {};
7320     if(config.tagName || config.dom || typeof config == "string"){ // element object
7321         config = {el: config, id: config.id || config};
7322     }
7323     this.initialConfig = config;
7324
7325     Roo.apply(this, config);
7326     this.addEvents({
7327         /**
7328          * @event disable
7329          * Fires after the component is disabled.
7330              * @param {Roo.Component} this
7331              */
7332         disable : true,
7333         /**
7334          * @event enable
7335          * Fires after the component is enabled.
7336              * @param {Roo.Component} this
7337              */
7338         enable : true,
7339         /**
7340          * @event beforeshow
7341          * Fires before the component is shown.  Return false to stop the show.
7342              * @param {Roo.Component} this
7343              */
7344         beforeshow : true,
7345         /**
7346          * @event show
7347          * Fires after the component is shown.
7348              * @param {Roo.Component} this
7349              */
7350         show : true,
7351         /**
7352          * @event beforehide
7353          * Fires before the component is hidden. Return false to stop the hide.
7354              * @param {Roo.Component} this
7355              */
7356         beforehide : true,
7357         /**
7358          * @event hide
7359          * Fires after the component is hidden.
7360              * @param {Roo.Component} this
7361              */
7362         hide : true,
7363         /**
7364          * @event beforerender
7365          * Fires before the component is rendered. Return false to stop the render.
7366              * @param {Roo.Component} this
7367              */
7368         beforerender : true,
7369         /**
7370          * @event render
7371          * Fires after the component is rendered.
7372              * @param {Roo.Component} this
7373              */
7374         render : true,
7375         /**
7376          * @event beforedestroy
7377          * Fires before the component is destroyed. Return false to stop the destroy.
7378              * @param {Roo.Component} this
7379              */
7380         beforedestroy : true,
7381         /**
7382          * @event destroy
7383          * Fires after the component is destroyed.
7384              * @param {Roo.Component} this
7385              */
7386         destroy : true
7387     });
7388     if(!this.id){
7389         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7390     }
7391     Roo.ComponentMgr.register(this);
7392     Roo.Component.superclass.constructor.call(this);
7393     this.initComponent();
7394     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7395         this.render(this.renderTo);
7396         delete this.renderTo;
7397     }
7398 };
7399
7400 /** @private */
7401 Roo.Component.AUTO_ID = 1000;
7402
7403 Roo.extend(Roo.Component, Roo.util.Observable, {
7404     /**
7405      * @scope Roo.Component.prototype
7406      * @type {Boolean}
7407      * true if this component is hidden. Read-only.
7408      */
7409     hidden : false,
7410     /**
7411      * @type {Boolean}
7412      * true if this component is disabled. Read-only.
7413      */
7414     disabled : false,
7415     /**
7416      * @type {Boolean}
7417      * true if this component has been rendered. Read-only.
7418      */
7419     rendered : false,
7420     
7421     /** @cfg {String} disableClass
7422      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7423      */
7424     disabledClass : "x-item-disabled",
7425         /** @cfg {Boolean} allowDomMove
7426          * Whether the component can move the Dom node when rendering (defaults to true).
7427          */
7428     allowDomMove : true,
7429     /** @cfg {String} hideMode
7430      * How this component should hidden. Supported values are
7431      * "visibility" (css visibility), "offsets" (negative offset position) and
7432      * "display" (css display) - defaults to "display".
7433      */
7434     hideMode: 'display',
7435
7436     /** @private */
7437     ctype : "Roo.Component",
7438
7439     /**
7440      * @cfg {String} actionMode 
7441      * which property holds the element that used for  hide() / show() / disable() / enable()
7442      * default is 'el' 
7443      */
7444     actionMode : "el",
7445
7446     /** @private */
7447     getActionEl : function(){
7448         return this[this.actionMode];
7449     },
7450
7451     initComponent : Roo.emptyFn,
7452     /**
7453      * If this is a lazy rendering component, render it to its container element.
7454      * @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.
7455      */
7456     render : function(container, position){
7457         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7458             if(!container && this.el){
7459                 this.el = Roo.get(this.el);
7460                 container = this.el.dom.parentNode;
7461                 this.allowDomMove = false;
7462             }
7463             this.container = Roo.get(container);
7464             this.rendered = true;
7465             if(position !== undefined){
7466                 if(typeof position == 'number'){
7467                     position = this.container.dom.childNodes[position];
7468                 }else{
7469                     position = Roo.getDom(position);
7470                 }
7471             }
7472             this.onRender(this.container, position || null);
7473             if(this.cls){
7474                 this.el.addClass(this.cls);
7475                 delete this.cls;
7476             }
7477             if(this.style){
7478                 this.el.applyStyles(this.style);
7479                 delete this.style;
7480             }
7481             this.fireEvent("render", this);
7482             this.afterRender(this.container);
7483             if(this.hidden){
7484                 this.hide();
7485             }
7486             if(this.disabled){
7487                 this.disable();
7488             }
7489         }
7490         return this;
7491     },
7492
7493     /** @private */
7494     // default function is not really useful
7495     onRender : function(ct, position){
7496         if(this.el){
7497             this.el = Roo.get(this.el);
7498             if(this.allowDomMove !== false){
7499                 ct.dom.insertBefore(this.el.dom, position);
7500             }
7501         }
7502     },
7503
7504     /** @private */
7505     getAutoCreate : function(){
7506         var cfg = typeof this.autoCreate == "object" ?
7507                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7508         if(this.id && !cfg.id){
7509             cfg.id = this.id;
7510         }
7511         return cfg;
7512     },
7513
7514     /** @private */
7515     afterRender : Roo.emptyFn,
7516
7517     /**
7518      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7519      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7520      */
7521     destroy : function(){
7522         if(this.fireEvent("beforedestroy", this) !== false){
7523             this.purgeListeners();
7524             this.beforeDestroy();
7525             if(this.rendered){
7526                 this.el.removeAllListeners();
7527                 this.el.remove();
7528                 if(this.actionMode == "container"){
7529                     this.container.remove();
7530                 }
7531             }
7532             this.onDestroy();
7533             Roo.ComponentMgr.unregister(this);
7534             this.fireEvent("destroy", this);
7535         }
7536     },
7537
7538         /** @private */
7539     beforeDestroy : function(){
7540
7541     },
7542
7543         /** @private */
7544         onDestroy : function(){
7545
7546     },
7547
7548     /**
7549      * Returns the underlying {@link Roo.Element}.
7550      * @return {Roo.Element} The element
7551      */
7552     getEl : function(){
7553         return this.el;
7554     },
7555
7556     /**
7557      * Returns the id of this component.
7558      * @return {String}
7559      */
7560     getId : function(){
7561         return this.id;
7562     },
7563
7564     /**
7565      * Try to focus this component.
7566      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7567      * @return {Roo.Component} this
7568      */
7569     focus : function(selectText){
7570         if(this.rendered){
7571             this.el.focus();
7572             if(selectText === true){
7573                 this.el.dom.select();
7574             }
7575         }
7576         return this;
7577     },
7578
7579     /** @private */
7580     blur : function(){
7581         if(this.rendered){
7582             this.el.blur();
7583         }
7584         return this;
7585     },
7586
7587     /**
7588      * Disable this component.
7589      * @return {Roo.Component} this
7590      */
7591     disable : function(){
7592         if(this.rendered){
7593             this.onDisable();
7594         }
7595         this.disabled = true;
7596         this.fireEvent("disable", this);
7597         return this;
7598     },
7599
7600         // private
7601     onDisable : function(){
7602         this.getActionEl().addClass(this.disabledClass);
7603         this.el.dom.disabled = true;
7604     },
7605
7606     /**
7607      * Enable this component.
7608      * @return {Roo.Component} this
7609      */
7610     enable : function(){
7611         if(this.rendered){
7612             this.onEnable();
7613         }
7614         this.disabled = false;
7615         this.fireEvent("enable", this);
7616         return this;
7617     },
7618
7619         // private
7620     onEnable : function(){
7621         this.getActionEl().removeClass(this.disabledClass);
7622         this.el.dom.disabled = false;
7623     },
7624
7625     /**
7626      * Convenience function for setting disabled/enabled by boolean.
7627      * @param {Boolean} disabled
7628      */
7629     setDisabled : function(disabled){
7630         this[disabled ? "disable" : "enable"]();
7631     },
7632
7633     /**
7634      * Show this component.
7635      * @return {Roo.Component} this
7636      */
7637     show: function(){
7638         if(this.fireEvent("beforeshow", this) !== false){
7639             this.hidden = false;
7640             if(this.rendered){
7641                 this.onShow();
7642             }
7643             this.fireEvent("show", this);
7644         }
7645         return this;
7646     },
7647
7648     // private
7649     onShow : function(){
7650         var ae = this.getActionEl();
7651         if(this.hideMode == 'visibility'){
7652             ae.dom.style.visibility = "visible";
7653         }else if(this.hideMode == 'offsets'){
7654             ae.removeClass('x-hidden');
7655         }else{
7656             ae.dom.style.display = "";
7657         }
7658     },
7659
7660     /**
7661      * Hide this component.
7662      * @return {Roo.Component} this
7663      */
7664     hide: function(){
7665         if(this.fireEvent("beforehide", this) !== false){
7666             this.hidden = true;
7667             if(this.rendered){
7668                 this.onHide();
7669             }
7670             this.fireEvent("hide", this);
7671         }
7672         return this;
7673     },
7674
7675     // private
7676     onHide : function(){
7677         var ae = this.getActionEl();
7678         if(this.hideMode == 'visibility'){
7679             ae.dom.style.visibility = "hidden";
7680         }else if(this.hideMode == 'offsets'){
7681             ae.addClass('x-hidden');
7682         }else{
7683             ae.dom.style.display = "none";
7684         }
7685     },
7686
7687     /**
7688      * Convenience function to hide or show this component by boolean.
7689      * @param {Boolean} visible True to show, false to hide
7690      * @return {Roo.Component} this
7691      */
7692     setVisible: function(visible){
7693         if(visible) {
7694             this.show();
7695         }else{
7696             this.hide();
7697         }
7698         return this;
7699     },
7700
7701     /**
7702      * Returns true if this component is visible.
7703      */
7704     isVisible : function(){
7705         return this.getActionEl().isVisible();
7706     },
7707
7708     cloneConfig : function(overrides){
7709         overrides = overrides || {};
7710         var id = overrides.id || Roo.id();
7711         var cfg = Roo.applyIf(overrides, this.initialConfig);
7712         cfg.id = id; // prevent dup id
7713         return new this.constructor(cfg);
7714     }
7715 });/*
7716  * Based on:
7717  * Ext JS Library 1.1.1
7718  * Copyright(c) 2006-2007, Ext JS, LLC.
7719  *
7720  * Originally Released Under LGPL - original licence link has changed is not relivant.
7721  *
7722  * Fork - LGPL
7723  * <script type="text/javascript">
7724  */
7725  (function(){ 
7726 /**
7727  * @class Roo.Layer
7728  * @extends Roo.Element
7729  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7730  * automatic maintaining of shadow/shim positions.
7731  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7732  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7733  * you can pass a string with a CSS class name. False turns off the shadow.
7734  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7735  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7736  * @cfg {String} cls CSS class to add to the element
7737  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7738  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7739  * @constructor
7740  * @param {Object} config An object with config options.
7741  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7742  */
7743
7744 Roo.Layer = function(config, existingEl){
7745     config = config || {};
7746     var dh = Roo.DomHelper;
7747     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7748     if(existingEl){
7749         this.dom = Roo.getDom(existingEl);
7750     }
7751     if(!this.dom){
7752         var o = config.dh || {tag: "div", cls: "x-layer"};
7753         this.dom = dh.append(pel, o);
7754     }
7755     if(config.cls){
7756         this.addClass(config.cls);
7757     }
7758     this.constrain = config.constrain !== false;
7759     this.visibilityMode = Roo.Element.VISIBILITY;
7760     if(config.id){
7761         this.id = this.dom.id = config.id;
7762     }else{
7763         this.id = Roo.id(this.dom);
7764     }
7765     this.zindex = config.zindex || this.getZIndex();
7766     this.position("absolute", this.zindex);
7767     if(config.shadow){
7768         this.shadowOffset = config.shadowOffset || 4;
7769         this.shadow = new Roo.Shadow({
7770             offset : this.shadowOffset,
7771             mode : config.shadow
7772         });
7773     }else{
7774         this.shadowOffset = 0;
7775     }
7776     this.useShim = config.shim !== false && Roo.useShims;
7777     this.useDisplay = config.useDisplay;
7778     this.hide();
7779 };
7780
7781 var supr = Roo.Element.prototype;
7782
7783 // shims are shared among layer to keep from having 100 iframes
7784 var shims = [];
7785
7786 Roo.extend(Roo.Layer, Roo.Element, {
7787
7788     getZIndex : function(){
7789         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7790     },
7791
7792     getShim : function(){
7793         if(!this.useShim){
7794             return null;
7795         }
7796         if(this.shim){
7797             return this.shim;
7798         }
7799         var shim = shims.shift();
7800         if(!shim){
7801             shim = this.createShim();
7802             shim.enableDisplayMode('block');
7803             shim.dom.style.display = 'none';
7804             shim.dom.style.visibility = 'visible';
7805         }
7806         var pn = this.dom.parentNode;
7807         if(shim.dom.parentNode != pn){
7808             pn.insertBefore(shim.dom, this.dom);
7809         }
7810         shim.setStyle('z-index', this.getZIndex()-2);
7811         this.shim = shim;
7812         return shim;
7813     },
7814
7815     hideShim : function(){
7816         if(this.shim){
7817             this.shim.setDisplayed(false);
7818             shims.push(this.shim);
7819             delete this.shim;
7820         }
7821     },
7822
7823     disableShadow : function(){
7824         if(this.shadow){
7825             this.shadowDisabled = true;
7826             this.shadow.hide();
7827             this.lastShadowOffset = this.shadowOffset;
7828             this.shadowOffset = 0;
7829         }
7830     },
7831
7832     enableShadow : function(show){
7833         if(this.shadow){
7834             this.shadowDisabled = false;
7835             this.shadowOffset = this.lastShadowOffset;
7836             delete this.lastShadowOffset;
7837             if(show){
7838                 this.sync(true);
7839             }
7840         }
7841     },
7842
7843     // private
7844     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7845     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7846     sync : function(doShow){
7847         var sw = this.shadow;
7848         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7849             var sh = this.getShim();
7850
7851             var w = this.getWidth(),
7852                 h = this.getHeight();
7853
7854             var l = this.getLeft(true),
7855                 t = this.getTop(true);
7856
7857             if(sw && !this.shadowDisabled){
7858                 if(doShow && !sw.isVisible()){
7859                     sw.show(this);
7860                 }else{
7861                     sw.realign(l, t, w, h);
7862                 }
7863                 if(sh){
7864                     if(doShow){
7865                        sh.show();
7866                     }
7867                     // fit the shim behind the shadow, so it is shimmed too
7868                     var a = sw.adjusts, s = sh.dom.style;
7869                     s.left = (Math.min(l, l+a.l))+"px";
7870                     s.top = (Math.min(t, t+a.t))+"px";
7871                     s.width = (w+a.w)+"px";
7872                     s.height = (h+a.h)+"px";
7873                 }
7874             }else if(sh){
7875                 if(doShow){
7876                    sh.show();
7877                 }
7878                 sh.setSize(w, h);
7879                 sh.setLeftTop(l, t);
7880             }
7881             
7882         }
7883     },
7884
7885     // private
7886     destroy : function(){
7887         this.hideShim();
7888         if(this.shadow){
7889             this.shadow.hide();
7890         }
7891         this.removeAllListeners();
7892         var pn = this.dom.parentNode;
7893         if(pn){
7894             pn.removeChild(this.dom);
7895         }
7896         Roo.Element.uncache(this.id);
7897     },
7898
7899     remove : function(){
7900         this.destroy();
7901     },
7902
7903     // private
7904     beginUpdate : function(){
7905         this.updating = true;
7906     },
7907
7908     // private
7909     endUpdate : function(){
7910         this.updating = false;
7911         this.sync(true);
7912     },
7913
7914     // private
7915     hideUnders : function(negOffset){
7916         if(this.shadow){
7917             this.shadow.hide();
7918         }
7919         this.hideShim();
7920     },
7921
7922     // private
7923     constrainXY : function(){
7924         if(this.constrain){
7925             var vw = Roo.lib.Dom.getViewWidth(),
7926                 vh = Roo.lib.Dom.getViewHeight();
7927             var s = Roo.get(document).getScroll();
7928
7929             var xy = this.getXY();
7930             var x = xy[0], y = xy[1];   
7931             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7932             // only move it if it needs it
7933             var moved = false;
7934             // first validate right/bottom
7935             if((x + w) > vw+s.left){
7936                 x = vw - w - this.shadowOffset;
7937                 moved = true;
7938             }
7939             if((y + h) > vh+s.top){
7940                 y = vh - h - this.shadowOffset;
7941                 moved = true;
7942             }
7943             // then make sure top/left isn't negative
7944             if(x < s.left){
7945                 x = s.left;
7946                 moved = true;
7947             }
7948             if(y < s.top){
7949                 y = s.top;
7950                 moved = true;
7951             }
7952             if(moved){
7953                 if(this.avoidY){
7954                     var ay = this.avoidY;
7955                     if(y <= ay && (y+h) >= ay){
7956                         y = ay-h-5;   
7957                     }
7958                 }
7959                 xy = [x, y];
7960                 this.storeXY(xy);
7961                 supr.setXY.call(this, xy);
7962                 this.sync();
7963             }
7964         }
7965     },
7966
7967     isVisible : function(){
7968         return this.visible;    
7969     },
7970
7971     // private
7972     showAction : function(){
7973         this.visible = true; // track visibility to prevent getStyle calls
7974         if(this.useDisplay === true){
7975             this.setDisplayed("");
7976         }else if(this.lastXY){
7977             supr.setXY.call(this, this.lastXY);
7978         }else if(this.lastLT){
7979             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7980         }
7981     },
7982
7983     // private
7984     hideAction : function(){
7985         this.visible = false;
7986         if(this.useDisplay === true){
7987             this.setDisplayed(false);
7988         }else{
7989             this.setLeftTop(-10000,-10000);
7990         }
7991     },
7992
7993     // overridden Element method
7994     setVisible : function(v, a, d, c, e){
7995         if(v){
7996             this.showAction();
7997         }
7998         if(a && v){
7999             var cb = function(){
8000                 this.sync(true);
8001                 if(c){
8002                     c();
8003                 }
8004             }.createDelegate(this);
8005             supr.setVisible.call(this, true, true, d, cb, e);
8006         }else{
8007             if(!v){
8008                 this.hideUnders(true);
8009             }
8010             var cb = c;
8011             if(a){
8012                 cb = function(){
8013                     this.hideAction();
8014                     if(c){
8015                         c();
8016                     }
8017                 }.createDelegate(this);
8018             }
8019             supr.setVisible.call(this, v, a, d, cb, e);
8020             if(v){
8021                 this.sync(true);
8022             }else if(!a){
8023                 this.hideAction();
8024             }
8025         }
8026     },
8027
8028     storeXY : function(xy){
8029         delete this.lastLT;
8030         this.lastXY = xy;
8031     },
8032
8033     storeLeftTop : function(left, top){
8034         delete this.lastXY;
8035         this.lastLT = [left, top];
8036     },
8037
8038     // private
8039     beforeFx : function(){
8040         this.beforeAction();
8041         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8042     },
8043
8044     // private
8045     afterFx : function(){
8046         Roo.Layer.superclass.afterFx.apply(this, arguments);
8047         this.sync(this.isVisible());
8048     },
8049
8050     // private
8051     beforeAction : function(){
8052         if(!this.updating && this.shadow){
8053             this.shadow.hide();
8054         }
8055     },
8056
8057     // overridden Element method
8058     setLeft : function(left){
8059         this.storeLeftTop(left, this.getTop(true));
8060         supr.setLeft.apply(this, arguments);
8061         this.sync();
8062     },
8063
8064     setTop : function(top){
8065         this.storeLeftTop(this.getLeft(true), top);
8066         supr.setTop.apply(this, arguments);
8067         this.sync();
8068     },
8069
8070     setLeftTop : function(left, top){
8071         this.storeLeftTop(left, top);
8072         supr.setLeftTop.apply(this, arguments);
8073         this.sync();
8074     },
8075
8076     setXY : function(xy, a, d, c, e){
8077         this.fixDisplay();
8078         this.beforeAction();
8079         this.storeXY(xy);
8080         var cb = this.createCB(c);
8081         supr.setXY.call(this, xy, a, d, cb, e);
8082         if(!a){
8083             cb();
8084         }
8085     },
8086
8087     // private
8088     createCB : function(c){
8089         var el = this;
8090         return function(){
8091             el.constrainXY();
8092             el.sync(true);
8093             if(c){
8094                 c();
8095             }
8096         };
8097     },
8098
8099     // overridden Element method
8100     setX : function(x, a, d, c, e){
8101         this.setXY([x, this.getY()], a, d, c, e);
8102     },
8103
8104     // overridden Element method
8105     setY : function(y, a, d, c, e){
8106         this.setXY([this.getX(), y], a, d, c, e);
8107     },
8108
8109     // overridden Element method
8110     setSize : function(w, h, a, d, c, e){
8111         this.beforeAction();
8112         var cb = this.createCB(c);
8113         supr.setSize.call(this, w, h, a, d, cb, e);
8114         if(!a){
8115             cb();
8116         }
8117     },
8118
8119     // overridden Element method
8120     setWidth : function(w, a, d, c, e){
8121         this.beforeAction();
8122         var cb = this.createCB(c);
8123         supr.setWidth.call(this, w, a, d, cb, e);
8124         if(!a){
8125             cb();
8126         }
8127     },
8128
8129     // overridden Element method
8130     setHeight : function(h, a, d, c, e){
8131         this.beforeAction();
8132         var cb = this.createCB(c);
8133         supr.setHeight.call(this, h, a, d, cb, e);
8134         if(!a){
8135             cb();
8136         }
8137     },
8138
8139     // overridden Element method
8140     setBounds : function(x, y, w, h, a, d, c, e){
8141         this.beforeAction();
8142         var cb = this.createCB(c);
8143         if(!a){
8144             this.storeXY([x, y]);
8145             supr.setXY.call(this, [x, y]);
8146             supr.setSize.call(this, w, h, a, d, cb, e);
8147             cb();
8148         }else{
8149             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8150         }
8151         return this;
8152     },
8153     
8154     /**
8155      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8156      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8157      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8158      * @param {Number} zindex The new z-index to set
8159      * @return {this} The Layer
8160      */
8161     setZIndex : function(zindex){
8162         this.zindex = zindex;
8163         this.setStyle("z-index", zindex + 2);
8164         if(this.shadow){
8165             this.shadow.setZIndex(zindex + 1);
8166         }
8167         if(this.shim){
8168             this.shim.setStyle("z-index", zindex);
8169         }
8170     }
8171 });
8172 })();/*
8173  * Based on:
8174  * Ext JS Library 1.1.1
8175  * Copyright(c) 2006-2007, Ext JS, LLC.
8176  *
8177  * Originally Released Under LGPL - original licence link has changed is not relivant.
8178  *
8179  * Fork - LGPL
8180  * <script type="text/javascript">
8181  */
8182
8183
8184 /**
8185  * @class Roo.Shadow
8186  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8187  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8188  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8189  * @constructor
8190  * Create a new Shadow
8191  * @param {Object} config The config object
8192  */
8193 Roo.Shadow = function(config){
8194     Roo.apply(this, config);
8195     if(typeof this.mode != "string"){
8196         this.mode = this.defaultMode;
8197     }
8198     var o = this.offset, a = {h: 0};
8199     var rad = Math.floor(this.offset/2);
8200     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8201         case "drop":
8202             a.w = 0;
8203             a.l = a.t = o;
8204             a.t -= 1;
8205             if(Roo.isIE){
8206                 a.l -= this.offset + rad;
8207                 a.t -= this.offset + rad;
8208                 a.w -= rad;
8209                 a.h -= rad;
8210                 a.t += 1;
8211             }
8212         break;
8213         case "sides":
8214             a.w = (o*2);
8215             a.l = -o;
8216             a.t = o-1;
8217             if(Roo.isIE){
8218                 a.l -= (this.offset - rad);
8219                 a.t -= this.offset + rad;
8220                 a.l += 1;
8221                 a.w -= (this.offset - rad)*2;
8222                 a.w -= rad + 1;
8223                 a.h -= 1;
8224             }
8225         break;
8226         case "frame":
8227             a.w = a.h = (o*2);
8228             a.l = a.t = -o;
8229             a.t += 1;
8230             a.h -= 2;
8231             if(Roo.isIE){
8232                 a.l -= (this.offset - rad);
8233                 a.t -= (this.offset - rad);
8234                 a.l += 1;
8235                 a.w -= (this.offset + rad + 1);
8236                 a.h -= (this.offset + rad);
8237                 a.h += 1;
8238             }
8239         break;
8240     };
8241
8242     this.adjusts = a;
8243 };
8244
8245 Roo.Shadow.prototype = {
8246     /**
8247      * @cfg {String} mode
8248      * The shadow display mode.  Supports the following options:<br />
8249      * sides: Shadow displays on both sides and bottom only<br />
8250      * frame: Shadow displays equally on all four sides<br />
8251      * drop: Traditional bottom-right drop shadow (default)
8252      */
8253     /**
8254      * @cfg {String} offset
8255      * The number of pixels to offset the shadow from the element (defaults to 4)
8256      */
8257     offset: 4,
8258
8259     // private
8260     defaultMode: "drop",
8261
8262     /**
8263      * Displays the shadow under the target element
8264      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8265      */
8266     show : function(target){
8267         target = Roo.get(target);
8268         if(!this.el){
8269             this.el = Roo.Shadow.Pool.pull();
8270             if(this.el.dom.nextSibling != target.dom){
8271                 this.el.insertBefore(target);
8272             }
8273         }
8274         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8275         if(Roo.isIE){
8276             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8277         }
8278         this.realign(
8279             target.getLeft(true),
8280             target.getTop(true),
8281             target.getWidth(),
8282             target.getHeight()
8283         );
8284         this.el.dom.style.display = "block";
8285     },
8286
8287     /**
8288      * Returns true if the shadow is visible, else false
8289      */
8290     isVisible : function(){
8291         return this.el ? true : false;  
8292     },
8293
8294     /**
8295      * Direct alignment when values are already available. Show must be called at least once before
8296      * calling this method to ensure it is initialized.
8297      * @param {Number} left The target element left position
8298      * @param {Number} top The target element top position
8299      * @param {Number} width The target element width
8300      * @param {Number} height The target element height
8301      */
8302     realign : function(l, t, w, h){
8303         if(!this.el){
8304             return;
8305         }
8306         var a = this.adjusts, d = this.el.dom, s = d.style;
8307         var iea = 0;
8308         s.left = (l+a.l)+"px";
8309         s.top = (t+a.t)+"px";
8310         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8311  
8312         if(s.width != sws || s.height != shs){
8313             s.width = sws;
8314             s.height = shs;
8315             if(!Roo.isIE){
8316                 var cn = d.childNodes;
8317                 var sww = Math.max(0, (sw-12))+"px";
8318                 cn[0].childNodes[1].style.width = sww;
8319                 cn[1].childNodes[1].style.width = sww;
8320                 cn[2].childNodes[1].style.width = sww;
8321                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8322             }
8323         }
8324     },
8325
8326     /**
8327      * Hides this shadow
8328      */
8329     hide : function(){
8330         if(this.el){
8331             this.el.dom.style.display = "none";
8332             Roo.Shadow.Pool.push(this.el);
8333             delete this.el;
8334         }
8335     },
8336
8337     /**
8338      * Adjust the z-index of this shadow
8339      * @param {Number} zindex The new z-index
8340      */
8341     setZIndex : function(z){
8342         this.zIndex = z;
8343         if(this.el){
8344             this.el.setStyle("z-index", z);
8345         }
8346     }
8347 };
8348
8349 // Private utility class that manages the internal Shadow cache
8350 Roo.Shadow.Pool = function(){
8351     var p = [];
8352     var markup = Roo.isIE ?
8353                  '<div class="x-ie-shadow"></div>' :
8354                  '<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>';
8355     return {
8356         pull : function(){
8357             var sh = p.shift();
8358             if(!sh){
8359                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8360                 sh.autoBoxAdjust = false;
8361             }
8362             return sh;
8363         },
8364
8365         push : function(sh){
8366             p.push(sh);
8367         }
8368     };
8369 }();/*
8370  * Based on:
8371  * Ext JS Library 1.1.1
8372  * Copyright(c) 2006-2007, Ext JS, LLC.
8373  *
8374  * Originally Released Under LGPL - original licence link has changed is not relivant.
8375  *
8376  * Fork - LGPL
8377  * <script type="text/javascript">
8378  */
8379
8380 /**
8381  * @class Roo.BoxComponent
8382  * @extends Roo.Component
8383  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8384  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8385  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8386  * layout containers.
8387  * @constructor
8388  * @param {Roo.Element/String/Object} config The configuration options.
8389  */
8390 Roo.BoxComponent = function(config){
8391     Roo.Component.call(this, config);
8392     this.addEvents({
8393         /**
8394          * @event resize
8395          * Fires after the component is resized.
8396              * @param {Roo.Component} this
8397              * @param {Number} adjWidth The box-adjusted width that was set
8398              * @param {Number} adjHeight The box-adjusted height that was set
8399              * @param {Number} rawWidth The width that was originally specified
8400              * @param {Number} rawHeight The height that was originally specified
8401              */
8402         resize : true,
8403         /**
8404          * @event move
8405          * Fires after the component is moved.
8406              * @param {Roo.Component} this
8407              * @param {Number} x The new x position
8408              * @param {Number} y The new y position
8409              */
8410         move : true
8411     });
8412 };
8413
8414 Roo.extend(Roo.BoxComponent, Roo.Component, {
8415     // private, set in afterRender to signify that the component has been rendered
8416     boxReady : false,
8417     // private, used to defer height settings to subclasses
8418     deferHeight: false,
8419     /** @cfg {Number} width
8420      * width (optional) size of component
8421      */
8422      /** @cfg {Number} height
8423      * height (optional) size of component
8424      */
8425      
8426     /**
8427      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8428      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8429      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8430      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8431      * @return {Roo.BoxComponent} this
8432      */
8433     setSize : function(w, h){
8434         // support for standard size objects
8435         if(typeof w == 'object'){
8436             h = w.height;
8437             w = w.width;
8438         }
8439         // not rendered
8440         if(!this.boxReady){
8441             this.width = w;
8442             this.height = h;
8443             return this;
8444         }
8445
8446         // prevent recalcs when not needed
8447         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8448             return this;
8449         }
8450         this.lastSize = {width: w, height: h};
8451
8452         var adj = this.adjustSize(w, h);
8453         var aw = adj.width, ah = adj.height;
8454         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8455             var rz = this.getResizeEl();
8456             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8457                 rz.setSize(aw, ah);
8458             }else if(!this.deferHeight && ah !== undefined){
8459                 rz.setHeight(ah);
8460             }else if(aw !== undefined){
8461                 rz.setWidth(aw);
8462             }
8463             this.onResize(aw, ah, w, h);
8464             this.fireEvent('resize', this, aw, ah, w, h);
8465         }
8466         return this;
8467     },
8468
8469     /**
8470      * Gets the current size of the component's underlying element.
8471      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8472      */
8473     getSize : function(){
8474         return this.el.getSize();
8475     },
8476
8477     /**
8478      * Gets the current XY position of the component's underlying element.
8479      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8480      * @return {Array} The XY position of the element (e.g., [100, 200])
8481      */
8482     getPosition : function(local){
8483         if(local === true){
8484             return [this.el.getLeft(true), this.el.getTop(true)];
8485         }
8486         return this.xy || this.el.getXY();
8487     },
8488
8489     /**
8490      * Gets the current box measurements of the component's underlying element.
8491      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8492      * @returns {Object} box An object in the format {x, y, width, height}
8493      */
8494     getBox : function(local){
8495         var s = this.el.getSize();
8496         if(local){
8497             s.x = this.el.getLeft(true);
8498             s.y = this.el.getTop(true);
8499         }else{
8500             var xy = this.xy || this.el.getXY();
8501             s.x = xy[0];
8502             s.y = xy[1];
8503         }
8504         return s;
8505     },
8506
8507     /**
8508      * Sets the current box measurements of the component's underlying element.
8509      * @param {Object} box An object in the format {x, y, width, height}
8510      * @returns {Roo.BoxComponent} this
8511      */
8512     updateBox : function(box){
8513         this.setSize(box.width, box.height);
8514         this.setPagePosition(box.x, box.y);
8515         return this;
8516     },
8517
8518     // protected
8519     getResizeEl : function(){
8520         return this.resizeEl || this.el;
8521     },
8522
8523     // protected
8524     getPositionEl : function(){
8525         return this.positionEl || this.el;
8526     },
8527
8528     /**
8529      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8530      * This method fires the move event.
8531      * @param {Number} left The new left
8532      * @param {Number} top The new top
8533      * @returns {Roo.BoxComponent} this
8534      */
8535     setPosition : function(x, y){
8536         this.x = x;
8537         this.y = y;
8538         if(!this.boxReady){
8539             return this;
8540         }
8541         var adj = this.adjustPosition(x, y);
8542         var ax = adj.x, ay = adj.y;
8543
8544         var el = this.getPositionEl();
8545         if(ax !== undefined || ay !== undefined){
8546             if(ax !== undefined && ay !== undefined){
8547                 el.setLeftTop(ax, ay);
8548             }else if(ax !== undefined){
8549                 el.setLeft(ax);
8550             }else if(ay !== undefined){
8551                 el.setTop(ay);
8552             }
8553             this.onPosition(ax, ay);
8554             this.fireEvent('move', this, ax, ay);
8555         }
8556         return this;
8557     },
8558
8559     /**
8560      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8561      * This method fires the move event.
8562      * @param {Number} x The new x position
8563      * @param {Number} y The new y position
8564      * @returns {Roo.BoxComponent} this
8565      */
8566     setPagePosition : function(x, y){
8567         this.pageX = x;
8568         this.pageY = y;
8569         if(!this.boxReady){
8570             return;
8571         }
8572         if(x === undefined || y === undefined){ // cannot translate undefined points
8573             return;
8574         }
8575         var p = this.el.translatePoints(x, y);
8576         this.setPosition(p.left, p.top);
8577         return this;
8578     },
8579
8580     // private
8581     onRender : function(ct, position){
8582         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8583         if(this.resizeEl){
8584             this.resizeEl = Roo.get(this.resizeEl);
8585         }
8586         if(this.positionEl){
8587             this.positionEl = Roo.get(this.positionEl);
8588         }
8589     },
8590
8591     // private
8592     afterRender : function(){
8593         Roo.BoxComponent.superclass.afterRender.call(this);
8594         this.boxReady = true;
8595         this.setSize(this.width, this.height);
8596         if(this.x || this.y){
8597             this.setPosition(this.x, this.y);
8598         }
8599         if(this.pageX || this.pageY){
8600             this.setPagePosition(this.pageX, this.pageY);
8601         }
8602     },
8603
8604     /**
8605      * Force the component's size to recalculate based on the underlying element's current height and width.
8606      * @returns {Roo.BoxComponent} this
8607      */
8608     syncSize : function(){
8609         delete this.lastSize;
8610         this.setSize(this.el.getWidth(), this.el.getHeight());
8611         return this;
8612     },
8613
8614     /**
8615      * Called after the component is resized, this method is empty by default but can be implemented by any
8616      * subclass that needs to perform custom logic after a resize occurs.
8617      * @param {Number} adjWidth The box-adjusted width that was set
8618      * @param {Number} adjHeight The box-adjusted height that was set
8619      * @param {Number} rawWidth The width that was originally specified
8620      * @param {Number} rawHeight The height that was originally specified
8621      */
8622     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8623
8624     },
8625
8626     /**
8627      * Called after the component is moved, this method is empty by default but can be implemented by any
8628      * subclass that needs to perform custom logic after a move occurs.
8629      * @param {Number} x The new x position
8630      * @param {Number} y The new y position
8631      */
8632     onPosition : function(x, y){
8633
8634     },
8635
8636     // private
8637     adjustSize : function(w, h){
8638         if(this.autoWidth){
8639             w = 'auto';
8640         }
8641         if(this.autoHeight){
8642             h = 'auto';
8643         }
8644         return {width : w, height: h};
8645     },
8646
8647     // private
8648     adjustPosition : function(x, y){
8649         return {x : x, y: y};
8650     }
8651 });/*
8652  * Based on:
8653  * Ext JS Library 1.1.1
8654  * Copyright(c) 2006-2007, Ext JS, LLC.
8655  *
8656  * Originally Released Under LGPL - original licence link has changed is not relivant.
8657  *
8658  * Fork - LGPL
8659  * <script type="text/javascript">
8660  */
8661
8662
8663 /**
8664  * @class Roo.SplitBar
8665  * @extends Roo.util.Observable
8666  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8667  * <br><br>
8668  * Usage:
8669  * <pre><code>
8670 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8671                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8672 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8673 split.minSize = 100;
8674 split.maxSize = 600;
8675 split.animate = true;
8676 split.on('moved', splitterMoved);
8677 </code></pre>
8678  * @constructor
8679  * Create a new SplitBar
8680  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8681  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8682  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8683  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8684                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8685                         position of the SplitBar).
8686  */
8687 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8688     
8689     /** @private */
8690     this.el = Roo.get(dragElement, true);
8691     this.el.dom.unselectable = "on";
8692     /** @private */
8693     this.resizingEl = Roo.get(resizingElement, true);
8694
8695     /**
8696      * @private
8697      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8698      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8699      * @type Number
8700      */
8701     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8702     
8703     /**
8704      * The minimum size of the resizing element. (Defaults to 0)
8705      * @type Number
8706      */
8707     this.minSize = 0;
8708     
8709     /**
8710      * The maximum size of the resizing element. (Defaults to 2000)
8711      * @type Number
8712      */
8713     this.maxSize = 2000;
8714     
8715     /**
8716      * Whether to animate the transition to the new size
8717      * @type Boolean
8718      */
8719     this.animate = false;
8720     
8721     /**
8722      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8723      * @type Boolean
8724      */
8725     this.useShim = false;
8726     
8727     /** @private */
8728     this.shim = null;
8729     
8730     if(!existingProxy){
8731         /** @private */
8732         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8733     }else{
8734         this.proxy = Roo.get(existingProxy).dom;
8735     }
8736     /** @private */
8737     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8738     
8739     /** @private */
8740     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8741     
8742     /** @private */
8743     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8744     
8745     /** @private */
8746     this.dragSpecs = {};
8747     
8748     /**
8749      * @private The adapter to use to positon and resize elements
8750      */
8751     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8752     this.adapter.init(this);
8753     
8754     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8755         /** @private */
8756         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8757         this.el.addClass("x-splitbar-h");
8758     }else{
8759         /** @private */
8760         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8761         this.el.addClass("x-splitbar-v");
8762     }
8763     
8764     this.addEvents({
8765         /**
8766          * @event resize
8767          * Fires when the splitter is moved (alias for {@link #event-moved})
8768          * @param {Roo.SplitBar} this
8769          * @param {Number} newSize the new width or height
8770          */
8771         "resize" : true,
8772         /**
8773          * @event moved
8774          * Fires when the splitter is moved
8775          * @param {Roo.SplitBar} this
8776          * @param {Number} newSize the new width or height
8777          */
8778         "moved" : true,
8779         /**
8780          * @event beforeresize
8781          * Fires before the splitter is dragged
8782          * @param {Roo.SplitBar} this
8783          */
8784         "beforeresize" : true,
8785
8786         "beforeapply" : true
8787     });
8788
8789     Roo.util.Observable.call(this);
8790 };
8791
8792 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8793     onStartProxyDrag : function(x, y){
8794         this.fireEvent("beforeresize", this);
8795         if(!this.overlay){
8796             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8797             o.unselectable();
8798             o.enableDisplayMode("block");
8799             // all splitbars share the same overlay
8800             Roo.SplitBar.prototype.overlay = o;
8801         }
8802         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8803         this.overlay.show();
8804         Roo.get(this.proxy).setDisplayed("block");
8805         var size = this.adapter.getElementSize(this);
8806         this.activeMinSize = this.getMinimumSize();;
8807         this.activeMaxSize = this.getMaximumSize();;
8808         var c1 = size - this.activeMinSize;
8809         var c2 = Math.max(this.activeMaxSize - size, 0);
8810         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8811             this.dd.resetConstraints();
8812             this.dd.setXConstraint(
8813                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8814                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8815             );
8816             this.dd.setYConstraint(0, 0);
8817         }else{
8818             this.dd.resetConstraints();
8819             this.dd.setXConstraint(0, 0);
8820             this.dd.setYConstraint(
8821                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8822                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8823             );
8824          }
8825         this.dragSpecs.startSize = size;
8826         this.dragSpecs.startPoint = [x, y];
8827         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8828     },
8829     
8830     /** 
8831      * @private Called after the drag operation by the DDProxy
8832      */
8833     onEndProxyDrag : function(e){
8834         Roo.get(this.proxy).setDisplayed(false);
8835         var endPoint = Roo.lib.Event.getXY(e);
8836         if(this.overlay){
8837             this.overlay.hide();
8838         }
8839         var newSize;
8840         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8841             newSize = this.dragSpecs.startSize + 
8842                 (this.placement == Roo.SplitBar.LEFT ?
8843                     endPoint[0] - this.dragSpecs.startPoint[0] :
8844                     this.dragSpecs.startPoint[0] - endPoint[0]
8845                 );
8846         }else{
8847             newSize = this.dragSpecs.startSize + 
8848                 (this.placement == Roo.SplitBar.TOP ?
8849                     endPoint[1] - this.dragSpecs.startPoint[1] :
8850                     this.dragSpecs.startPoint[1] - endPoint[1]
8851                 );
8852         }
8853         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8854         if(newSize != this.dragSpecs.startSize){
8855             if(this.fireEvent('beforeapply', this, newSize) !== false){
8856                 this.adapter.setElementSize(this, newSize);
8857                 this.fireEvent("moved", this, newSize);
8858                 this.fireEvent("resize", this, newSize);
8859             }
8860         }
8861     },
8862     
8863     /**
8864      * Get the adapter this SplitBar uses
8865      * @return The adapter object
8866      */
8867     getAdapter : function(){
8868         return this.adapter;
8869     },
8870     
8871     /**
8872      * Set the adapter this SplitBar uses
8873      * @param {Object} adapter A SplitBar adapter object
8874      */
8875     setAdapter : function(adapter){
8876         this.adapter = adapter;
8877         this.adapter.init(this);
8878     },
8879     
8880     /**
8881      * Gets the minimum size for the resizing element
8882      * @return {Number} The minimum size
8883      */
8884     getMinimumSize : function(){
8885         return this.minSize;
8886     },
8887     
8888     /**
8889      * Sets the minimum size for the resizing element
8890      * @param {Number} minSize The minimum size
8891      */
8892     setMinimumSize : function(minSize){
8893         this.minSize = minSize;
8894     },
8895     
8896     /**
8897      * Gets the maximum size for the resizing element
8898      * @return {Number} The maximum size
8899      */
8900     getMaximumSize : function(){
8901         return this.maxSize;
8902     },
8903     
8904     /**
8905      * Sets the maximum size for the resizing element
8906      * @param {Number} maxSize The maximum size
8907      */
8908     setMaximumSize : function(maxSize){
8909         this.maxSize = maxSize;
8910     },
8911     
8912     /**
8913      * Sets the initialize size for the resizing element
8914      * @param {Number} size The initial size
8915      */
8916     setCurrentSize : function(size){
8917         var oldAnimate = this.animate;
8918         this.animate = false;
8919         this.adapter.setElementSize(this, size);
8920         this.animate = oldAnimate;
8921     },
8922     
8923     /**
8924      * Destroy this splitbar. 
8925      * @param {Boolean} removeEl True to remove the element
8926      */
8927     destroy : function(removeEl){
8928         if(this.shim){
8929             this.shim.remove();
8930         }
8931         this.dd.unreg();
8932         this.proxy.parentNode.removeChild(this.proxy);
8933         if(removeEl){
8934             this.el.remove();
8935         }
8936     }
8937 });
8938
8939 /**
8940  * @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.
8941  */
8942 Roo.SplitBar.createProxy = function(dir){
8943     var proxy = new Roo.Element(document.createElement("div"));
8944     proxy.unselectable();
8945     var cls = 'x-splitbar-proxy';
8946     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8947     document.body.appendChild(proxy.dom);
8948     return proxy.dom;
8949 };
8950
8951 /** 
8952  * @class Roo.SplitBar.BasicLayoutAdapter
8953  * Default Adapter. It assumes the splitter and resizing element are not positioned
8954  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8955  */
8956 Roo.SplitBar.BasicLayoutAdapter = function(){
8957 };
8958
8959 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8960     // do nothing for now
8961     init : function(s){
8962     
8963     },
8964     /**
8965      * Called before drag operations to get the current size of the resizing element. 
8966      * @param {Roo.SplitBar} s The SplitBar using this adapter
8967      */
8968      getElementSize : function(s){
8969         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8970             return s.resizingEl.getWidth();
8971         }else{
8972             return s.resizingEl.getHeight();
8973         }
8974     },
8975     
8976     /**
8977      * Called after drag operations to set the size of the resizing element.
8978      * @param {Roo.SplitBar} s The SplitBar using this adapter
8979      * @param {Number} newSize The new size to set
8980      * @param {Function} onComplete A function to be invoked when resizing is complete
8981      */
8982     setElementSize : function(s, newSize, onComplete){
8983         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8984             if(!s.animate){
8985                 s.resizingEl.setWidth(newSize);
8986                 if(onComplete){
8987                     onComplete(s, newSize);
8988                 }
8989             }else{
8990                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8991             }
8992         }else{
8993             
8994             if(!s.animate){
8995                 s.resizingEl.setHeight(newSize);
8996                 if(onComplete){
8997                     onComplete(s, newSize);
8998                 }
8999             }else{
9000                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9001             }
9002         }
9003     }
9004 };
9005
9006 /** 
9007  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9008  * @extends Roo.SplitBar.BasicLayoutAdapter
9009  * Adapter that  moves the splitter element to align with the resized sizing element. 
9010  * Used with an absolute positioned SplitBar.
9011  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9012  * document.body, make sure you assign an id to the body element.
9013  */
9014 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9015     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9016     this.container = Roo.get(container);
9017 };
9018
9019 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9020     init : function(s){
9021         this.basic.init(s);
9022     },
9023     
9024     getElementSize : function(s){
9025         return this.basic.getElementSize(s);
9026     },
9027     
9028     setElementSize : function(s, newSize, onComplete){
9029         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9030     },
9031     
9032     moveSplitter : function(s){
9033         var yes = Roo.SplitBar;
9034         switch(s.placement){
9035             case yes.LEFT:
9036                 s.el.setX(s.resizingEl.getRight());
9037                 break;
9038             case yes.RIGHT:
9039                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9040                 break;
9041             case yes.TOP:
9042                 s.el.setY(s.resizingEl.getBottom());
9043                 break;
9044             case yes.BOTTOM:
9045                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9046                 break;
9047         }
9048     }
9049 };
9050
9051 /**
9052  * Orientation constant - Create a vertical SplitBar
9053  * @static
9054  * @type Number
9055  */
9056 Roo.SplitBar.VERTICAL = 1;
9057
9058 /**
9059  * Orientation constant - Create a horizontal SplitBar
9060  * @static
9061  * @type Number
9062  */
9063 Roo.SplitBar.HORIZONTAL = 2;
9064
9065 /**
9066  * Placement constant - The resizing element is to the left of the splitter element
9067  * @static
9068  * @type Number
9069  */
9070 Roo.SplitBar.LEFT = 1;
9071
9072 /**
9073  * Placement constant - The resizing element is to the right of the splitter element
9074  * @static
9075  * @type Number
9076  */
9077 Roo.SplitBar.RIGHT = 2;
9078
9079 /**
9080  * Placement constant - The resizing element is positioned above the splitter element
9081  * @static
9082  * @type Number
9083  */
9084 Roo.SplitBar.TOP = 3;
9085
9086 /**
9087  * Placement constant - The resizing element is positioned under splitter element
9088  * @static
9089  * @type Number
9090  */
9091 Roo.SplitBar.BOTTOM = 4;
9092 /*
9093  * Based on:
9094  * Ext JS Library 1.1.1
9095  * Copyright(c) 2006-2007, Ext JS, LLC.
9096  *
9097  * Originally Released Under LGPL - original licence link has changed is not relivant.
9098  *
9099  * Fork - LGPL
9100  * <script type="text/javascript">
9101  */
9102
9103 /**
9104  * @class Roo.View
9105  * @extends Roo.util.Observable
9106  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9107  * This class also supports single and multi selection modes. <br>
9108  * Create a data model bound view:
9109  <pre><code>
9110  var store = new Roo.data.Store(...);
9111
9112  var view = new Roo.View({
9113     el : "my-element",
9114     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9115  
9116     singleSelect: true,
9117     selectedClass: "ydataview-selected",
9118     store: store
9119  });
9120
9121  // listen for node click?
9122  view.on("click", function(vw, index, node, e){
9123  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9124  });
9125
9126  // load XML data
9127  dataModel.load("foobar.xml");
9128  </code></pre>
9129  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9130  * <br><br>
9131  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9132  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9133  * 
9134  * Note: old style constructor is still suported (container, template, config)
9135  * 
9136  * @constructor
9137  * Create a new View
9138  * @param {Object} config The config object
9139  * 
9140  */
9141 Roo.View = function(config, depreciated_tpl, depreciated_config){
9142     
9143     if (typeof(depreciated_tpl) == 'undefined') {
9144         // new way.. - universal constructor.
9145         Roo.apply(this, config);
9146         this.el  = Roo.get(this.el);
9147     } else {
9148         // old format..
9149         this.el  = Roo.get(config);
9150         this.tpl = depreciated_tpl;
9151         Roo.apply(this, depreciated_config);
9152     }
9153      
9154     
9155     if(typeof(this.tpl) == "string"){
9156         this.tpl = new Roo.Template(this.tpl);
9157     } else {
9158         // support xtype ctors..
9159         this.tpl = new Roo.factory(this.tpl, Roo);
9160     }
9161     
9162     
9163     this.tpl.compile();
9164    
9165
9166      
9167     /** @private */
9168     this.addEvents({
9169         /**
9170          * @event beforeclick
9171          * Fires before a click is processed. Returns false to cancel the default action.
9172          * @param {Roo.View} this
9173          * @param {Number} index The index of the target node
9174          * @param {HTMLElement} node The target node
9175          * @param {Roo.EventObject} e The raw event object
9176          */
9177             "beforeclick" : true,
9178         /**
9179          * @event click
9180          * Fires when a template node is clicked.
9181          * @param {Roo.View} this
9182          * @param {Number} index The index of the target node
9183          * @param {HTMLElement} node The target node
9184          * @param {Roo.EventObject} e The raw event object
9185          */
9186             "click" : true,
9187         /**
9188          * @event dblclick
9189          * Fires when a template node is double clicked.
9190          * @param {Roo.View} this
9191          * @param {Number} index The index of the target node
9192          * @param {HTMLElement} node The target node
9193          * @param {Roo.EventObject} e The raw event object
9194          */
9195             "dblclick" : true,
9196         /**
9197          * @event contextmenu
9198          * Fires when a template node is right clicked.
9199          * @param {Roo.View} this
9200          * @param {Number} index The index of the target node
9201          * @param {HTMLElement} node The target node
9202          * @param {Roo.EventObject} e The raw event object
9203          */
9204             "contextmenu" : true,
9205         /**
9206          * @event selectionchange
9207          * Fires when the selected nodes change.
9208          * @param {Roo.View} this
9209          * @param {Array} selections Array of the selected nodes
9210          */
9211             "selectionchange" : true,
9212     
9213         /**
9214          * @event beforeselect
9215          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9216          * @param {Roo.View} this
9217          * @param {HTMLElement} node The node to be selected
9218          * @param {Array} selections Array of currently selected nodes
9219          */
9220             "beforeselect" : true,
9221         /**
9222          * @event preparedata
9223          * Fires on every row to render, to allow you to change the data.
9224          * @param {Roo.View} this
9225          * @param {Object} data to be rendered (change this)
9226          */
9227           "preparedata" : true
9228         });
9229
9230     this.el.on({
9231         "click": this.onClick,
9232         "dblclick": this.onDblClick,
9233         "contextmenu": this.onContextMenu,
9234         scope:this
9235     });
9236
9237     this.selections = [];
9238     this.nodes = [];
9239     this.cmp = new Roo.CompositeElementLite([]);
9240     if(this.store){
9241         this.store = Roo.factory(this.store, Roo.data);
9242         this.setStore(this.store, true);
9243     }
9244     Roo.View.superclass.constructor.call(this);
9245 };
9246
9247 Roo.extend(Roo.View, Roo.util.Observable, {
9248     
9249      /**
9250      * @cfg {Roo.data.Store} store Data store to load data from.
9251      */
9252     store : false,
9253     
9254     /**
9255      * @cfg {String|Roo.Element} el The container element.
9256      */
9257     el : '',
9258     
9259     /**
9260      * @cfg {String|Roo.Template} tpl The template used by this View 
9261      */
9262     tpl : false,
9263     
9264     /**
9265      * @cfg {String} selectedClass The css class to add to selected nodes
9266      */
9267     selectedClass : "x-view-selected",
9268      /**
9269      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9270      */
9271     emptyText : "",
9272     /**
9273      * @cfg {Boolean} multiSelect Allow multiple selection
9274      */
9275     multiSelect : false,
9276     /**
9277      * @cfg {Boolean} singleSelect Allow single selection
9278      */
9279     singleSelect:  false,
9280     
9281     /**
9282      * @cfg {Boolean} toggleSelect - selecting 
9283      */
9284     toggleSelect : false,
9285     
9286     /**
9287      * Returns the element this view is bound to.
9288      * @return {Roo.Element}
9289      */
9290     getEl : function(){
9291         return this.el;
9292     },
9293
9294     /**
9295      * Refreshes the view.
9296      */
9297     refresh : function(){
9298         var t = this.tpl;
9299         this.clearSelections();
9300         this.el.update("");
9301         var html = [];
9302         var records = this.store.getRange();
9303         if(records.length < 1){
9304             this.el.update(this.emptyText);
9305             return;
9306         }
9307         for(var i = 0, len = records.length; i < len; i++){
9308             var data = this.prepareData(records[i].data, i, records[i]);
9309             this.fireEvent("preparedata", this, data, i, records[i]);
9310             html[html.length] = t.apply(data);
9311         }
9312         this.el.update(html.join(""));
9313         this.nodes = this.el.dom.childNodes;
9314         this.updateIndexes(0);
9315     },
9316
9317     /**
9318      * Function to override to reformat the data that is sent to
9319      * the template for each node.
9320      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9321      * a JSON object for an UpdateManager bound view).
9322      */
9323     prepareData : function(data){
9324         return data;
9325     },
9326
9327     onUpdate : function(ds, record){
9328         this.clearSelections();
9329         var index = this.store.indexOf(record);
9330         var n = this.nodes[index];
9331         this.tpl.insertBefore(n, this.prepareData(record.data));
9332         n.parentNode.removeChild(n);
9333         this.updateIndexes(index, index);
9334     },
9335
9336     onAdd : function(ds, records, index){
9337         this.clearSelections();
9338         if(this.nodes.length == 0){
9339             this.refresh();
9340             return;
9341         }
9342         var n = this.nodes[index];
9343         for(var i = 0, len = records.length; i < len; i++){
9344             var d = this.prepareData(records[i].data);
9345             if(n){
9346                 this.tpl.insertBefore(n, d);
9347             }else{
9348                 this.tpl.append(this.el, d);
9349             }
9350         }
9351         this.updateIndexes(index);
9352     },
9353
9354     onRemove : function(ds, record, index){
9355         this.clearSelections();
9356         this.el.dom.removeChild(this.nodes[index]);
9357         this.updateIndexes(index);
9358     },
9359
9360     /**
9361      * Refresh an individual node.
9362      * @param {Number} index
9363      */
9364     refreshNode : function(index){
9365         this.onUpdate(this.store, this.store.getAt(index));
9366     },
9367
9368     updateIndexes : function(startIndex, endIndex){
9369         var ns = this.nodes;
9370         startIndex = startIndex || 0;
9371         endIndex = endIndex || ns.length - 1;
9372         for(var i = startIndex; i <= endIndex; i++){
9373             ns[i].nodeIndex = i;
9374         }
9375     },
9376
9377     /**
9378      * Changes the data store this view uses and refresh the view.
9379      * @param {Store} store
9380      */
9381     setStore : function(store, initial){
9382         if(!initial && this.store){
9383             this.store.un("datachanged", this.refresh);
9384             this.store.un("add", this.onAdd);
9385             this.store.un("remove", this.onRemove);
9386             this.store.un("update", this.onUpdate);
9387             this.store.un("clear", this.refresh);
9388         }
9389         if(store){
9390           
9391             store.on("datachanged", this.refresh, this);
9392             store.on("add", this.onAdd, this);
9393             store.on("remove", this.onRemove, this);
9394             store.on("update", this.onUpdate, this);
9395             store.on("clear", this.refresh, this);
9396         }
9397         
9398         if(store){
9399             this.refresh();
9400         }
9401     },
9402
9403     /**
9404      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9405      * @param {HTMLElement} node
9406      * @return {HTMLElement} The template node
9407      */
9408     findItemFromChild : function(node){
9409         var el = this.el.dom;
9410         if(!node || node.parentNode == el){
9411                     return node;
9412             }
9413             var p = node.parentNode;
9414             while(p && p != el){
9415             if(p.parentNode == el){
9416                 return p;
9417             }
9418             p = p.parentNode;
9419         }
9420             return null;
9421     },
9422
9423     /** @ignore */
9424     onClick : function(e){
9425         var item = this.findItemFromChild(e.getTarget());
9426         if(item){
9427             var index = this.indexOf(item);
9428             if(this.onItemClick(item, index, e) !== false){
9429                 this.fireEvent("click", this, index, item, e);
9430             }
9431         }else{
9432             this.clearSelections();
9433         }
9434     },
9435
9436     /** @ignore */
9437     onContextMenu : function(e){
9438         var item = this.findItemFromChild(e.getTarget());
9439         if(item){
9440             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9441         }
9442     },
9443
9444     /** @ignore */
9445     onDblClick : function(e){
9446         var item = this.findItemFromChild(e.getTarget());
9447         if(item){
9448             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9449         }
9450     },
9451
9452     onItemClick : function(item, index, e)
9453     {
9454         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9455             return false;
9456         }
9457         if (this.toggleSelect) {
9458             var m = this.isSelected(item) ? 'unselect' : 'select';
9459             Roo.log(m);
9460             var _t = this;
9461             _t[m](item, true, false);
9462             return true;
9463         }
9464         if(this.multiSelect || this.singleSelect){
9465             if(this.multiSelect && e.shiftKey && this.lastSelection){
9466                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9467             }else{
9468                 this.select(item, this.multiSelect && e.ctrlKey);
9469                 this.lastSelection = item;
9470             }
9471             e.preventDefault();
9472         }
9473         return true;
9474     },
9475
9476     /**
9477      * Get the number of selected nodes.
9478      * @return {Number}
9479      */
9480     getSelectionCount : function(){
9481         return this.selections.length;
9482     },
9483
9484     /**
9485      * Get the currently selected nodes.
9486      * @return {Array} An array of HTMLElements
9487      */
9488     getSelectedNodes : function(){
9489         return this.selections;
9490     },
9491
9492     /**
9493      * Get the indexes of the selected nodes.
9494      * @return {Array}
9495      */
9496     getSelectedIndexes : function(){
9497         var indexes = [], s = this.selections;
9498         for(var i = 0, len = s.length; i < len; i++){
9499             indexes.push(s[i].nodeIndex);
9500         }
9501         return indexes;
9502     },
9503
9504     /**
9505      * Clear all selections
9506      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9507      */
9508     clearSelections : function(suppressEvent){
9509         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9510             this.cmp.elements = this.selections;
9511             this.cmp.removeClass(this.selectedClass);
9512             this.selections = [];
9513             if(!suppressEvent){
9514                 this.fireEvent("selectionchange", this, this.selections);
9515             }
9516         }
9517     },
9518
9519     /**
9520      * Returns true if the passed node is selected
9521      * @param {HTMLElement/Number} node The node or node index
9522      * @return {Boolean}
9523      */
9524     isSelected : function(node){
9525         var s = this.selections;
9526         if(s.length < 1){
9527             return false;
9528         }
9529         node = this.getNode(node);
9530         return s.indexOf(node) !== -1;
9531     },
9532
9533     /**
9534      * Selects nodes.
9535      * @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
9536      * @param {Boolean} keepExisting (optional) true to keep existing selections
9537      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9538      */
9539     select : function(nodeInfo, keepExisting, suppressEvent){
9540         if(nodeInfo instanceof Array){
9541             if(!keepExisting){
9542                 this.clearSelections(true);
9543             }
9544             for(var i = 0, len = nodeInfo.length; i < len; i++){
9545                 this.select(nodeInfo[i], true, true);
9546             }
9547             return;
9548         } 
9549         var node = this.getNode(nodeInfo);
9550         if(!node || this.isSelected(node)){
9551             return; // already selected.
9552         }
9553         if(!keepExisting){
9554             this.clearSelections(true);
9555         }
9556         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9557             Roo.fly(node).addClass(this.selectedClass);
9558             this.selections.push(node);
9559             if(!suppressEvent){
9560                 this.fireEvent("selectionchange", this, this.selections);
9561             }
9562         }
9563         
9564         
9565     },
9566       /**
9567      * Unselects nodes.
9568      * @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
9569      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9570      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9571      */
9572     unselect : function(nodeInfo, keepExisting, suppressEvent)
9573     {
9574         if(nodeInfo instanceof Array){
9575             Roo.each(this.selections, function(s) {
9576                 this.unselect(s, nodeInfo);
9577             }, this);
9578             return;
9579         }
9580         var node = this.getNode(nodeInfo);
9581         if(!node || !this.isSelected(node)){
9582             Roo.log("not selected");
9583             return; // not selected.
9584         }
9585         // fireevent???
9586         var ns = [];
9587         Roo.each(this.selections, function(s) {
9588             if (s == node ) {
9589                 Roo.fly(node).removeClass(this.selectedClass);
9590
9591                 return;
9592             }
9593             ns.push(s);
9594         },this);
9595         
9596         this.selections= ns;
9597         this.fireEvent("selectionchange", this, this.selections);
9598     },
9599
9600     /**
9601      * Gets a template node.
9602      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9603      * @return {HTMLElement} The node or null if it wasn't found
9604      */
9605     getNode : function(nodeInfo){
9606         if(typeof nodeInfo == "string"){
9607             return document.getElementById(nodeInfo);
9608         }else if(typeof nodeInfo == "number"){
9609             return this.nodes[nodeInfo];
9610         }
9611         return nodeInfo;
9612     },
9613
9614     /**
9615      * Gets a range template nodes.
9616      * @param {Number} startIndex
9617      * @param {Number} endIndex
9618      * @return {Array} An array of nodes
9619      */
9620     getNodes : function(start, end){
9621         var ns = this.nodes;
9622         start = start || 0;
9623         end = typeof end == "undefined" ? ns.length - 1 : end;
9624         var nodes = [];
9625         if(start <= end){
9626             for(var i = start; i <= end; i++){
9627                 nodes.push(ns[i]);
9628             }
9629         } else{
9630             for(var i = start; i >= end; i--){
9631                 nodes.push(ns[i]);
9632             }
9633         }
9634         return nodes;
9635     },
9636
9637     /**
9638      * Finds the index of the passed node
9639      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9640      * @return {Number} The index of the node or -1
9641      */
9642     indexOf : function(node){
9643         node = this.getNode(node);
9644         if(typeof node.nodeIndex == "number"){
9645             return node.nodeIndex;
9646         }
9647         var ns = this.nodes;
9648         for(var i = 0, len = ns.length; i < len; i++){
9649             if(ns[i] == node){
9650                 return i;
9651             }
9652         }
9653         return -1;
9654     }
9655 });
9656 /*
9657  * Based on:
9658  * Ext JS Library 1.1.1
9659  * Copyright(c) 2006-2007, Ext JS, LLC.
9660  *
9661  * Originally Released Under LGPL - original licence link has changed is not relivant.
9662  *
9663  * Fork - LGPL
9664  * <script type="text/javascript">
9665  */
9666
9667 /**
9668  * @class Roo.JsonView
9669  * @extends Roo.View
9670  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9671 <pre><code>
9672 var view = new Roo.JsonView({
9673     container: "my-element",
9674     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9675     multiSelect: true, 
9676     jsonRoot: "data" 
9677 });
9678
9679 // listen for node click?
9680 view.on("click", function(vw, index, node, e){
9681     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9682 });
9683
9684 // direct load of JSON data
9685 view.load("foobar.php");
9686
9687 // Example from my blog list
9688 var tpl = new Roo.Template(
9689     '&lt;div class="entry"&gt;' +
9690     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9691     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9692     "&lt;/div&gt;&lt;hr /&gt;"
9693 );
9694
9695 var moreView = new Roo.JsonView({
9696     container :  "entry-list", 
9697     template : tpl,
9698     jsonRoot: "posts"
9699 });
9700 moreView.on("beforerender", this.sortEntries, this);
9701 moreView.load({
9702     url: "/blog/get-posts.php",
9703     params: "allposts=true",
9704     text: "Loading Blog Entries..."
9705 });
9706 </code></pre>
9707
9708 * Note: old code is supported with arguments : (container, template, config)
9709
9710
9711  * @constructor
9712  * Create a new JsonView
9713  * 
9714  * @param {Object} config The config object
9715  * 
9716  */
9717 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9718     
9719     
9720     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9721
9722     var um = this.el.getUpdateManager();
9723     um.setRenderer(this);
9724     um.on("update", this.onLoad, this);
9725     um.on("failure", this.onLoadException, this);
9726
9727     /**
9728      * @event beforerender
9729      * Fires before rendering of the downloaded JSON data.
9730      * @param {Roo.JsonView} this
9731      * @param {Object} data The JSON data loaded
9732      */
9733     /**
9734      * @event load
9735      * Fires when data is loaded.
9736      * @param {Roo.JsonView} this
9737      * @param {Object} data The JSON data loaded
9738      * @param {Object} response The raw Connect response object
9739      */
9740     /**
9741      * @event loadexception
9742      * Fires when loading fails.
9743      * @param {Roo.JsonView} this
9744      * @param {Object} response The raw Connect response object
9745      */
9746     this.addEvents({
9747         'beforerender' : true,
9748         'load' : true,
9749         'loadexception' : true
9750     });
9751 };
9752 Roo.extend(Roo.JsonView, Roo.View, {
9753     /**
9754      * @type {String} The root property in the loaded JSON object that contains the data
9755      */
9756     jsonRoot : "",
9757
9758     /**
9759      * Refreshes the view.
9760      */
9761     refresh : function(){
9762         this.clearSelections();
9763         this.el.update("");
9764         var html = [];
9765         var o = this.jsonData;
9766         if(o && o.length > 0){
9767             for(var i = 0, len = o.length; i < len; i++){
9768                 var data = this.prepareData(o[i], i, o);
9769                 html[html.length] = this.tpl.apply(data);
9770             }
9771         }else{
9772             html.push(this.emptyText);
9773         }
9774         this.el.update(html.join(""));
9775         this.nodes = this.el.dom.childNodes;
9776         this.updateIndexes(0);
9777     },
9778
9779     /**
9780      * 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.
9781      * @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:
9782      <pre><code>
9783      view.load({
9784          url: "your-url.php",
9785          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9786          callback: yourFunction,
9787          scope: yourObject, //(optional scope)
9788          discardUrl: false,
9789          nocache: false,
9790          text: "Loading...",
9791          timeout: 30,
9792          scripts: false
9793      });
9794      </code></pre>
9795      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9796      * 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.
9797      * @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}
9798      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9799      * @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.
9800      */
9801     load : function(){
9802         var um = this.el.getUpdateManager();
9803         um.update.apply(um, arguments);
9804     },
9805
9806     render : function(el, response){
9807         this.clearSelections();
9808         this.el.update("");
9809         var o;
9810         try{
9811             o = Roo.util.JSON.decode(response.responseText);
9812             if(this.jsonRoot){
9813                 
9814                 o = o[this.jsonRoot];
9815             }
9816         } catch(e){
9817         }
9818         /**
9819          * The current JSON data or null
9820          */
9821         this.jsonData = o;
9822         this.beforeRender();
9823         this.refresh();
9824     },
9825
9826 /**
9827  * Get the number of records in the current JSON dataset
9828  * @return {Number}
9829  */
9830     getCount : function(){
9831         return this.jsonData ? this.jsonData.length : 0;
9832     },
9833
9834 /**
9835  * Returns the JSON object for the specified node(s)
9836  * @param {HTMLElement/Array} node The node or an array of nodes
9837  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9838  * you get the JSON object for the node
9839  */
9840     getNodeData : function(node){
9841         if(node instanceof Array){
9842             var data = [];
9843             for(var i = 0, len = node.length; i < len; i++){
9844                 data.push(this.getNodeData(node[i]));
9845             }
9846             return data;
9847         }
9848         return this.jsonData[this.indexOf(node)] || null;
9849     },
9850
9851     beforeRender : function(){
9852         this.snapshot = this.jsonData;
9853         if(this.sortInfo){
9854             this.sort.apply(this, this.sortInfo);
9855         }
9856         this.fireEvent("beforerender", this, this.jsonData);
9857     },
9858
9859     onLoad : function(el, o){
9860         this.fireEvent("load", this, this.jsonData, o);
9861     },
9862
9863     onLoadException : function(el, o){
9864         this.fireEvent("loadexception", this, o);
9865     },
9866
9867 /**
9868  * Filter the data by a specific property.
9869  * @param {String} property A property on your JSON objects
9870  * @param {String/RegExp} value Either string that the property values
9871  * should start with, or a RegExp to test against the property
9872  */
9873     filter : function(property, value){
9874         if(this.jsonData){
9875             var data = [];
9876             var ss = this.snapshot;
9877             if(typeof value == "string"){
9878                 var vlen = value.length;
9879                 if(vlen == 0){
9880                     this.clearFilter();
9881                     return;
9882                 }
9883                 value = value.toLowerCase();
9884                 for(var i = 0, len = ss.length; i < len; i++){
9885                     var o = ss[i];
9886                     if(o[property].substr(0, vlen).toLowerCase() == value){
9887                         data.push(o);
9888                     }
9889                 }
9890             } else if(value.exec){ // regex?
9891                 for(var i = 0, len = ss.length; i < len; i++){
9892                     var o = ss[i];
9893                     if(value.test(o[property])){
9894                         data.push(o);
9895                     }
9896                 }
9897             } else{
9898                 return;
9899             }
9900             this.jsonData = data;
9901             this.refresh();
9902         }
9903     },
9904
9905 /**
9906  * Filter by a function. The passed function will be called with each
9907  * object in the current dataset. If the function returns true the value is kept,
9908  * otherwise it is filtered.
9909  * @param {Function} fn
9910  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9911  */
9912     filterBy : function(fn, scope){
9913         if(this.jsonData){
9914             var data = [];
9915             var ss = this.snapshot;
9916             for(var i = 0, len = ss.length; i < len; i++){
9917                 var o = ss[i];
9918                 if(fn.call(scope || this, o)){
9919                     data.push(o);
9920                 }
9921             }
9922             this.jsonData = data;
9923             this.refresh();
9924         }
9925     },
9926
9927 /**
9928  * Clears the current filter.
9929  */
9930     clearFilter : function(){
9931         if(this.snapshot && this.jsonData != this.snapshot){
9932             this.jsonData = this.snapshot;
9933             this.refresh();
9934         }
9935     },
9936
9937
9938 /**
9939  * Sorts the data for this view and refreshes it.
9940  * @param {String} property A property on your JSON objects to sort on
9941  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9942  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9943  */
9944     sort : function(property, dir, sortType){
9945         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9946         if(this.jsonData){
9947             var p = property;
9948             var dsc = dir && dir.toLowerCase() == "desc";
9949             var f = function(o1, o2){
9950                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9951                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9952                 ;
9953                 if(v1 < v2){
9954                     return dsc ? +1 : -1;
9955                 } else if(v1 > v2){
9956                     return dsc ? -1 : +1;
9957                 } else{
9958                     return 0;
9959                 }
9960             };
9961             this.jsonData.sort(f);
9962             this.refresh();
9963             if(this.jsonData != this.snapshot){
9964                 this.snapshot.sort(f);
9965             }
9966         }
9967     }
9968 });/*
9969  * Based on:
9970  * Ext JS Library 1.1.1
9971  * Copyright(c) 2006-2007, Ext JS, LLC.
9972  *
9973  * Originally Released Under LGPL - original licence link has changed is not relivant.
9974  *
9975  * Fork - LGPL
9976  * <script type="text/javascript">
9977  */
9978  
9979
9980 /**
9981  * @class Roo.ColorPalette
9982  * @extends Roo.Component
9983  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9984  * Here's an example of typical usage:
9985  * <pre><code>
9986 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9987 cp.render('my-div');
9988
9989 cp.on('select', function(palette, selColor){
9990     // do something with selColor
9991 });
9992 </code></pre>
9993  * @constructor
9994  * Create a new ColorPalette
9995  * @param {Object} config The config object
9996  */
9997 Roo.ColorPalette = function(config){
9998     Roo.ColorPalette.superclass.constructor.call(this, config);
9999     this.addEvents({
10000         /**
10001              * @event select
10002              * Fires when a color is selected
10003              * @param {ColorPalette} this
10004              * @param {String} color The 6-digit color hex code (without the # symbol)
10005              */
10006         select: true
10007     });
10008
10009     if(this.handler){
10010         this.on("select", this.handler, this.scope, true);
10011     }
10012 };
10013 Roo.extend(Roo.ColorPalette, Roo.Component, {
10014     /**
10015      * @cfg {String} itemCls
10016      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10017      */
10018     itemCls : "x-color-palette",
10019     /**
10020      * @cfg {String} value
10021      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10022      * the hex codes are case-sensitive.
10023      */
10024     value : null,
10025     clickEvent:'click',
10026     // private
10027     ctype: "Roo.ColorPalette",
10028
10029     /**
10030      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10031      */
10032     allowReselect : false,
10033
10034     /**
10035      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10036      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10037      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10038      * of colors with the width setting until the box is symmetrical.</p>
10039      * <p>You can override individual colors if needed:</p>
10040      * <pre><code>
10041 var cp = new Roo.ColorPalette();
10042 cp.colors[0] = "FF0000";  // change the first box to red
10043 </code></pre>
10044
10045 Or you can provide a custom array of your own for complete control:
10046 <pre><code>
10047 var cp = new Roo.ColorPalette();
10048 cp.colors = ["000000", "993300", "333300"];
10049 </code></pre>
10050      * @type Array
10051      */
10052     colors : [
10053         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10054         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10055         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10056         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10057         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10058     ],
10059
10060     // private
10061     onRender : function(container, position){
10062         var t = new Roo.MasterTemplate(
10063             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10064         );
10065         var c = this.colors;
10066         for(var i = 0, len = c.length; i < len; i++){
10067             t.add([c[i]]);
10068         }
10069         var el = document.createElement("div");
10070         el.className = this.itemCls;
10071         t.overwrite(el);
10072         container.dom.insertBefore(el, position);
10073         this.el = Roo.get(el);
10074         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10075         if(this.clickEvent != 'click'){
10076             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10077         }
10078     },
10079
10080     // private
10081     afterRender : function(){
10082         Roo.ColorPalette.superclass.afterRender.call(this);
10083         if(this.value){
10084             var s = this.value;
10085             this.value = null;
10086             this.select(s);
10087         }
10088     },
10089
10090     // private
10091     handleClick : function(e, t){
10092         e.preventDefault();
10093         if(!this.disabled){
10094             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10095             this.select(c.toUpperCase());
10096         }
10097     },
10098
10099     /**
10100      * Selects the specified color in the palette (fires the select event)
10101      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10102      */
10103     select : function(color){
10104         color = color.replace("#", "");
10105         if(color != this.value || this.allowReselect){
10106             var el = this.el;
10107             if(this.value){
10108                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10109             }
10110             el.child("a.color-"+color).addClass("x-color-palette-sel");
10111             this.value = color;
10112             this.fireEvent("select", this, color);
10113         }
10114     }
10115 });/*
10116  * Based on:
10117  * Ext JS Library 1.1.1
10118  * Copyright(c) 2006-2007, Ext JS, LLC.
10119  *
10120  * Originally Released Under LGPL - original licence link has changed is not relivant.
10121  *
10122  * Fork - LGPL
10123  * <script type="text/javascript">
10124  */
10125  
10126 /**
10127  * @class Roo.DatePicker
10128  * @extends Roo.Component
10129  * Simple date picker class.
10130  * @constructor
10131  * Create a new DatePicker
10132  * @param {Object} config The config object
10133  */
10134 Roo.DatePicker = function(config){
10135     Roo.DatePicker.superclass.constructor.call(this, config);
10136
10137     this.value = config && config.value ?
10138                  config.value.clearTime() : new Date().clearTime();
10139
10140     this.addEvents({
10141         /**
10142              * @event select
10143              * Fires when a date is selected
10144              * @param {DatePicker} this
10145              * @param {Date} date The selected date
10146              */
10147         'select': true,
10148         /**
10149              * @event monthchange
10150              * Fires when the displayed month changes 
10151              * @param {DatePicker} this
10152              * @param {Date} date The selected month
10153              */
10154         'monthchange': true
10155     });
10156
10157     if(this.handler){
10158         this.on("select", this.handler,  this.scope || this);
10159     }
10160     // build the disabledDatesRE
10161     if(!this.disabledDatesRE && this.disabledDates){
10162         var dd = this.disabledDates;
10163         var re = "(?:";
10164         for(var i = 0; i < dd.length; i++){
10165             re += dd[i];
10166             if(i != dd.length-1) re += "|";
10167         }
10168         this.disabledDatesRE = new RegExp(re + ")");
10169     }
10170 };
10171
10172 Roo.extend(Roo.DatePicker, Roo.Component, {
10173     /**
10174      * @cfg {String} todayText
10175      * The text to display on the button that selects the current date (defaults to "Today")
10176      */
10177     todayText : "Today",
10178     /**
10179      * @cfg {String} okText
10180      * The text to display on the ok button
10181      */
10182     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10183     /**
10184      * @cfg {String} cancelText
10185      * The text to display on the cancel button
10186      */
10187     cancelText : "Cancel",
10188     /**
10189      * @cfg {String} todayTip
10190      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10191      */
10192     todayTip : "{0} (Spacebar)",
10193     /**
10194      * @cfg {Date} minDate
10195      * Minimum allowable date (JavaScript date object, defaults to null)
10196      */
10197     minDate : null,
10198     /**
10199      * @cfg {Date} maxDate
10200      * Maximum allowable date (JavaScript date object, defaults to null)
10201      */
10202     maxDate : null,
10203     /**
10204      * @cfg {String} minText
10205      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10206      */
10207     minText : "This date is before the minimum date",
10208     /**
10209      * @cfg {String} maxText
10210      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10211      */
10212     maxText : "This date is after the maximum date",
10213     /**
10214      * @cfg {String} format
10215      * The default date format string which can be overriden for localization support.  The format must be
10216      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10217      */
10218     format : "m/d/y",
10219     /**
10220      * @cfg {Array} disabledDays
10221      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10222      */
10223     disabledDays : null,
10224     /**
10225      * @cfg {String} disabledDaysText
10226      * The tooltip to display when the date falls on a disabled day (defaults to "")
10227      */
10228     disabledDaysText : "",
10229     /**
10230      * @cfg {RegExp} disabledDatesRE
10231      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10232      */
10233     disabledDatesRE : null,
10234     /**
10235      * @cfg {String} disabledDatesText
10236      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10237      */
10238     disabledDatesText : "",
10239     /**
10240      * @cfg {Boolean} constrainToViewport
10241      * True to constrain the date picker to the viewport (defaults to true)
10242      */
10243     constrainToViewport : true,
10244     /**
10245      * @cfg {Array} monthNames
10246      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10247      */
10248     monthNames : Date.monthNames,
10249     /**
10250      * @cfg {Array} dayNames
10251      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10252      */
10253     dayNames : Date.dayNames,
10254     /**
10255      * @cfg {String} nextText
10256      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10257      */
10258     nextText: 'Next Month (Control+Right)',
10259     /**
10260      * @cfg {String} prevText
10261      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10262      */
10263     prevText: 'Previous Month (Control+Left)',
10264     /**
10265      * @cfg {String} monthYearText
10266      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10267      */
10268     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10269     /**
10270      * @cfg {Number} startDay
10271      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10272      */
10273     startDay : 0,
10274     /**
10275      * @cfg {Bool} showClear
10276      * Show a clear button (usefull for date form elements that can be blank.)
10277      */
10278     
10279     showClear: false,
10280     
10281     /**
10282      * Sets the value of the date field
10283      * @param {Date} value The date to set
10284      */
10285     setValue : function(value){
10286         var old = this.value;
10287         this.value = value.clearTime(true);
10288         if(this.el){
10289             this.update(this.value);
10290         }
10291     },
10292
10293     /**
10294      * Gets the current selected value of the date field
10295      * @return {Date} The selected date
10296      */
10297     getValue : function(){
10298         return this.value;
10299     },
10300
10301     // private
10302     focus : function(){
10303         if(this.el){
10304             this.update(this.activeDate);
10305         }
10306     },
10307
10308     // private
10309     onRender : function(container, position){
10310         var m = [
10311              '<table cellspacing="0">',
10312                 '<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>',
10313                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10314         var dn = this.dayNames;
10315         for(var i = 0; i < 7; i++){
10316             var d = this.startDay+i;
10317             if(d > 6){
10318                 d = d-7;
10319             }
10320             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10321         }
10322         m[m.length] = "</tr></thead><tbody><tr>";
10323         for(var i = 0; i < 42; i++) {
10324             if(i % 7 == 0 && i != 0){
10325                 m[m.length] = "</tr><tr>";
10326             }
10327             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10328         }
10329         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10330             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10331
10332         var el = document.createElement("div");
10333         el.className = "x-date-picker";
10334         el.innerHTML = m.join("");
10335
10336         container.dom.insertBefore(el, position);
10337
10338         this.el = Roo.get(el);
10339         this.eventEl = Roo.get(el.firstChild);
10340
10341         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10342             handler: this.showPrevMonth,
10343             scope: this,
10344             preventDefault:true,
10345             stopDefault:true
10346         });
10347
10348         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10349             handler: this.showNextMonth,
10350             scope: this,
10351             preventDefault:true,
10352             stopDefault:true
10353         });
10354
10355         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10356
10357         this.monthPicker = this.el.down('div.x-date-mp');
10358         this.monthPicker.enableDisplayMode('block');
10359         
10360         var kn = new Roo.KeyNav(this.eventEl, {
10361             "left" : function(e){
10362                 e.ctrlKey ?
10363                     this.showPrevMonth() :
10364                     this.update(this.activeDate.add("d", -1));
10365             },
10366
10367             "right" : function(e){
10368                 e.ctrlKey ?
10369                     this.showNextMonth() :
10370                     this.update(this.activeDate.add("d", 1));
10371             },
10372
10373             "up" : function(e){
10374                 e.ctrlKey ?
10375                     this.showNextYear() :
10376                     this.update(this.activeDate.add("d", -7));
10377             },
10378
10379             "down" : function(e){
10380                 e.ctrlKey ?
10381                     this.showPrevYear() :
10382                     this.update(this.activeDate.add("d", 7));
10383             },
10384
10385             "pageUp" : function(e){
10386                 this.showNextMonth();
10387             },
10388
10389             "pageDown" : function(e){
10390                 this.showPrevMonth();
10391             },
10392
10393             "enter" : function(e){
10394                 e.stopPropagation();
10395                 return true;
10396             },
10397
10398             scope : this
10399         });
10400
10401         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10402
10403         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10404
10405         this.el.unselectable();
10406         
10407         this.cells = this.el.select("table.x-date-inner tbody td");
10408         this.textNodes = this.el.query("table.x-date-inner tbody span");
10409
10410         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10411             text: "&#160;",
10412             tooltip: this.monthYearText
10413         });
10414
10415         this.mbtn.on('click', this.showMonthPicker, this);
10416         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10417
10418
10419         var today = (new Date()).dateFormat(this.format);
10420         
10421         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10422         if (this.showClear) {
10423             baseTb.add( new Roo.Toolbar.Fill());
10424         }
10425         baseTb.add({
10426             text: String.format(this.todayText, today),
10427             tooltip: String.format(this.todayTip, today),
10428             handler: this.selectToday,
10429             scope: this
10430         });
10431         
10432         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10433             
10434         //});
10435         if (this.showClear) {
10436             
10437             baseTb.add( new Roo.Toolbar.Fill());
10438             baseTb.add({
10439                 text: '&#160;',
10440                 cls: 'x-btn-icon x-btn-clear',
10441                 handler: function() {
10442                     //this.value = '';
10443                     this.fireEvent("select", this, '');
10444                 },
10445                 scope: this
10446             });
10447         }
10448         
10449         
10450         if(Roo.isIE){
10451             this.el.repaint();
10452         }
10453         this.update(this.value);
10454     },
10455
10456     createMonthPicker : function(){
10457         if(!this.monthPicker.dom.firstChild){
10458             var buf = ['<table border="0" cellspacing="0">'];
10459             for(var i = 0; i < 6; i++){
10460                 buf.push(
10461                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10462                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10463                     i == 0 ?
10464                     '<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>' :
10465                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10466                 );
10467             }
10468             buf.push(
10469                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10470                     this.okText,
10471                     '</button><button type="button" class="x-date-mp-cancel">',
10472                     this.cancelText,
10473                     '</button></td></tr>',
10474                 '</table>'
10475             );
10476             this.monthPicker.update(buf.join(''));
10477             this.monthPicker.on('click', this.onMonthClick, this);
10478             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10479
10480             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10481             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10482
10483             this.mpMonths.each(function(m, a, i){
10484                 i += 1;
10485                 if((i%2) == 0){
10486                     m.dom.xmonth = 5 + Math.round(i * .5);
10487                 }else{
10488                     m.dom.xmonth = Math.round((i-1) * .5);
10489                 }
10490             });
10491         }
10492     },
10493
10494     showMonthPicker : function(){
10495         this.createMonthPicker();
10496         var size = this.el.getSize();
10497         this.monthPicker.setSize(size);
10498         this.monthPicker.child('table').setSize(size);
10499
10500         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10501         this.updateMPMonth(this.mpSelMonth);
10502         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10503         this.updateMPYear(this.mpSelYear);
10504
10505         this.monthPicker.slideIn('t', {duration:.2});
10506     },
10507
10508     updateMPYear : function(y){
10509         this.mpyear = y;
10510         var ys = this.mpYears.elements;
10511         for(var i = 1; i <= 10; i++){
10512             var td = ys[i-1], y2;
10513             if((i%2) == 0){
10514                 y2 = y + Math.round(i * .5);
10515                 td.firstChild.innerHTML = y2;
10516                 td.xyear = y2;
10517             }else{
10518                 y2 = y - (5-Math.round(i * .5));
10519                 td.firstChild.innerHTML = y2;
10520                 td.xyear = y2;
10521             }
10522             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10523         }
10524     },
10525
10526     updateMPMonth : function(sm){
10527         this.mpMonths.each(function(m, a, i){
10528             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10529         });
10530     },
10531
10532     selectMPMonth: function(m){
10533         
10534     },
10535
10536     onMonthClick : function(e, t){
10537         e.stopEvent();
10538         var el = new Roo.Element(t), pn;
10539         if(el.is('button.x-date-mp-cancel')){
10540             this.hideMonthPicker();
10541         }
10542         else if(el.is('button.x-date-mp-ok')){
10543             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10544             this.hideMonthPicker();
10545         }
10546         else if(pn = el.up('td.x-date-mp-month', 2)){
10547             this.mpMonths.removeClass('x-date-mp-sel');
10548             pn.addClass('x-date-mp-sel');
10549             this.mpSelMonth = pn.dom.xmonth;
10550         }
10551         else if(pn = el.up('td.x-date-mp-year', 2)){
10552             this.mpYears.removeClass('x-date-mp-sel');
10553             pn.addClass('x-date-mp-sel');
10554             this.mpSelYear = pn.dom.xyear;
10555         }
10556         else if(el.is('a.x-date-mp-prev')){
10557             this.updateMPYear(this.mpyear-10);
10558         }
10559         else if(el.is('a.x-date-mp-next')){
10560             this.updateMPYear(this.mpyear+10);
10561         }
10562     },
10563
10564     onMonthDblClick : function(e, t){
10565         e.stopEvent();
10566         var el = new Roo.Element(t), pn;
10567         if(pn = el.up('td.x-date-mp-month', 2)){
10568             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10569             this.hideMonthPicker();
10570         }
10571         else if(pn = el.up('td.x-date-mp-year', 2)){
10572             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10573             this.hideMonthPicker();
10574         }
10575     },
10576
10577     hideMonthPicker : function(disableAnim){
10578         if(this.monthPicker){
10579             if(disableAnim === true){
10580                 this.monthPicker.hide();
10581             }else{
10582                 this.monthPicker.slideOut('t', {duration:.2});
10583             }
10584         }
10585     },
10586
10587     // private
10588     showPrevMonth : function(e){
10589         this.update(this.activeDate.add("mo", -1));
10590     },
10591
10592     // private
10593     showNextMonth : function(e){
10594         this.update(this.activeDate.add("mo", 1));
10595     },
10596
10597     // private
10598     showPrevYear : function(){
10599         this.update(this.activeDate.add("y", -1));
10600     },
10601
10602     // private
10603     showNextYear : function(){
10604         this.update(this.activeDate.add("y", 1));
10605     },
10606
10607     // private
10608     handleMouseWheel : function(e){
10609         var delta = e.getWheelDelta();
10610         if(delta > 0){
10611             this.showPrevMonth();
10612             e.stopEvent();
10613         } else if(delta < 0){
10614             this.showNextMonth();
10615             e.stopEvent();
10616         }
10617     },
10618
10619     // private
10620     handleDateClick : function(e, t){
10621         e.stopEvent();
10622         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10623             this.setValue(new Date(t.dateValue));
10624             this.fireEvent("select", this, this.value);
10625         }
10626     },
10627
10628     // private
10629     selectToday : function(){
10630         this.setValue(new Date().clearTime());
10631         this.fireEvent("select", this, this.value);
10632     },
10633
10634     // private
10635     update : function(date)
10636     {
10637         var vd = this.activeDate;
10638         this.activeDate = date;
10639         if(vd && this.el){
10640             var t = date.getTime();
10641             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10642                 this.cells.removeClass("x-date-selected");
10643                 this.cells.each(function(c){
10644                    if(c.dom.firstChild.dateValue == t){
10645                        c.addClass("x-date-selected");
10646                        setTimeout(function(){
10647                             try{c.dom.firstChild.focus();}catch(e){}
10648                        }, 50);
10649                        return false;
10650                    }
10651                 });
10652                 return;
10653             }
10654         }
10655         
10656         var days = date.getDaysInMonth();
10657         var firstOfMonth = date.getFirstDateOfMonth();
10658         var startingPos = firstOfMonth.getDay()-this.startDay;
10659
10660         if(startingPos <= this.startDay){
10661             startingPos += 7;
10662         }
10663
10664         var pm = date.add("mo", -1);
10665         var prevStart = pm.getDaysInMonth()-startingPos;
10666
10667         var cells = this.cells.elements;
10668         var textEls = this.textNodes;
10669         days += startingPos;
10670
10671         // convert everything to numbers so it's fast
10672         var day = 86400000;
10673         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10674         var today = new Date().clearTime().getTime();
10675         var sel = date.clearTime().getTime();
10676         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10677         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10678         var ddMatch = this.disabledDatesRE;
10679         var ddText = this.disabledDatesText;
10680         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10681         var ddaysText = this.disabledDaysText;
10682         var format = this.format;
10683
10684         var setCellClass = function(cal, cell){
10685             cell.title = "";
10686             var t = d.getTime();
10687             cell.firstChild.dateValue = t;
10688             if(t == today){
10689                 cell.className += " x-date-today";
10690                 cell.title = cal.todayText;
10691             }
10692             if(t == sel){
10693                 cell.className += " x-date-selected";
10694                 setTimeout(function(){
10695                     try{cell.firstChild.focus();}catch(e){}
10696                 }, 50);
10697             }
10698             // disabling
10699             if(t < min) {
10700                 cell.className = " x-date-disabled";
10701                 cell.title = cal.minText;
10702                 return;
10703             }
10704             if(t > max) {
10705                 cell.className = " x-date-disabled";
10706                 cell.title = cal.maxText;
10707                 return;
10708             }
10709             if(ddays){
10710                 if(ddays.indexOf(d.getDay()) != -1){
10711                     cell.title = ddaysText;
10712                     cell.className = " x-date-disabled";
10713                 }
10714             }
10715             if(ddMatch && format){
10716                 var fvalue = d.dateFormat(format);
10717                 if(ddMatch.test(fvalue)){
10718                     cell.title = ddText.replace("%0", fvalue);
10719                     cell.className = " x-date-disabled";
10720                 }
10721             }
10722         };
10723
10724         var i = 0;
10725         for(; i < startingPos; i++) {
10726             textEls[i].innerHTML = (++prevStart);
10727             d.setDate(d.getDate()+1);
10728             cells[i].className = "x-date-prevday";
10729             setCellClass(this, cells[i]);
10730         }
10731         for(; i < days; i++){
10732             intDay = i - startingPos + 1;
10733             textEls[i].innerHTML = (intDay);
10734             d.setDate(d.getDate()+1);
10735             cells[i].className = "x-date-active";
10736             setCellClass(this, cells[i]);
10737         }
10738         var extraDays = 0;
10739         for(; i < 42; i++) {
10740              textEls[i].innerHTML = (++extraDays);
10741              d.setDate(d.getDate()+1);
10742              cells[i].className = "x-date-nextday";
10743              setCellClass(this, cells[i]);
10744         }
10745
10746         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10747         this.fireEvent('monthchange', this, date);
10748         
10749         if(!this.internalRender){
10750             var main = this.el.dom.firstChild;
10751             var w = main.offsetWidth;
10752             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10753             Roo.fly(main).setWidth(w);
10754             this.internalRender = true;
10755             // opera does not respect the auto grow header center column
10756             // then, after it gets a width opera refuses to recalculate
10757             // without a second pass
10758             if(Roo.isOpera && !this.secondPass){
10759                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10760                 this.secondPass = true;
10761                 this.update.defer(10, this, [date]);
10762             }
10763         }
10764         
10765         
10766     }
10767 });        /*
10768  * Based on:
10769  * Ext JS Library 1.1.1
10770  * Copyright(c) 2006-2007, Ext JS, LLC.
10771  *
10772  * Originally Released Under LGPL - original licence link has changed is not relivant.
10773  *
10774  * Fork - LGPL
10775  * <script type="text/javascript">
10776  */
10777 /**
10778  * @class Roo.TabPanel
10779  * @extends Roo.util.Observable
10780  * A lightweight tab container.
10781  * <br><br>
10782  * Usage:
10783  * <pre><code>
10784 // basic tabs 1, built from existing content
10785 var tabs = new Roo.TabPanel("tabs1");
10786 tabs.addTab("script", "View Script");
10787 tabs.addTab("markup", "View Markup");
10788 tabs.activate("script");
10789
10790 // more advanced tabs, built from javascript
10791 var jtabs = new Roo.TabPanel("jtabs");
10792 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10793
10794 // set up the UpdateManager
10795 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10796 var updater = tab2.getUpdateManager();
10797 updater.setDefaultUrl("ajax1.htm");
10798 tab2.on('activate', updater.refresh, updater, true);
10799
10800 // Use setUrl for Ajax loading
10801 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10802 tab3.setUrl("ajax2.htm", null, true);
10803
10804 // Disabled tab
10805 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10806 tab4.disable();
10807
10808 jtabs.activate("jtabs-1");
10809  * </code></pre>
10810  * @constructor
10811  * Create a new TabPanel.
10812  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10813  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10814  */
10815 Roo.TabPanel = function(container, config){
10816     /**
10817     * The container element for this TabPanel.
10818     * @type Roo.Element
10819     */
10820     this.el = Roo.get(container, true);
10821     if(config){
10822         if(typeof config == "boolean"){
10823             this.tabPosition = config ? "bottom" : "top";
10824         }else{
10825             Roo.apply(this, config);
10826         }
10827     }
10828     if(this.tabPosition == "bottom"){
10829         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10830         this.el.addClass("x-tabs-bottom");
10831     }
10832     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10833     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10834     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10835     if(Roo.isIE){
10836         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10837     }
10838     if(this.tabPosition != "bottom"){
10839         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10840          * @type Roo.Element
10841          */
10842         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10843         this.el.addClass("x-tabs-top");
10844     }
10845     this.items = [];
10846
10847     this.bodyEl.setStyle("position", "relative");
10848
10849     this.active = null;
10850     this.activateDelegate = this.activate.createDelegate(this);
10851
10852     this.addEvents({
10853         /**
10854          * @event tabchange
10855          * Fires when the active tab changes
10856          * @param {Roo.TabPanel} this
10857          * @param {Roo.TabPanelItem} activePanel The new active tab
10858          */
10859         "tabchange": true,
10860         /**
10861          * @event beforetabchange
10862          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10863          * @param {Roo.TabPanel} this
10864          * @param {Object} e Set cancel to true on this object to cancel the tab change
10865          * @param {Roo.TabPanelItem} tab The tab being changed to
10866          */
10867         "beforetabchange" : true
10868     });
10869
10870     Roo.EventManager.onWindowResize(this.onResize, this);
10871     this.cpad = this.el.getPadding("lr");
10872     this.hiddenCount = 0;
10873
10874
10875     // toolbar on the tabbar support...
10876     if (this.toolbar) {
10877         var tcfg = this.toolbar;
10878         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10879         this.toolbar = new Roo.Toolbar(tcfg);
10880         if (Roo.isSafari) {
10881             var tbl = tcfg.container.child('table', true);
10882             tbl.setAttribute('width', '100%');
10883         }
10884         
10885     }
10886    
10887
10888
10889     Roo.TabPanel.superclass.constructor.call(this);
10890 };
10891
10892 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10893     /*
10894      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10895      */
10896     tabPosition : "top",
10897     /*
10898      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10899      */
10900     currentTabWidth : 0,
10901     /*
10902      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10903      */
10904     minTabWidth : 40,
10905     /*
10906      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10907      */
10908     maxTabWidth : 250,
10909     /*
10910      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10911      */
10912     preferredTabWidth : 175,
10913     /*
10914      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10915      */
10916     resizeTabs : false,
10917     /*
10918      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10919      */
10920     monitorResize : true,
10921     /*
10922      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10923      */
10924     toolbar : false,
10925
10926     /**
10927      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10928      * @param {String} id The id of the div to use <b>or create</b>
10929      * @param {String} text The text for the tab
10930      * @param {String} content (optional) Content to put in the TabPanelItem body
10931      * @param {Boolean} closable (optional) True to create a close icon on the tab
10932      * @return {Roo.TabPanelItem} The created TabPanelItem
10933      */
10934     addTab : function(id, text, content, closable){
10935         var item = new Roo.TabPanelItem(this, id, text, closable);
10936         this.addTabItem(item);
10937         if(content){
10938             item.setContent(content);
10939         }
10940         return item;
10941     },
10942
10943     /**
10944      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10945      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10946      * @return {Roo.TabPanelItem}
10947      */
10948     getTab : function(id){
10949         return this.items[id];
10950     },
10951
10952     /**
10953      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10954      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10955      */
10956     hideTab : function(id){
10957         var t = this.items[id];
10958         if(!t.isHidden()){
10959            t.setHidden(true);
10960            this.hiddenCount++;
10961            this.autoSizeTabs();
10962         }
10963     },
10964
10965     /**
10966      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10967      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10968      */
10969     unhideTab : function(id){
10970         var t = this.items[id];
10971         if(t.isHidden()){
10972            t.setHidden(false);
10973            this.hiddenCount--;
10974            this.autoSizeTabs();
10975         }
10976     },
10977
10978     /**
10979      * Adds an existing {@link Roo.TabPanelItem}.
10980      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10981      */
10982     addTabItem : function(item){
10983         this.items[item.id] = item;
10984         this.items.push(item);
10985         if(this.resizeTabs){
10986            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10987            this.autoSizeTabs();
10988         }else{
10989             item.autoSize();
10990         }
10991     },
10992
10993     /**
10994      * Removes a {@link Roo.TabPanelItem}.
10995      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10996      */
10997     removeTab : function(id){
10998         var items = this.items;
10999         var tab = items[id];
11000         if(!tab) { return; }
11001         var index = items.indexOf(tab);
11002         if(this.active == tab && items.length > 1){
11003             var newTab = this.getNextAvailable(index);
11004             if(newTab) {
11005                 newTab.activate();
11006             }
11007         }
11008         this.stripEl.dom.removeChild(tab.pnode.dom);
11009         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11010             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11011         }
11012         items.splice(index, 1);
11013         delete this.items[tab.id];
11014         tab.fireEvent("close", tab);
11015         tab.purgeListeners();
11016         this.autoSizeTabs();
11017     },
11018
11019     getNextAvailable : function(start){
11020         var items = this.items;
11021         var index = start;
11022         // look for a next tab that will slide over to
11023         // replace the one being removed
11024         while(index < items.length){
11025             var item = items[++index];
11026             if(item && !item.isHidden()){
11027                 return item;
11028             }
11029         }
11030         // if one isn't found select the previous tab (on the left)
11031         index = start;
11032         while(index >= 0){
11033             var item = items[--index];
11034             if(item && !item.isHidden()){
11035                 return item;
11036             }
11037         }
11038         return null;
11039     },
11040
11041     /**
11042      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11043      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11044      */
11045     disableTab : function(id){
11046         var tab = this.items[id];
11047         if(tab && this.active != tab){
11048             tab.disable();
11049         }
11050     },
11051
11052     /**
11053      * Enables a {@link Roo.TabPanelItem} that is disabled.
11054      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11055      */
11056     enableTab : function(id){
11057         var tab = this.items[id];
11058         tab.enable();
11059     },
11060
11061     /**
11062      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11063      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11064      * @return {Roo.TabPanelItem} The TabPanelItem.
11065      */
11066     activate : function(id){
11067         var tab = this.items[id];
11068         if(!tab){
11069             return null;
11070         }
11071         if(tab == this.active || tab.disabled){
11072             return tab;
11073         }
11074         var e = {};
11075         this.fireEvent("beforetabchange", this, e, tab);
11076         if(e.cancel !== true && !tab.disabled){
11077             if(this.active){
11078                 this.active.hide();
11079             }
11080             this.active = this.items[id];
11081             this.active.show();
11082             this.fireEvent("tabchange", this, this.active);
11083         }
11084         return tab;
11085     },
11086
11087     /**
11088      * Gets the active {@link Roo.TabPanelItem}.
11089      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11090      */
11091     getActiveTab : function(){
11092         return this.active;
11093     },
11094
11095     /**
11096      * Updates the tab body element to fit the height of the container element
11097      * for overflow scrolling
11098      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11099      */
11100     syncHeight : function(targetHeight){
11101         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11102         var bm = this.bodyEl.getMargins();
11103         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11104         this.bodyEl.setHeight(newHeight);
11105         return newHeight;
11106     },
11107
11108     onResize : function(){
11109         if(this.monitorResize){
11110             this.autoSizeTabs();
11111         }
11112     },
11113
11114     /**
11115      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11116      */
11117     beginUpdate : function(){
11118         this.updating = true;
11119     },
11120
11121     /**
11122      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11123      */
11124     endUpdate : function(){
11125         this.updating = false;
11126         this.autoSizeTabs();
11127     },
11128
11129     /**
11130      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11131      */
11132     autoSizeTabs : function(){
11133         var count = this.items.length;
11134         var vcount = count - this.hiddenCount;
11135         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11136         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11137         var availWidth = Math.floor(w / vcount);
11138         var b = this.stripBody;
11139         if(b.getWidth() > w){
11140             var tabs = this.items;
11141             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11142             if(availWidth < this.minTabWidth){
11143                 /*if(!this.sleft){    // incomplete scrolling code
11144                     this.createScrollButtons();
11145                 }
11146                 this.showScroll();
11147                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11148             }
11149         }else{
11150             if(this.currentTabWidth < this.preferredTabWidth){
11151                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11152             }
11153         }
11154     },
11155
11156     /**
11157      * Returns the number of tabs in this TabPanel.
11158      * @return {Number}
11159      */
11160      getCount : function(){
11161          return this.items.length;
11162      },
11163
11164     /**
11165      * Resizes all the tabs to the passed width
11166      * @param {Number} The new width
11167      */
11168     setTabWidth : function(width){
11169         this.currentTabWidth = width;
11170         for(var i = 0, len = this.items.length; i < len; i++) {
11171                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11172         }
11173     },
11174
11175     /**
11176      * Destroys this TabPanel
11177      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11178      */
11179     destroy : function(removeEl){
11180         Roo.EventManager.removeResizeListener(this.onResize, this);
11181         for(var i = 0, len = this.items.length; i < len; i++){
11182             this.items[i].purgeListeners();
11183         }
11184         if(removeEl === true){
11185             this.el.update("");
11186             this.el.remove();
11187         }
11188     }
11189 });
11190
11191 /**
11192  * @class Roo.TabPanelItem
11193  * @extends Roo.util.Observable
11194  * Represents an individual item (tab plus body) in a TabPanel.
11195  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11196  * @param {String} id The id of this TabPanelItem
11197  * @param {String} text The text for the tab of this TabPanelItem
11198  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11199  */
11200 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11201     /**
11202      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11203      * @type Roo.TabPanel
11204      */
11205     this.tabPanel = tabPanel;
11206     /**
11207      * The id for this TabPanelItem
11208      * @type String
11209      */
11210     this.id = id;
11211     /** @private */
11212     this.disabled = false;
11213     /** @private */
11214     this.text = text;
11215     /** @private */
11216     this.loaded = false;
11217     this.closable = closable;
11218
11219     /**
11220      * The body element for this TabPanelItem.
11221      * @type Roo.Element
11222      */
11223     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11224     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11225     this.bodyEl.setStyle("display", "block");
11226     this.bodyEl.setStyle("zoom", "1");
11227     this.hideAction();
11228
11229     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11230     /** @private */
11231     this.el = Roo.get(els.el, true);
11232     this.inner = Roo.get(els.inner, true);
11233     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11234     this.pnode = Roo.get(els.el.parentNode, true);
11235     this.el.on("mousedown", this.onTabMouseDown, this);
11236     this.el.on("click", this.onTabClick, this);
11237     /** @private */
11238     if(closable){
11239         var c = Roo.get(els.close, true);
11240         c.dom.title = this.closeText;
11241         c.addClassOnOver("close-over");
11242         c.on("click", this.closeClick, this);
11243      }
11244
11245     this.addEvents({
11246          /**
11247          * @event activate
11248          * Fires when this tab becomes the active tab.
11249          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11250          * @param {Roo.TabPanelItem} this
11251          */
11252         "activate": true,
11253         /**
11254          * @event beforeclose
11255          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11256          * @param {Roo.TabPanelItem} this
11257          * @param {Object} e Set cancel to true on this object to cancel the close.
11258          */
11259         "beforeclose": true,
11260         /**
11261          * @event close
11262          * Fires when this tab is closed.
11263          * @param {Roo.TabPanelItem} this
11264          */
11265          "close": true,
11266         /**
11267          * @event deactivate
11268          * Fires when this tab is no longer the active tab.
11269          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11270          * @param {Roo.TabPanelItem} this
11271          */
11272          "deactivate" : true
11273     });
11274     this.hidden = false;
11275
11276     Roo.TabPanelItem.superclass.constructor.call(this);
11277 };
11278
11279 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11280     purgeListeners : function(){
11281        Roo.util.Observable.prototype.purgeListeners.call(this);
11282        this.el.removeAllListeners();
11283     },
11284     /**
11285      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11286      */
11287     show : function(){
11288         this.pnode.addClass("on");
11289         this.showAction();
11290         if(Roo.isOpera){
11291             this.tabPanel.stripWrap.repaint();
11292         }
11293         this.fireEvent("activate", this.tabPanel, this);
11294     },
11295
11296     /**
11297      * Returns true if this tab is the active tab.
11298      * @return {Boolean}
11299      */
11300     isActive : function(){
11301         return this.tabPanel.getActiveTab() == this;
11302     },
11303
11304     /**
11305      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11306      */
11307     hide : function(){
11308         this.pnode.removeClass("on");
11309         this.hideAction();
11310         this.fireEvent("deactivate", this.tabPanel, this);
11311     },
11312
11313     hideAction : function(){
11314         this.bodyEl.hide();
11315         this.bodyEl.setStyle("position", "absolute");
11316         this.bodyEl.setLeft("-20000px");
11317         this.bodyEl.setTop("-20000px");
11318     },
11319
11320     showAction : function(){
11321         this.bodyEl.setStyle("position", "relative");
11322         this.bodyEl.setTop("");
11323         this.bodyEl.setLeft("");
11324         this.bodyEl.show();
11325     },
11326
11327     /**
11328      * Set the tooltip for the tab.
11329      * @param {String} tooltip The tab's tooltip
11330      */
11331     setTooltip : function(text){
11332         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11333             this.textEl.dom.qtip = text;
11334             this.textEl.dom.removeAttribute('title');
11335         }else{
11336             this.textEl.dom.title = text;
11337         }
11338     },
11339
11340     onTabClick : function(e){
11341         e.preventDefault();
11342         this.tabPanel.activate(this.id);
11343     },
11344
11345     onTabMouseDown : function(e){
11346         e.preventDefault();
11347         this.tabPanel.activate(this.id);
11348     },
11349
11350     getWidth : function(){
11351         return this.inner.getWidth();
11352     },
11353
11354     setWidth : function(width){
11355         var iwidth = width - this.pnode.getPadding("lr");
11356         this.inner.setWidth(iwidth);
11357         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11358         this.pnode.setWidth(width);
11359     },
11360
11361     /**
11362      * Show or hide the tab
11363      * @param {Boolean} hidden True to hide or false to show.
11364      */
11365     setHidden : function(hidden){
11366         this.hidden = hidden;
11367         this.pnode.setStyle("display", hidden ? "none" : "");
11368     },
11369
11370     /**
11371      * Returns true if this tab is "hidden"
11372      * @return {Boolean}
11373      */
11374     isHidden : function(){
11375         return this.hidden;
11376     },
11377
11378     /**
11379      * Returns the text for this tab
11380      * @return {String}
11381      */
11382     getText : function(){
11383         return this.text;
11384     },
11385
11386     autoSize : function(){
11387         //this.el.beginMeasure();
11388         this.textEl.setWidth(1);
11389         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11390         //this.el.endMeasure();
11391     },
11392
11393     /**
11394      * Sets the text for the tab (Note: this also sets the tooltip text)
11395      * @param {String} text The tab's text and tooltip
11396      */
11397     setText : function(text){
11398         this.text = text;
11399         this.textEl.update(text);
11400         this.setTooltip(text);
11401         if(!this.tabPanel.resizeTabs){
11402             this.autoSize();
11403         }
11404     },
11405     /**
11406      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11407      */
11408     activate : function(){
11409         this.tabPanel.activate(this.id);
11410     },
11411
11412     /**
11413      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11414      */
11415     disable : function(){
11416         if(this.tabPanel.active != this){
11417             this.disabled = true;
11418             this.pnode.addClass("disabled");
11419         }
11420     },
11421
11422     /**
11423      * Enables this TabPanelItem if it was previously disabled.
11424      */
11425     enable : function(){
11426         this.disabled = false;
11427         this.pnode.removeClass("disabled");
11428     },
11429
11430     /**
11431      * Sets the content for this TabPanelItem.
11432      * @param {String} content The content
11433      * @param {Boolean} loadScripts true to look for and load scripts
11434      */
11435     setContent : function(content, loadScripts){
11436         this.bodyEl.update(content, loadScripts);
11437     },
11438
11439     /**
11440      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11441      * @return {Roo.UpdateManager} The UpdateManager
11442      */
11443     getUpdateManager : function(){
11444         return this.bodyEl.getUpdateManager();
11445     },
11446
11447     /**
11448      * Set a URL to be used to load the content for this TabPanelItem.
11449      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11450      * @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)
11451      * @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)
11452      * @return {Roo.UpdateManager} The UpdateManager
11453      */
11454     setUrl : function(url, params, loadOnce){
11455         if(this.refreshDelegate){
11456             this.un('activate', this.refreshDelegate);
11457         }
11458         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11459         this.on("activate", this.refreshDelegate);
11460         return this.bodyEl.getUpdateManager();
11461     },
11462
11463     /** @private */
11464     _handleRefresh : function(url, params, loadOnce){
11465         if(!loadOnce || !this.loaded){
11466             var updater = this.bodyEl.getUpdateManager();
11467             updater.update(url, params, this._setLoaded.createDelegate(this));
11468         }
11469     },
11470
11471     /**
11472      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11473      *   Will fail silently if the setUrl method has not been called.
11474      *   This does not activate the panel, just updates its content.
11475      */
11476     refresh : function(){
11477         if(this.refreshDelegate){
11478            this.loaded = false;
11479            this.refreshDelegate();
11480         }
11481     },
11482
11483     /** @private */
11484     _setLoaded : function(){
11485         this.loaded = true;
11486     },
11487
11488     /** @private */
11489     closeClick : function(e){
11490         var o = {};
11491         e.stopEvent();
11492         this.fireEvent("beforeclose", this, o);
11493         if(o.cancel !== true){
11494             this.tabPanel.removeTab(this.id);
11495         }
11496     },
11497     /**
11498      * The text displayed in the tooltip for the close icon.
11499      * @type String
11500      */
11501     closeText : "Close this tab"
11502 });
11503
11504 /** @private */
11505 Roo.TabPanel.prototype.createStrip = function(container){
11506     var strip = document.createElement("div");
11507     strip.className = "x-tabs-wrap";
11508     container.appendChild(strip);
11509     return strip;
11510 };
11511 /** @private */
11512 Roo.TabPanel.prototype.createStripList = function(strip){
11513     // div wrapper for retard IE
11514     // returns the "tr" element.
11515     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11516         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11517         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11518     return strip.firstChild.firstChild.firstChild.firstChild;
11519 };
11520 /** @private */
11521 Roo.TabPanel.prototype.createBody = function(container){
11522     var body = document.createElement("div");
11523     Roo.id(body, "tab-body");
11524     Roo.fly(body).addClass("x-tabs-body");
11525     container.appendChild(body);
11526     return body;
11527 };
11528 /** @private */
11529 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11530     var body = Roo.getDom(id);
11531     if(!body){
11532         body = document.createElement("div");
11533         body.id = id;
11534     }
11535     Roo.fly(body).addClass("x-tabs-item-body");
11536     bodyEl.insertBefore(body, bodyEl.firstChild);
11537     return body;
11538 };
11539 /** @private */
11540 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11541     var td = document.createElement("td");
11542     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11543     //stripEl.appendChild(td);
11544     if(closable){
11545         td.className = "x-tabs-closable";
11546         if(!this.closeTpl){
11547             this.closeTpl = new Roo.Template(
11548                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11549                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11550                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11551             );
11552         }
11553         var el = this.closeTpl.overwrite(td, {"text": text});
11554         var close = el.getElementsByTagName("div")[0];
11555         var inner = el.getElementsByTagName("em")[0];
11556         return {"el": el, "close": close, "inner": inner};
11557     } else {
11558         if(!this.tabTpl){
11559             this.tabTpl = new Roo.Template(
11560                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11561                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11562             );
11563         }
11564         var el = this.tabTpl.overwrite(td, {"text": text});
11565         var inner = el.getElementsByTagName("em")[0];
11566         return {"el": el, "inner": inner};
11567     }
11568 };/*
11569  * Based on:
11570  * Ext JS Library 1.1.1
11571  * Copyright(c) 2006-2007, Ext JS, LLC.
11572  *
11573  * Originally Released Under LGPL - original licence link has changed is not relivant.
11574  *
11575  * Fork - LGPL
11576  * <script type="text/javascript">
11577  */
11578
11579 /**
11580  * @class Roo.Button
11581  * @extends Roo.util.Observable
11582  * Simple Button class
11583  * @cfg {String} text The button text
11584  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11585  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11586  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11587  * @cfg {Object} scope The scope of the handler
11588  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11589  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11590  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11591  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11592  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11593  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11594    applies if enableToggle = true)
11595  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11596  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11597   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11598  * @constructor
11599  * Create a new button
11600  * @param {Object} config The config object
11601  */
11602 Roo.Button = function(renderTo, config)
11603 {
11604     if (!config) {
11605         config = renderTo;
11606         renderTo = config.renderTo || false;
11607     }
11608     
11609     Roo.apply(this, config);
11610     this.addEvents({
11611         /**
11612              * @event click
11613              * Fires when this button is clicked
11614              * @param {Button} this
11615              * @param {EventObject} e The click event
11616              */
11617             "click" : true,
11618         /**
11619              * @event toggle
11620              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11621              * @param {Button} this
11622              * @param {Boolean} pressed
11623              */
11624             "toggle" : true,
11625         /**
11626              * @event mouseover
11627              * Fires when the mouse hovers over the button
11628              * @param {Button} this
11629              * @param {Event} e The event object
11630              */
11631         'mouseover' : true,
11632         /**
11633              * @event mouseout
11634              * Fires when the mouse exits the button
11635              * @param {Button} this
11636              * @param {Event} e The event object
11637              */
11638         'mouseout': true,
11639          /**
11640              * @event render
11641              * Fires when the button is rendered
11642              * @param {Button} this
11643              */
11644         'render': true
11645     });
11646     if(this.menu){
11647         this.menu = Roo.menu.MenuMgr.get(this.menu);
11648     }
11649     // register listeners first!!  - so render can be captured..
11650     Roo.util.Observable.call(this);
11651     if(renderTo){
11652         this.render(renderTo);
11653     }
11654     
11655   
11656 };
11657
11658 Roo.extend(Roo.Button, Roo.util.Observable, {
11659     /**
11660      * 
11661      */
11662     
11663     /**
11664      * Read-only. True if this button is hidden
11665      * @type Boolean
11666      */
11667     hidden : false,
11668     /**
11669      * Read-only. True if this button is disabled
11670      * @type Boolean
11671      */
11672     disabled : false,
11673     /**
11674      * Read-only. True if this button is pressed (only if enableToggle = true)
11675      * @type Boolean
11676      */
11677     pressed : false,
11678
11679     /**
11680      * @cfg {Number} tabIndex 
11681      * The DOM tabIndex for this button (defaults to undefined)
11682      */
11683     tabIndex : undefined,
11684
11685     /**
11686      * @cfg {Boolean} enableToggle
11687      * True to enable pressed/not pressed toggling (defaults to false)
11688      */
11689     enableToggle: false,
11690     /**
11691      * @cfg {Mixed} menu
11692      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11693      */
11694     menu : undefined,
11695     /**
11696      * @cfg {String} menuAlign
11697      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11698      */
11699     menuAlign : "tl-bl?",
11700
11701     /**
11702      * @cfg {String} iconCls
11703      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11704      */
11705     iconCls : undefined,
11706     /**
11707      * @cfg {String} type
11708      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11709      */
11710     type : 'button',
11711
11712     // private
11713     menuClassTarget: 'tr',
11714
11715     /**
11716      * @cfg {String} clickEvent
11717      * The type of event to map to the button's event handler (defaults to 'click')
11718      */
11719     clickEvent : 'click',
11720
11721     /**
11722      * @cfg {Boolean} handleMouseEvents
11723      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11724      */
11725     handleMouseEvents : true,
11726
11727     /**
11728      * @cfg {String} tooltipType
11729      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11730      */
11731     tooltipType : 'qtip',
11732
11733     /**
11734      * @cfg {String} cls
11735      * A CSS class to apply to the button's main element.
11736      */
11737     
11738     /**
11739      * @cfg {Roo.Template} template (Optional)
11740      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11741      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11742      * require code modifications if required elements (e.g. a button) aren't present.
11743      */
11744
11745     // private
11746     render : function(renderTo){
11747         var btn;
11748         if(this.hideParent){
11749             this.parentEl = Roo.get(renderTo);
11750         }
11751         if(!this.dhconfig){
11752             if(!this.template){
11753                 if(!Roo.Button.buttonTemplate){
11754                     // hideous table template
11755                     Roo.Button.buttonTemplate = new Roo.Template(
11756                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11757                         '<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>',
11758                         "</tr></tbody></table>");
11759                 }
11760                 this.template = Roo.Button.buttonTemplate;
11761             }
11762             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11763             var btnEl = btn.child("button:first");
11764             btnEl.on('focus', this.onFocus, this);
11765             btnEl.on('blur', this.onBlur, this);
11766             if(this.cls){
11767                 btn.addClass(this.cls);
11768             }
11769             if(this.icon){
11770                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11771             }
11772             if(this.iconCls){
11773                 btnEl.addClass(this.iconCls);
11774                 if(!this.cls){
11775                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11776                 }
11777             }
11778             if(this.tabIndex !== undefined){
11779                 btnEl.dom.tabIndex = this.tabIndex;
11780             }
11781             if(this.tooltip){
11782                 if(typeof this.tooltip == 'object'){
11783                     Roo.QuickTips.tips(Roo.apply({
11784                           target: btnEl.id
11785                     }, this.tooltip));
11786                 } else {
11787                     btnEl.dom[this.tooltipType] = this.tooltip;
11788                 }
11789             }
11790         }else{
11791             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11792         }
11793         this.el = btn;
11794         if(this.id){
11795             this.el.dom.id = this.el.id = this.id;
11796         }
11797         if(this.menu){
11798             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11799             this.menu.on("show", this.onMenuShow, this);
11800             this.menu.on("hide", this.onMenuHide, this);
11801         }
11802         btn.addClass("x-btn");
11803         if(Roo.isIE && !Roo.isIE7){
11804             this.autoWidth.defer(1, this);
11805         }else{
11806             this.autoWidth();
11807         }
11808         if(this.handleMouseEvents){
11809             btn.on("mouseover", this.onMouseOver, this);
11810             btn.on("mouseout", this.onMouseOut, this);
11811             btn.on("mousedown", this.onMouseDown, this);
11812         }
11813         btn.on(this.clickEvent, this.onClick, this);
11814         //btn.on("mouseup", this.onMouseUp, this);
11815         if(this.hidden){
11816             this.hide();
11817         }
11818         if(this.disabled){
11819             this.disable();
11820         }
11821         Roo.ButtonToggleMgr.register(this);
11822         if(this.pressed){
11823             this.el.addClass("x-btn-pressed");
11824         }
11825         if(this.repeat){
11826             var repeater = new Roo.util.ClickRepeater(btn,
11827                 typeof this.repeat == "object" ? this.repeat : {}
11828             );
11829             repeater.on("click", this.onClick,  this);
11830         }
11831         
11832         this.fireEvent('render', this);
11833         
11834     },
11835     /**
11836      * Returns the button's underlying element
11837      * @return {Roo.Element} The element
11838      */
11839     getEl : function(){
11840         return this.el;  
11841     },
11842     
11843     /**
11844      * Destroys this Button and removes any listeners.
11845      */
11846     destroy : function(){
11847         Roo.ButtonToggleMgr.unregister(this);
11848         this.el.removeAllListeners();
11849         this.purgeListeners();
11850         this.el.remove();
11851     },
11852
11853     // private
11854     autoWidth : function(){
11855         if(this.el){
11856             this.el.setWidth("auto");
11857             if(Roo.isIE7 && Roo.isStrict){
11858                 var ib = this.el.child('button');
11859                 if(ib && ib.getWidth() > 20){
11860                     ib.clip();
11861                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11862                 }
11863             }
11864             if(this.minWidth){
11865                 if(this.hidden){
11866                     this.el.beginMeasure();
11867                 }
11868                 if(this.el.getWidth() < this.minWidth){
11869                     this.el.setWidth(this.minWidth);
11870                 }
11871                 if(this.hidden){
11872                     this.el.endMeasure();
11873                 }
11874             }
11875         }
11876     },
11877
11878     /**
11879      * Assigns this button's click handler
11880      * @param {Function} handler The function to call when the button is clicked
11881      * @param {Object} scope (optional) Scope for the function passed in
11882      */
11883     setHandler : function(handler, scope){
11884         this.handler = handler;
11885         this.scope = scope;  
11886     },
11887     
11888     /**
11889      * Sets this button's text
11890      * @param {String} text The button text
11891      */
11892     setText : function(text){
11893         this.text = text;
11894         if(this.el){
11895             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11896         }
11897         this.autoWidth();
11898     },
11899     
11900     /**
11901      * Gets the text for this button
11902      * @return {String} The button text
11903      */
11904     getText : function(){
11905         return this.text;  
11906     },
11907     
11908     /**
11909      * Show this button
11910      */
11911     show: function(){
11912         this.hidden = false;
11913         if(this.el){
11914             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11915         }
11916     },
11917     
11918     /**
11919      * Hide this button
11920      */
11921     hide: function(){
11922         this.hidden = true;
11923         if(this.el){
11924             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11925         }
11926     },
11927     
11928     /**
11929      * Convenience function for boolean show/hide
11930      * @param {Boolean} visible True to show, false to hide
11931      */
11932     setVisible: function(visible){
11933         if(visible) {
11934             this.show();
11935         }else{
11936             this.hide();
11937         }
11938     },
11939     
11940     /**
11941      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11942      * @param {Boolean} state (optional) Force a particular state
11943      */
11944     toggle : function(state){
11945         state = state === undefined ? !this.pressed : state;
11946         if(state != this.pressed){
11947             if(state){
11948                 this.el.addClass("x-btn-pressed");
11949                 this.pressed = true;
11950                 this.fireEvent("toggle", this, true);
11951             }else{
11952                 this.el.removeClass("x-btn-pressed");
11953                 this.pressed = false;
11954                 this.fireEvent("toggle", this, false);
11955             }
11956             if(this.toggleHandler){
11957                 this.toggleHandler.call(this.scope || this, this, state);
11958             }
11959         }
11960     },
11961     
11962     /**
11963      * Focus the button
11964      */
11965     focus : function(){
11966         this.el.child('button:first').focus();
11967     },
11968     
11969     /**
11970      * Disable this button
11971      */
11972     disable : function(){
11973         if(this.el){
11974             this.el.addClass("x-btn-disabled");
11975         }
11976         this.disabled = true;
11977     },
11978     
11979     /**
11980      * Enable this button
11981      */
11982     enable : function(){
11983         if(this.el){
11984             this.el.removeClass("x-btn-disabled");
11985         }
11986         this.disabled = false;
11987     },
11988
11989     /**
11990      * Convenience function for boolean enable/disable
11991      * @param {Boolean} enabled True to enable, false to disable
11992      */
11993     setDisabled : function(v){
11994         this[v !== true ? "enable" : "disable"]();
11995     },
11996
11997     // private
11998     onClick : function(e){
11999         if(e){
12000             e.preventDefault();
12001         }
12002         if(e.button != 0){
12003             return;
12004         }
12005         if(!this.disabled){
12006             if(this.enableToggle){
12007                 this.toggle();
12008             }
12009             if(this.menu && !this.menu.isVisible()){
12010                 this.menu.show(this.el, this.menuAlign);
12011             }
12012             this.fireEvent("click", this, e);
12013             if(this.handler){
12014                 this.el.removeClass("x-btn-over");
12015                 this.handler.call(this.scope || this, this, e);
12016             }
12017         }
12018     },
12019     // private
12020     onMouseOver : function(e){
12021         if(!this.disabled){
12022             this.el.addClass("x-btn-over");
12023             this.fireEvent('mouseover', this, e);
12024         }
12025     },
12026     // private
12027     onMouseOut : function(e){
12028         if(!e.within(this.el,  true)){
12029             this.el.removeClass("x-btn-over");
12030             this.fireEvent('mouseout', this, e);
12031         }
12032     },
12033     // private
12034     onFocus : function(e){
12035         if(!this.disabled){
12036             this.el.addClass("x-btn-focus");
12037         }
12038     },
12039     // private
12040     onBlur : function(e){
12041         this.el.removeClass("x-btn-focus");
12042     },
12043     // private
12044     onMouseDown : function(e){
12045         if(!this.disabled && e.button == 0){
12046             this.el.addClass("x-btn-click");
12047             Roo.get(document).on('mouseup', this.onMouseUp, this);
12048         }
12049     },
12050     // private
12051     onMouseUp : function(e){
12052         if(e.button == 0){
12053             this.el.removeClass("x-btn-click");
12054             Roo.get(document).un('mouseup', this.onMouseUp, this);
12055         }
12056     },
12057     // private
12058     onMenuShow : function(e){
12059         this.el.addClass("x-btn-menu-active");
12060     },
12061     // private
12062     onMenuHide : function(e){
12063         this.el.removeClass("x-btn-menu-active");
12064     }   
12065 });
12066
12067 // Private utility class used by Button
12068 Roo.ButtonToggleMgr = function(){
12069    var groups = {};
12070    
12071    function toggleGroup(btn, state){
12072        if(state){
12073            var g = groups[btn.toggleGroup];
12074            for(var i = 0, l = g.length; i < l; i++){
12075                if(g[i] != btn){
12076                    g[i].toggle(false);
12077                }
12078            }
12079        }
12080    }
12081    
12082    return {
12083        register : function(btn){
12084            if(!btn.toggleGroup){
12085                return;
12086            }
12087            var g = groups[btn.toggleGroup];
12088            if(!g){
12089                g = groups[btn.toggleGroup] = [];
12090            }
12091            g.push(btn);
12092            btn.on("toggle", toggleGroup);
12093        },
12094        
12095        unregister : function(btn){
12096            if(!btn.toggleGroup){
12097                return;
12098            }
12099            var g = groups[btn.toggleGroup];
12100            if(g){
12101                g.remove(btn);
12102                btn.un("toggle", toggleGroup);
12103            }
12104        }
12105    };
12106 }();/*
12107  * Based on:
12108  * Ext JS Library 1.1.1
12109  * Copyright(c) 2006-2007, Ext JS, LLC.
12110  *
12111  * Originally Released Under LGPL - original licence link has changed is not relivant.
12112  *
12113  * Fork - LGPL
12114  * <script type="text/javascript">
12115  */
12116  
12117 /**
12118  * @class Roo.SplitButton
12119  * @extends Roo.Button
12120  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12121  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12122  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12123  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12124  * @cfg {String} arrowTooltip The title attribute of the arrow
12125  * @constructor
12126  * Create a new menu button
12127  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12128  * @param {Object} config The config object
12129  */
12130 Roo.SplitButton = function(renderTo, config){
12131     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12132     /**
12133      * @event arrowclick
12134      * Fires when this button's arrow is clicked
12135      * @param {SplitButton} this
12136      * @param {EventObject} e The click event
12137      */
12138     this.addEvents({"arrowclick":true});
12139 };
12140
12141 Roo.extend(Roo.SplitButton, Roo.Button, {
12142     render : function(renderTo){
12143         // this is one sweet looking template!
12144         var tpl = new Roo.Template(
12145             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12146             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12147             '<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>',
12148             "</tbody></table></td><td>",
12149             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12150             '<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>',
12151             "</tbody></table></td></tr></table>"
12152         );
12153         var btn = tpl.append(renderTo, [this.text, this.type], true);
12154         var btnEl = btn.child("button");
12155         if(this.cls){
12156             btn.addClass(this.cls);
12157         }
12158         if(this.icon){
12159             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12160         }
12161         if(this.iconCls){
12162             btnEl.addClass(this.iconCls);
12163             if(!this.cls){
12164                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12165             }
12166         }
12167         this.el = btn;
12168         if(this.handleMouseEvents){
12169             btn.on("mouseover", this.onMouseOver, this);
12170             btn.on("mouseout", this.onMouseOut, this);
12171             btn.on("mousedown", this.onMouseDown, this);
12172             btn.on("mouseup", this.onMouseUp, this);
12173         }
12174         btn.on(this.clickEvent, this.onClick, this);
12175         if(this.tooltip){
12176             if(typeof this.tooltip == 'object'){
12177                 Roo.QuickTips.tips(Roo.apply({
12178                       target: btnEl.id
12179                 }, this.tooltip));
12180             } else {
12181                 btnEl.dom[this.tooltipType] = this.tooltip;
12182             }
12183         }
12184         if(this.arrowTooltip){
12185             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12186         }
12187         if(this.hidden){
12188             this.hide();
12189         }
12190         if(this.disabled){
12191             this.disable();
12192         }
12193         if(this.pressed){
12194             this.el.addClass("x-btn-pressed");
12195         }
12196         if(Roo.isIE && !Roo.isIE7){
12197             this.autoWidth.defer(1, this);
12198         }else{
12199             this.autoWidth();
12200         }
12201         if(this.menu){
12202             this.menu.on("show", this.onMenuShow, this);
12203             this.menu.on("hide", this.onMenuHide, this);
12204         }
12205         this.fireEvent('render', this);
12206     },
12207
12208     // private
12209     autoWidth : function(){
12210         if(this.el){
12211             var tbl = this.el.child("table:first");
12212             var tbl2 = this.el.child("table:last");
12213             this.el.setWidth("auto");
12214             tbl.setWidth("auto");
12215             if(Roo.isIE7 && Roo.isStrict){
12216                 var ib = this.el.child('button:first');
12217                 if(ib && ib.getWidth() > 20){
12218                     ib.clip();
12219                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12220                 }
12221             }
12222             if(this.minWidth){
12223                 if(this.hidden){
12224                     this.el.beginMeasure();
12225                 }
12226                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12227                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12228                 }
12229                 if(this.hidden){
12230                     this.el.endMeasure();
12231                 }
12232             }
12233             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12234         } 
12235     },
12236     /**
12237      * Sets this button's click handler
12238      * @param {Function} handler The function to call when the button is clicked
12239      * @param {Object} scope (optional) Scope for the function passed above
12240      */
12241     setHandler : function(handler, scope){
12242         this.handler = handler;
12243         this.scope = scope;  
12244     },
12245     
12246     /**
12247      * Sets this button's arrow click handler
12248      * @param {Function} handler The function to call when the arrow is clicked
12249      * @param {Object} scope (optional) Scope for the function passed above
12250      */
12251     setArrowHandler : function(handler, scope){
12252         this.arrowHandler = handler;
12253         this.scope = scope;  
12254     },
12255     
12256     /**
12257      * Focus the button
12258      */
12259     focus : function(){
12260         if(this.el){
12261             this.el.child("button:first").focus();
12262         }
12263     },
12264
12265     // private
12266     onClick : function(e){
12267         e.preventDefault();
12268         if(!this.disabled){
12269             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12270                 if(this.menu && !this.menu.isVisible()){
12271                     this.menu.show(this.el, this.menuAlign);
12272                 }
12273                 this.fireEvent("arrowclick", this, e);
12274                 if(this.arrowHandler){
12275                     this.arrowHandler.call(this.scope || this, this, e);
12276                 }
12277             }else{
12278                 this.fireEvent("click", this, e);
12279                 if(this.handler){
12280                     this.handler.call(this.scope || this, this, e);
12281                 }
12282             }
12283         }
12284     },
12285     // private
12286     onMouseDown : function(e){
12287         if(!this.disabled){
12288             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12289         }
12290     },
12291     // private
12292     onMouseUp : function(e){
12293         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12294     }   
12295 });
12296
12297
12298 // backwards compat
12299 Roo.MenuButton = Roo.SplitButton;/*
12300  * Based on:
12301  * Ext JS Library 1.1.1
12302  * Copyright(c) 2006-2007, Ext JS, LLC.
12303  *
12304  * Originally Released Under LGPL - original licence link has changed is not relivant.
12305  *
12306  * Fork - LGPL
12307  * <script type="text/javascript">
12308  */
12309
12310 /**
12311  * @class Roo.Toolbar
12312  * Basic Toolbar class.
12313  * @constructor
12314  * Creates a new Toolbar
12315  * @param {Object} container The config object
12316  */ 
12317 Roo.Toolbar = function(container, buttons, config)
12318 {
12319     /// old consturctor format still supported..
12320     if(container instanceof Array){ // omit the container for later rendering
12321         buttons = container;
12322         config = buttons;
12323         container = null;
12324     }
12325     if (typeof(container) == 'object' && container.xtype) {
12326         config = container;
12327         container = config.container;
12328         buttons = config.buttons || []; // not really - use items!!
12329     }
12330     var xitems = [];
12331     if (config && config.items) {
12332         xitems = config.items;
12333         delete config.items;
12334     }
12335     Roo.apply(this, config);
12336     this.buttons = buttons;
12337     
12338     if(container){
12339         this.render(container);
12340     }
12341     this.xitems = xitems;
12342     Roo.each(xitems, function(b) {
12343         this.add(b);
12344     }, this);
12345     
12346 };
12347
12348 Roo.Toolbar.prototype = {
12349     /**
12350      * @cfg {Array} items
12351      * array of button configs or elements to add (will be converted to a MixedCollection)
12352      */
12353     
12354     /**
12355      * @cfg {String/HTMLElement/Element} container
12356      * The id or element that will contain the toolbar
12357      */
12358     // private
12359     render : function(ct){
12360         this.el = Roo.get(ct);
12361         if(this.cls){
12362             this.el.addClass(this.cls);
12363         }
12364         // using a table allows for vertical alignment
12365         // 100% width is needed by Safari...
12366         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12367         this.tr = this.el.child("tr", true);
12368         var autoId = 0;
12369         this.items = new Roo.util.MixedCollection(false, function(o){
12370             return o.id || ("item" + (++autoId));
12371         });
12372         if(this.buttons){
12373             this.add.apply(this, this.buttons);
12374             delete this.buttons;
12375         }
12376     },
12377
12378     /**
12379      * Adds element(s) to the toolbar -- this function takes a variable number of 
12380      * arguments of mixed type and adds them to the toolbar.
12381      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12382      * <ul>
12383      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12384      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12385      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12386      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12387      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12388      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12389      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12390      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12391      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12392      * </ul>
12393      * @param {Mixed} arg2
12394      * @param {Mixed} etc.
12395      */
12396     add : function(){
12397         var a = arguments, l = a.length;
12398         for(var i = 0; i < l; i++){
12399             this._add(a[i]);
12400         }
12401     },
12402     // private..
12403     _add : function(el) {
12404         
12405         if (el.xtype) {
12406             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12407         }
12408         
12409         if (el.applyTo){ // some kind of form field
12410             return this.addField(el);
12411         } 
12412         if (el.render){ // some kind of Toolbar.Item
12413             return this.addItem(el);
12414         }
12415         if (typeof el == "string"){ // string
12416             if(el == "separator" || el == "-"){
12417                 return this.addSeparator();
12418             }
12419             if (el == " "){
12420                 return this.addSpacer();
12421             }
12422             if(el == "->"){
12423                 return this.addFill();
12424             }
12425             return this.addText(el);
12426             
12427         }
12428         if(el.tagName){ // element
12429             return this.addElement(el);
12430         }
12431         if(typeof el == "object"){ // must be button config?
12432             return this.addButton(el);
12433         }
12434         // and now what?!?!
12435         return false;
12436         
12437     },
12438     
12439     /**
12440      * Add an Xtype element
12441      * @param {Object} xtype Xtype Object
12442      * @return {Object} created Object
12443      */
12444     addxtype : function(e){
12445         return this.add(e);  
12446     },
12447     
12448     /**
12449      * Returns the Element for this toolbar.
12450      * @return {Roo.Element}
12451      */
12452     getEl : function(){
12453         return this.el;  
12454     },
12455     
12456     /**
12457      * Adds a separator
12458      * @return {Roo.Toolbar.Item} The separator item
12459      */
12460     addSeparator : function(){
12461         return this.addItem(new Roo.Toolbar.Separator());
12462     },
12463
12464     /**
12465      * Adds a spacer element
12466      * @return {Roo.Toolbar.Spacer} The spacer item
12467      */
12468     addSpacer : function(){
12469         return this.addItem(new Roo.Toolbar.Spacer());
12470     },
12471
12472     /**
12473      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12474      * @return {Roo.Toolbar.Fill} The fill item
12475      */
12476     addFill : function(){
12477         return this.addItem(new Roo.Toolbar.Fill());
12478     },
12479
12480     /**
12481      * Adds any standard HTML element to the toolbar
12482      * @param {String/HTMLElement/Element} el The element or id of the element to add
12483      * @return {Roo.Toolbar.Item} The element's item
12484      */
12485     addElement : function(el){
12486         return this.addItem(new Roo.Toolbar.Item(el));
12487     },
12488     /**
12489      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12490      * @type Roo.util.MixedCollection  
12491      */
12492     items : false,
12493      
12494     /**
12495      * Adds any Toolbar.Item or subclass
12496      * @param {Roo.Toolbar.Item} item
12497      * @return {Roo.Toolbar.Item} The item
12498      */
12499     addItem : function(item){
12500         var td = this.nextBlock();
12501         item.render(td);
12502         this.items.add(item);
12503         return item;
12504     },
12505     
12506     /**
12507      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12508      * @param {Object/Array} config A button config or array of configs
12509      * @return {Roo.Toolbar.Button/Array}
12510      */
12511     addButton : function(config){
12512         if(config instanceof Array){
12513             var buttons = [];
12514             for(var i = 0, len = config.length; i < len; i++) {
12515                 buttons.push(this.addButton(config[i]));
12516             }
12517             return buttons;
12518         }
12519         var b = config;
12520         if(!(config instanceof Roo.Toolbar.Button)){
12521             b = config.split ?
12522                 new Roo.Toolbar.SplitButton(config) :
12523                 new Roo.Toolbar.Button(config);
12524         }
12525         var td = this.nextBlock();
12526         b.render(td);
12527         this.items.add(b);
12528         return b;
12529     },
12530     
12531     /**
12532      * Adds text to the toolbar
12533      * @param {String} text The text to add
12534      * @return {Roo.Toolbar.Item} The element's item
12535      */
12536     addText : function(text){
12537         return this.addItem(new Roo.Toolbar.TextItem(text));
12538     },
12539     
12540     /**
12541      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12542      * @param {Number} index The index where the item is to be inserted
12543      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12544      * @return {Roo.Toolbar.Button/Item}
12545      */
12546     insertButton : function(index, item){
12547         if(item instanceof Array){
12548             var buttons = [];
12549             for(var i = 0, len = item.length; i < len; i++) {
12550                buttons.push(this.insertButton(index + i, item[i]));
12551             }
12552             return buttons;
12553         }
12554         if (!(item instanceof Roo.Toolbar.Button)){
12555            item = new Roo.Toolbar.Button(item);
12556         }
12557         var td = document.createElement("td");
12558         this.tr.insertBefore(td, this.tr.childNodes[index]);
12559         item.render(td);
12560         this.items.insert(index, item);
12561         return item;
12562     },
12563     
12564     /**
12565      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12566      * @param {Object} config
12567      * @return {Roo.Toolbar.Item} The element's item
12568      */
12569     addDom : function(config, returnEl){
12570         var td = this.nextBlock();
12571         Roo.DomHelper.overwrite(td, config);
12572         var ti = new Roo.Toolbar.Item(td.firstChild);
12573         ti.render(td);
12574         this.items.add(ti);
12575         return ti;
12576     },
12577
12578     /**
12579      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12580      * @type Roo.util.MixedCollection  
12581      */
12582     fields : false,
12583     
12584     /**
12585      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12586      * Note: the field should not have been rendered yet. For a field that has already been
12587      * rendered, use {@link #addElement}.
12588      * @param {Roo.form.Field} field
12589      * @return {Roo.ToolbarItem}
12590      */
12591      
12592       
12593     addField : function(field) {
12594         if (!this.fields) {
12595             var autoId = 0;
12596             this.fields = new Roo.util.MixedCollection(false, function(o){
12597                 return o.id || ("item" + (++autoId));
12598             });
12599
12600         }
12601         
12602         var td = this.nextBlock();
12603         field.render(td);
12604         var ti = new Roo.Toolbar.Item(td.firstChild);
12605         ti.render(td);
12606         this.items.add(ti);
12607         this.fields.add(field);
12608         return ti;
12609     },
12610     /**
12611      * Hide the toolbar
12612      * @method hide
12613      */
12614      
12615       
12616     hide : function()
12617     {
12618         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12619         this.el.child('div').hide();
12620     },
12621     /**
12622      * Show the toolbar
12623      * @method show
12624      */
12625     show : function()
12626     {
12627         this.el.child('div').show();
12628     },
12629       
12630     // private
12631     nextBlock : function(){
12632         var td = document.createElement("td");
12633         this.tr.appendChild(td);
12634         return td;
12635     },
12636
12637     // private
12638     destroy : function(){
12639         if(this.items){ // rendered?
12640             Roo.destroy.apply(Roo, this.items.items);
12641         }
12642         if(this.fields){ // rendered?
12643             Roo.destroy.apply(Roo, this.fields.items);
12644         }
12645         Roo.Element.uncache(this.el, this.tr);
12646     }
12647 };
12648
12649 /**
12650  * @class Roo.Toolbar.Item
12651  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12652  * @constructor
12653  * Creates a new Item
12654  * @param {HTMLElement} el 
12655  */
12656 Roo.Toolbar.Item = function(el){
12657     this.el = Roo.getDom(el);
12658     this.id = Roo.id(this.el);
12659     this.hidden = false;
12660 };
12661
12662 Roo.Toolbar.Item.prototype = {
12663     
12664     /**
12665      * Get this item's HTML Element
12666      * @return {HTMLElement}
12667      */
12668     getEl : function(){
12669        return this.el;  
12670     },
12671
12672     // private
12673     render : function(td){
12674         this.td = td;
12675         td.appendChild(this.el);
12676     },
12677     
12678     /**
12679      * Removes and destroys this item.
12680      */
12681     destroy : function(){
12682         this.td.parentNode.removeChild(this.td);
12683     },
12684     
12685     /**
12686      * Shows this item.
12687      */
12688     show: function(){
12689         this.hidden = false;
12690         this.td.style.display = "";
12691     },
12692     
12693     /**
12694      * Hides this item.
12695      */
12696     hide: function(){
12697         this.hidden = true;
12698         this.td.style.display = "none";
12699     },
12700     
12701     /**
12702      * Convenience function for boolean show/hide.
12703      * @param {Boolean} visible true to show/false to hide
12704      */
12705     setVisible: function(visible){
12706         if(visible) {
12707             this.show();
12708         }else{
12709             this.hide();
12710         }
12711     },
12712     
12713     /**
12714      * Try to focus this item.
12715      */
12716     focus : function(){
12717         Roo.fly(this.el).focus();
12718     },
12719     
12720     /**
12721      * Disables this item.
12722      */
12723     disable : function(){
12724         Roo.fly(this.td).addClass("x-item-disabled");
12725         this.disabled = true;
12726         this.el.disabled = true;
12727     },
12728     
12729     /**
12730      * Enables this item.
12731      */
12732     enable : function(){
12733         Roo.fly(this.td).removeClass("x-item-disabled");
12734         this.disabled = false;
12735         this.el.disabled = false;
12736     }
12737 };
12738
12739
12740 /**
12741  * @class Roo.Toolbar.Separator
12742  * @extends Roo.Toolbar.Item
12743  * A simple toolbar separator class
12744  * @constructor
12745  * Creates a new Separator
12746  */
12747 Roo.Toolbar.Separator = function(){
12748     var s = document.createElement("span");
12749     s.className = "ytb-sep";
12750     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12751 };
12752 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12753     enable:Roo.emptyFn,
12754     disable:Roo.emptyFn,
12755     focus:Roo.emptyFn
12756 });
12757
12758 /**
12759  * @class Roo.Toolbar.Spacer
12760  * @extends Roo.Toolbar.Item
12761  * A simple element that adds extra horizontal space to a toolbar.
12762  * @constructor
12763  * Creates a new Spacer
12764  */
12765 Roo.Toolbar.Spacer = function(){
12766     var s = document.createElement("div");
12767     s.className = "ytb-spacer";
12768     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12769 };
12770 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12771     enable:Roo.emptyFn,
12772     disable:Roo.emptyFn,
12773     focus:Roo.emptyFn
12774 });
12775
12776 /**
12777  * @class Roo.Toolbar.Fill
12778  * @extends Roo.Toolbar.Spacer
12779  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12780  * @constructor
12781  * Creates a new Spacer
12782  */
12783 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12784     // private
12785     render : function(td){
12786         td.style.width = '100%';
12787         Roo.Toolbar.Fill.superclass.render.call(this, td);
12788     }
12789 });
12790
12791 /**
12792  * @class Roo.Toolbar.TextItem
12793  * @extends Roo.Toolbar.Item
12794  * A simple class that renders text directly into a toolbar.
12795  * @constructor
12796  * Creates a new TextItem
12797  * @param {String} text
12798  */
12799 Roo.Toolbar.TextItem = function(text){
12800     if (typeof(text) == 'object') {
12801         text = text.text;
12802     }
12803     var s = document.createElement("span");
12804     s.className = "ytb-text";
12805     s.innerHTML = text;
12806     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12807 };
12808 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12809     enable:Roo.emptyFn,
12810     disable:Roo.emptyFn,
12811     focus:Roo.emptyFn
12812 });
12813
12814 /**
12815  * @class Roo.Toolbar.Button
12816  * @extends Roo.Button
12817  * A button that renders into a toolbar.
12818  * @constructor
12819  * Creates a new Button
12820  * @param {Object} config A standard {@link Roo.Button} config object
12821  */
12822 Roo.Toolbar.Button = function(config){
12823     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12824 };
12825 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12826     render : function(td){
12827         this.td = td;
12828         Roo.Toolbar.Button.superclass.render.call(this, td);
12829     },
12830     
12831     /**
12832      * Removes and destroys this button
12833      */
12834     destroy : function(){
12835         Roo.Toolbar.Button.superclass.destroy.call(this);
12836         this.td.parentNode.removeChild(this.td);
12837     },
12838     
12839     /**
12840      * Shows this button
12841      */
12842     show: function(){
12843         this.hidden = false;
12844         this.td.style.display = "";
12845     },
12846     
12847     /**
12848      * Hides this button
12849      */
12850     hide: function(){
12851         this.hidden = true;
12852         this.td.style.display = "none";
12853     },
12854
12855     /**
12856      * Disables this item
12857      */
12858     disable : function(){
12859         Roo.fly(this.td).addClass("x-item-disabled");
12860         this.disabled = true;
12861     },
12862
12863     /**
12864      * Enables this item
12865      */
12866     enable : function(){
12867         Roo.fly(this.td).removeClass("x-item-disabled");
12868         this.disabled = false;
12869     }
12870 });
12871 // backwards compat
12872 Roo.ToolbarButton = Roo.Toolbar.Button;
12873
12874 /**
12875  * @class Roo.Toolbar.SplitButton
12876  * @extends Roo.SplitButton
12877  * A menu button that renders into a toolbar.
12878  * @constructor
12879  * Creates a new SplitButton
12880  * @param {Object} config A standard {@link Roo.SplitButton} config object
12881  */
12882 Roo.Toolbar.SplitButton = function(config){
12883     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12884 };
12885 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12886     render : function(td){
12887         this.td = td;
12888         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12889     },
12890     
12891     /**
12892      * Removes and destroys this button
12893      */
12894     destroy : function(){
12895         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12896         this.td.parentNode.removeChild(this.td);
12897     },
12898     
12899     /**
12900      * Shows this button
12901      */
12902     show: function(){
12903         this.hidden = false;
12904         this.td.style.display = "";
12905     },
12906     
12907     /**
12908      * Hides this button
12909      */
12910     hide: function(){
12911         this.hidden = true;
12912         this.td.style.display = "none";
12913     }
12914 });
12915
12916 // backwards compat
12917 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12918  * Based on:
12919  * Ext JS Library 1.1.1
12920  * Copyright(c) 2006-2007, Ext JS, LLC.
12921  *
12922  * Originally Released Under LGPL - original licence link has changed is not relivant.
12923  *
12924  * Fork - LGPL
12925  * <script type="text/javascript">
12926  */
12927  
12928 /**
12929  * @class Roo.PagingToolbar
12930  * @extends Roo.Toolbar
12931  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12932  * @constructor
12933  * Create a new PagingToolbar
12934  * @param {Object} config The config object
12935  */
12936 Roo.PagingToolbar = function(el, ds, config)
12937 {
12938     // old args format still supported... - xtype is prefered..
12939     if (typeof(el) == 'object' && el.xtype) {
12940         // created from xtype...
12941         config = el;
12942         ds = el.dataSource;
12943         el = config.container;
12944     }
12945     var items = [];
12946     if (config.items) {
12947         items = config.items;
12948         config.items = [];
12949     }
12950     
12951     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12952     this.ds = ds;
12953     this.cursor = 0;
12954     this.renderButtons(this.el);
12955     this.bind(ds);
12956     
12957     // supprot items array.
12958    
12959     Roo.each(items, function(e) {
12960         this.add(Roo.factory(e));
12961     },this);
12962     
12963 };
12964
12965 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12966     /**
12967      * @cfg {Roo.data.Store} dataSource
12968      * The underlying data store providing the paged data
12969      */
12970     /**
12971      * @cfg {String/HTMLElement/Element} container
12972      * container The id or element that will contain the toolbar
12973      */
12974     /**
12975      * @cfg {Boolean} displayInfo
12976      * True to display the displayMsg (defaults to false)
12977      */
12978     /**
12979      * @cfg {Number} pageSize
12980      * The number of records to display per page (defaults to 20)
12981      */
12982     pageSize: 20,
12983     /**
12984      * @cfg {String} displayMsg
12985      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12986      */
12987     displayMsg : 'Displaying {0} - {1} of {2}',
12988     /**
12989      * @cfg {String} emptyMsg
12990      * The message to display when no records are found (defaults to "No data to display")
12991      */
12992     emptyMsg : 'No data to display',
12993     /**
12994      * Customizable piece of the default paging text (defaults to "Page")
12995      * @type String
12996      */
12997     beforePageText : "Page",
12998     /**
12999      * Customizable piece of the default paging text (defaults to "of %0")
13000      * @type String
13001      */
13002     afterPageText : "of {0}",
13003     /**
13004      * Customizable piece of the default paging text (defaults to "First Page")
13005      * @type String
13006      */
13007     firstText : "First Page",
13008     /**
13009      * Customizable piece of the default paging text (defaults to "Previous Page")
13010      * @type String
13011      */
13012     prevText : "Previous Page",
13013     /**
13014      * Customizable piece of the default paging text (defaults to "Next Page")
13015      * @type String
13016      */
13017     nextText : "Next Page",
13018     /**
13019      * Customizable piece of the default paging text (defaults to "Last Page")
13020      * @type String
13021      */
13022     lastText : "Last Page",
13023     /**
13024      * Customizable piece of the default paging text (defaults to "Refresh")
13025      * @type String
13026      */
13027     refreshText : "Refresh",
13028
13029     // private
13030     renderButtons : function(el){
13031         Roo.PagingToolbar.superclass.render.call(this, el);
13032         this.first = this.addButton({
13033             tooltip: this.firstText,
13034             cls: "x-btn-icon x-grid-page-first",
13035             disabled: true,
13036             handler: this.onClick.createDelegate(this, ["first"])
13037         });
13038         this.prev = this.addButton({
13039             tooltip: this.prevText,
13040             cls: "x-btn-icon x-grid-page-prev",
13041             disabled: true,
13042             handler: this.onClick.createDelegate(this, ["prev"])
13043         });
13044         //this.addSeparator();
13045         this.add(this.beforePageText);
13046         this.field = Roo.get(this.addDom({
13047            tag: "input",
13048            type: "text",
13049            size: "3",
13050            value: "1",
13051            cls: "x-grid-page-number"
13052         }).el);
13053         this.field.on("keydown", this.onPagingKeydown, this);
13054         this.field.on("focus", function(){this.dom.select();});
13055         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13056         this.field.setHeight(18);
13057         //this.addSeparator();
13058         this.next = this.addButton({
13059             tooltip: this.nextText,
13060             cls: "x-btn-icon x-grid-page-next",
13061             disabled: true,
13062             handler: this.onClick.createDelegate(this, ["next"])
13063         });
13064         this.last = this.addButton({
13065             tooltip: this.lastText,
13066             cls: "x-btn-icon x-grid-page-last",
13067             disabled: true,
13068             handler: this.onClick.createDelegate(this, ["last"])
13069         });
13070         //this.addSeparator();
13071         this.loading = this.addButton({
13072             tooltip: this.refreshText,
13073             cls: "x-btn-icon x-grid-loading",
13074             handler: this.onClick.createDelegate(this, ["refresh"])
13075         });
13076
13077         if(this.displayInfo){
13078             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13079         }
13080     },
13081
13082     // private
13083     updateInfo : function(){
13084         if(this.displayEl){
13085             var count = this.ds.getCount();
13086             var msg = count == 0 ?
13087                 this.emptyMsg :
13088                 String.format(
13089                     this.displayMsg,
13090                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13091                 );
13092             this.displayEl.update(msg);
13093         }
13094     },
13095
13096     // private
13097     onLoad : function(ds, r, o){
13098        this.cursor = o.params ? o.params.start : 0;
13099        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13100
13101        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13102        this.field.dom.value = ap;
13103        this.first.setDisabled(ap == 1);
13104        this.prev.setDisabled(ap == 1);
13105        this.next.setDisabled(ap == ps);
13106        this.last.setDisabled(ap == ps);
13107        this.loading.enable();
13108        this.updateInfo();
13109     },
13110
13111     // private
13112     getPageData : function(){
13113         var total = this.ds.getTotalCount();
13114         return {
13115             total : total,
13116             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13117             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13118         };
13119     },
13120
13121     // private
13122     onLoadError : function(){
13123         this.loading.enable();
13124     },
13125
13126     // private
13127     onPagingKeydown : function(e){
13128         var k = e.getKey();
13129         var d = this.getPageData();
13130         if(k == e.RETURN){
13131             var v = this.field.dom.value, pageNum;
13132             if(!v || isNaN(pageNum = parseInt(v, 10))){
13133                 this.field.dom.value = d.activePage;
13134                 return;
13135             }
13136             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13137             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13138             e.stopEvent();
13139         }
13140         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))
13141         {
13142           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13143           this.field.dom.value = pageNum;
13144           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13145           e.stopEvent();
13146         }
13147         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13148         {
13149           var v = this.field.dom.value, pageNum; 
13150           var increment = (e.shiftKey) ? 10 : 1;
13151           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13152             increment *= -1;
13153           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13154             this.field.dom.value = d.activePage;
13155             return;
13156           }
13157           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13158           {
13159             this.field.dom.value = parseInt(v, 10) + increment;
13160             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13161             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13162           }
13163           e.stopEvent();
13164         }
13165     },
13166
13167     // private
13168     beforeLoad : function(){
13169         if(this.loading){
13170             this.loading.disable();
13171         }
13172     },
13173
13174     // private
13175     onClick : function(which){
13176         var ds = this.ds;
13177         switch(which){
13178             case "first":
13179                 ds.load({params:{start: 0, limit: this.pageSize}});
13180             break;
13181             case "prev":
13182                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13183             break;
13184             case "next":
13185                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13186             break;
13187             case "last":
13188                 var total = ds.getTotalCount();
13189                 var extra = total % this.pageSize;
13190                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13191                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13192             break;
13193             case "refresh":
13194                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13195             break;
13196         }
13197     },
13198
13199     /**
13200      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13201      * @param {Roo.data.Store} store The data store to unbind
13202      */
13203     unbind : function(ds){
13204         ds.un("beforeload", this.beforeLoad, this);
13205         ds.un("load", this.onLoad, this);
13206         ds.un("loadexception", this.onLoadError, this);
13207         ds.un("remove", this.updateInfo, this);
13208         ds.un("add", this.updateInfo, this);
13209         this.ds = undefined;
13210     },
13211
13212     /**
13213      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13214      * @param {Roo.data.Store} store The data store to bind
13215      */
13216     bind : function(ds){
13217         ds.on("beforeload", this.beforeLoad, this);
13218         ds.on("load", this.onLoad, this);
13219         ds.on("loadexception", this.onLoadError, this);
13220         ds.on("remove", this.updateInfo, this);
13221         ds.on("add", this.updateInfo, this);
13222         this.ds = ds;
13223     }
13224 });/*
13225  * Based on:
13226  * Ext JS Library 1.1.1
13227  * Copyright(c) 2006-2007, Ext JS, LLC.
13228  *
13229  * Originally Released Under LGPL - original licence link has changed is not relivant.
13230  *
13231  * Fork - LGPL
13232  * <script type="text/javascript">
13233  */
13234
13235 /**
13236  * @class Roo.Resizable
13237  * @extends Roo.util.Observable
13238  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13239  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13240  * 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
13241  * the element will be wrapped for you automatically.</p>
13242  * <p>Here is the list of valid resize handles:</p>
13243  * <pre>
13244 Value   Description
13245 ------  -------------------
13246  'n'     north
13247  's'     south
13248  'e'     east
13249  'w'     west
13250  'nw'    northwest
13251  'sw'    southwest
13252  'se'    southeast
13253  'ne'    northeast
13254  'hd'    horizontal drag
13255  'all'   all
13256 </pre>
13257  * <p>Here's an example showing the creation of a typical Resizable:</p>
13258  * <pre><code>
13259 var resizer = new Roo.Resizable("element-id", {
13260     handles: 'all',
13261     minWidth: 200,
13262     minHeight: 100,
13263     maxWidth: 500,
13264     maxHeight: 400,
13265     pinned: true
13266 });
13267 resizer.on("resize", myHandler);
13268 </code></pre>
13269  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13270  * resizer.east.setDisplayed(false);</p>
13271  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13272  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13273  * resize operation's new size (defaults to [0, 0])
13274  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13275  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13276  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13277  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13278  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13279  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13280  * @cfg {Number} width The width of the element in pixels (defaults to null)
13281  * @cfg {Number} height The height of the element in pixels (defaults to null)
13282  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13283  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13284  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13285  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13286  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13287  * in favor of the handles config option (defaults to false)
13288  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13289  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13290  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13291  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13292  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13293  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13294  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13295  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13296  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13297  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13298  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13299  * @constructor
13300  * Create a new resizable component
13301  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13302  * @param {Object} config configuration options
13303   */
13304 Roo.Resizable = function(el, config)
13305 {
13306     this.el = Roo.get(el);
13307
13308     if(config && config.wrap){
13309         config.resizeChild = this.el;
13310         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13311         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13312         this.el.setStyle("overflow", "hidden");
13313         this.el.setPositioning(config.resizeChild.getPositioning());
13314         config.resizeChild.clearPositioning();
13315         if(!config.width || !config.height){
13316             var csize = config.resizeChild.getSize();
13317             this.el.setSize(csize.width, csize.height);
13318         }
13319         if(config.pinned && !config.adjustments){
13320             config.adjustments = "auto";
13321         }
13322     }
13323
13324     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13325     this.proxy.unselectable();
13326     this.proxy.enableDisplayMode('block');
13327
13328     Roo.apply(this, config);
13329
13330     if(this.pinned){
13331         this.disableTrackOver = true;
13332         this.el.addClass("x-resizable-pinned");
13333     }
13334     // if the element isn't positioned, make it relative
13335     var position = this.el.getStyle("position");
13336     if(position != "absolute" && position != "fixed"){
13337         this.el.setStyle("position", "relative");
13338     }
13339     if(!this.handles){ // no handles passed, must be legacy style
13340         this.handles = 's,e,se';
13341         if(this.multiDirectional){
13342             this.handles += ',n,w';
13343         }
13344     }
13345     if(this.handles == "all"){
13346         this.handles = "n s e w ne nw se sw";
13347     }
13348     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13349     var ps = Roo.Resizable.positions;
13350     for(var i = 0, len = hs.length; i < len; i++){
13351         if(hs[i] && ps[hs[i]]){
13352             var pos = ps[hs[i]];
13353             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13354         }
13355     }
13356     // legacy
13357     this.corner = this.southeast;
13358     
13359     // updateBox = the box can move..
13360     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13361         this.updateBox = true;
13362     }
13363
13364     this.activeHandle = null;
13365
13366     if(this.resizeChild){
13367         if(typeof this.resizeChild == "boolean"){
13368             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13369         }else{
13370             this.resizeChild = Roo.get(this.resizeChild, true);
13371         }
13372     }
13373     
13374     if(this.adjustments == "auto"){
13375         var rc = this.resizeChild;
13376         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13377         if(rc && (hw || hn)){
13378             rc.position("relative");
13379             rc.setLeft(hw ? hw.el.getWidth() : 0);
13380             rc.setTop(hn ? hn.el.getHeight() : 0);
13381         }
13382         this.adjustments = [
13383             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13384             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13385         ];
13386     }
13387
13388     if(this.draggable){
13389         this.dd = this.dynamic ?
13390             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13391         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13392     }
13393
13394     // public events
13395     this.addEvents({
13396         /**
13397          * @event beforeresize
13398          * Fired before resize is allowed. Set enabled to false to cancel resize.
13399          * @param {Roo.Resizable} this
13400          * @param {Roo.EventObject} e The mousedown event
13401          */
13402         "beforeresize" : true,
13403         /**
13404          * @event resize
13405          * Fired after a resize.
13406          * @param {Roo.Resizable} this
13407          * @param {Number} width The new width
13408          * @param {Number} height The new height
13409          * @param {Roo.EventObject} e The mouseup event
13410          */
13411         "resize" : true
13412     });
13413
13414     if(this.width !== null && this.height !== null){
13415         this.resizeTo(this.width, this.height);
13416     }else{
13417         this.updateChildSize();
13418     }
13419     if(Roo.isIE){
13420         this.el.dom.style.zoom = 1;
13421     }
13422     Roo.Resizable.superclass.constructor.call(this);
13423 };
13424
13425 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13426         resizeChild : false,
13427         adjustments : [0, 0],
13428         minWidth : 5,
13429         minHeight : 5,
13430         maxWidth : 10000,
13431         maxHeight : 10000,
13432         enabled : true,
13433         animate : false,
13434         duration : .35,
13435         dynamic : false,
13436         handles : false,
13437         multiDirectional : false,
13438         disableTrackOver : false,
13439         easing : 'easeOutStrong',
13440         widthIncrement : 0,
13441         heightIncrement : 0,
13442         pinned : false,
13443         width : null,
13444         height : null,
13445         preserveRatio : false,
13446         transparent: false,
13447         minX: 0,
13448         minY: 0,
13449         draggable: false,
13450
13451         /**
13452          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13453          */
13454         constrainTo: undefined,
13455         /**
13456          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13457          */
13458         resizeRegion: undefined,
13459
13460
13461     /**
13462      * Perform a manual resize
13463      * @param {Number} width
13464      * @param {Number} height
13465      */
13466     resizeTo : function(width, height){
13467         this.el.setSize(width, height);
13468         this.updateChildSize();
13469         this.fireEvent("resize", this, width, height, null);
13470     },
13471
13472     // private
13473     startSizing : function(e, handle){
13474         this.fireEvent("beforeresize", this, e);
13475         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13476
13477             if(!this.overlay){
13478                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13479                 this.overlay.unselectable();
13480                 this.overlay.enableDisplayMode("block");
13481                 this.overlay.on("mousemove", this.onMouseMove, this);
13482                 this.overlay.on("mouseup", this.onMouseUp, this);
13483             }
13484             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13485
13486             this.resizing = true;
13487             this.startBox = this.el.getBox();
13488             this.startPoint = e.getXY();
13489             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13490                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13491
13492             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13493             this.overlay.show();
13494
13495             if(this.constrainTo) {
13496                 var ct = Roo.get(this.constrainTo);
13497                 this.resizeRegion = ct.getRegion().adjust(
13498                     ct.getFrameWidth('t'),
13499                     ct.getFrameWidth('l'),
13500                     -ct.getFrameWidth('b'),
13501                     -ct.getFrameWidth('r')
13502                 );
13503             }
13504
13505             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13506             this.proxy.show();
13507             this.proxy.setBox(this.startBox);
13508             if(!this.dynamic){
13509                 this.proxy.setStyle('visibility', 'visible');
13510             }
13511         }
13512     },
13513
13514     // private
13515     onMouseDown : function(handle, e){
13516         if(this.enabled){
13517             e.stopEvent();
13518             this.activeHandle = handle;
13519             this.startSizing(e, handle);
13520         }
13521     },
13522
13523     // private
13524     onMouseUp : function(e){
13525         var size = this.resizeElement();
13526         this.resizing = false;
13527         this.handleOut();
13528         this.overlay.hide();
13529         this.proxy.hide();
13530         this.fireEvent("resize", this, size.width, size.height, e);
13531     },
13532
13533     // private
13534     updateChildSize : function(){
13535         if(this.resizeChild){
13536             var el = this.el;
13537             var child = this.resizeChild;
13538             var adj = this.adjustments;
13539             if(el.dom.offsetWidth){
13540                 var b = el.getSize(true);
13541                 child.setSize(b.width+adj[0], b.height+adj[1]);
13542             }
13543             // Second call here for IE
13544             // The first call enables instant resizing and
13545             // the second call corrects scroll bars if they
13546             // exist
13547             if(Roo.isIE){
13548                 setTimeout(function(){
13549                     if(el.dom.offsetWidth){
13550                         var b = el.getSize(true);
13551                         child.setSize(b.width+adj[0], b.height+adj[1]);
13552                     }
13553                 }, 10);
13554             }
13555         }
13556     },
13557
13558     // private
13559     snap : function(value, inc, min){
13560         if(!inc || !value) return value;
13561         var newValue = value;
13562         var m = value % inc;
13563         if(m > 0){
13564             if(m > (inc/2)){
13565                 newValue = value + (inc-m);
13566             }else{
13567                 newValue = value - m;
13568             }
13569         }
13570         return Math.max(min, newValue);
13571     },
13572
13573     // private
13574     resizeElement : function(){
13575         var box = this.proxy.getBox();
13576         if(this.updateBox){
13577             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13578         }else{
13579             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13580         }
13581         this.updateChildSize();
13582         if(!this.dynamic){
13583             this.proxy.hide();
13584         }
13585         return box;
13586     },
13587
13588     // private
13589     constrain : function(v, diff, m, mx){
13590         if(v - diff < m){
13591             diff = v - m;
13592         }else if(v - diff > mx){
13593             diff = mx - v;
13594         }
13595         return diff;
13596     },
13597
13598     // private
13599     onMouseMove : function(e){
13600         if(this.enabled){
13601             try{// try catch so if something goes wrong the user doesn't get hung
13602
13603             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13604                 return;
13605             }
13606
13607             //var curXY = this.startPoint;
13608             var curSize = this.curSize || this.startBox;
13609             var x = this.startBox.x, y = this.startBox.y;
13610             var ox = x, oy = y;
13611             var w = curSize.width, h = curSize.height;
13612             var ow = w, oh = h;
13613             var mw = this.minWidth, mh = this.minHeight;
13614             var mxw = this.maxWidth, mxh = this.maxHeight;
13615             var wi = this.widthIncrement;
13616             var hi = this.heightIncrement;
13617
13618             var eventXY = e.getXY();
13619             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13620             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13621
13622             var pos = this.activeHandle.position;
13623
13624             switch(pos){
13625                 case "east":
13626                     w += diffX;
13627                     w = Math.min(Math.max(mw, w), mxw);
13628                     break;
13629              
13630                 case "south":
13631                     h += diffY;
13632                     h = Math.min(Math.max(mh, h), mxh);
13633                     break;
13634                 case "southeast":
13635                     w += diffX;
13636                     h += diffY;
13637                     w = Math.min(Math.max(mw, w), mxw);
13638                     h = Math.min(Math.max(mh, h), mxh);
13639                     break;
13640                 case "north":
13641                     diffY = this.constrain(h, diffY, mh, mxh);
13642                     y += diffY;
13643                     h -= diffY;
13644                     break;
13645                 case "hdrag":
13646                     
13647                     if (wi) {
13648                         var adiffX = Math.abs(diffX);
13649                         var sub = (adiffX % wi); // how much 
13650                         if (sub > (wi/2)) { // far enough to snap
13651                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13652                         } else {
13653                             // remove difference.. 
13654                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13655                         }
13656                     }
13657                     x += diffX;
13658                     x = Math.max(this.minX, x);
13659                     break;
13660                 case "west":
13661                     diffX = this.constrain(w, diffX, mw, mxw);
13662                     x += diffX;
13663                     w -= diffX;
13664                     break;
13665                 case "northeast":
13666                     w += diffX;
13667                     w = Math.min(Math.max(mw, w), mxw);
13668                     diffY = this.constrain(h, diffY, mh, mxh);
13669                     y += diffY;
13670                     h -= diffY;
13671                     break;
13672                 case "northwest":
13673                     diffX = this.constrain(w, diffX, mw, mxw);
13674                     diffY = this.constrain(h, diffY, mh, mxh);
13675                     y += diffY;
13676                     h -= diffY;
13677                     x += diffX;
13678                     w -= diffX;
13679                     break;
13680                case "southwest":
13681                     diffX = this.constrain(w, diffX, mw, mxw);
13682                     h += diffY;
13683                     h = Math.min(Math.max(mh, h), mxh);
13684                     x += diffX;
13685                     w -= diffX;
13686                     break;
13687             }
13688
13689             var sw = this.snap(w, wi, mw);
13690             var sh = this.snap(h, hi, mh);
13691             if(sw != w || sh != h){
13692                 switch(pos){
13693                     case "northeast":
13694                         y -= sh - h;
13695                     break;
13696                     case "north":
13697                         y -= sh - h;
13698                         break;
13699                     case "southwest":
13700                         x -= sw - w;
13701                     break;
13702                     case "west":
13703                         x -= sw - w;
13704                         break;
13705                     case "northwest":
13706                         x -= sw - w;
13707                         y -= sh - h;
13708                     break;
13709                 }
13710                 w = sw;
13711                 h = sh;
13712             }
13713
13714             if(this.preserveRatio){
13715                 switch(pos){
13716                     case "southeast":
13717                     case "east":
13718                         h = oh * (w/ow);
13719                         h = Math.min(Math.max(mh, h), mxh);
13720                         w = ow * (h/oh);
13721                        break;
13722                     case "south":
13723                         w = ow * (h/oh);
13724                         w = Math.min(Math.max(mw, w), mxw);
13725                         h = oh * (w/ow);
13726                         break;
13727                     case "northeast":
13728                         w = ow * (h/oh);
13729                         w = Math.min(Math.max(mw, w), mxw);
13730                         h = oh * (w/ow);
13731                     break;
13732                     case "north":
13733                         var tw = w;
13734                         w = ow * (h/oh);
13735                         w = Math.min(Math.max(mw, w), mxw);
13736                         h = oh * (w/ow);
13737                         x += (tw - w) / 2;
13738                         break;
13739                     case "southwest":
13740                         h = oh * (w/ow);
13741                         h = Math.min(Math.max(mh, h), mxh);
13742                         var tw = w;
13743                         w = ow * (h/oh);
13744                         x += tw - w;
13745                         break;
13746                     case "west":
13747                         var th = h;
13748                         h = oh * (w/ow);
13749                         h = Math.min(Math.max(mh, h), mxh);
13750                         y += (th - h) / 2;
13751                         var tw = w;
13752                         w = ow * (h/oh);
13753                         x += tw - w;
13754                        break;
13755                     case "northwest":
13756                         var tw = w;
13757                         var th = h;
13758                         h = oh * (w/ow);
13759                         h = Math.min(Math.max(mh, h), mxh);
13760                         w = ow * (h/oh);
13761                         y += th - h;
13762                         x += tw - w;
13763                        break;
13764
13765                 }
13766             }
13767             if (pos == 'hdrag') {
13768                 w = ow;
13769             }
13770             this.proxy.setBounds(x, y, w, h);
13771             if(this.dynamic){
13772                 this.resizeElement();
13773             }
13774             }catch(e){}
13775         }
13776     },
13777
13778     // private
13779     handleOver : function(){
13780         if(this.enabled){
13781             this.el.addClass("x-resizable-over");
13782         }
13783     },
13784
13785     // private
13786     handleOut : function(){
13787         if(!this.resizing){
13788             this.el.removeClass("x-resizable-over");
13789         }
13790     },
13791
13792     /**
13793      * Returns the element this component is bound to.
13794      * @return {Roo.Element}
13795      */
13796     getEl : function(){
13797         return this.el;
13798     },
13799
13800     /**
13801      * Returns the resizeChild element (or null).
13802      * @return {Roo.Element}
13803      */
13804     getResizeChild : function(){
13805         return this.resizeChild;
13806     },
13807
13808     /**
13809      * Destroys this resizable. If the element was wrapped and
13810      * removeEl is not true then the element remains.
13811      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13812      */
13813     destroy : function(removeEl){
13814         this.proxy.remove();
13815         if(this.overlay){
13816             this.overlay.removeAllListeners();
13817             this.overlay.remove();
13818         }
13819         var ps = Roo.Resizable.positions;
13820         for(var k in ps){
13821             if(typeof ps[k] != "function" && this[ps[k]]){
13822                 var h = this[ps[k]];
13823                 h.el.removeAllListeners();
13824                 h.el.remove();
13825             }
13826         }
13827         if(removeEl){
13828             this.el.update("");
13829             this.el.remove();
13830         }
13831     }
13832 });
13833
13834 // private
13835 // hash to map config positions to true positions
13836 Roo.Resizable.positions = {
13837     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13838     hd: "hdrag"
13839 };
13840
13841 // private
13842 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13843     if(!this.tpl){
13844         // only initialize the template if resizable is used
13845         var tpl = Roo.DomHelper.createTemplate(
13846             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13847         );
13848         tpl.compile();
13849         Roo.Resizable.Handle.prototype.tpl = tpl;
13850     }
13851     this.position = pos;
13852     this.rz = rz;
13853     // show north drag fro topdra
13854     var handlepos = pos == 'hdrag' ? 'north' : pos;
13855     
13856     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13857     if (pos == 'hdrag') {
13858         this.el.setStyle('cursor', 'pointer');
13859     }
13860     this.el.unselectable();
13861     if(transparent){
13862         this.el.setOpacity(0);
13863     }
13864     this.el.on("mousedown", this.onMouseDown, this);
13865     if(!disableTrackOver){
13866         this.el.on("mouseover", this.onMouseOver, this);
13867         this.el.on("mouseout", this.onMouseOut, this);
13868     }
13869 };
13870
13871 // private
13872 Roo.Resizable.Handle.prototype = {
13873     afterResize : function(rz){
13874         // do nothing
13875     },
13876     // private
13877     onMouseDown : function(e){
13878         this.rz.onMouseDown(this, e);
13879     },
13880     // private
13881     onMouseOver : function(e){
13882         this.rz.handleOver(this, e);
13883     },
13884     // private
13885     onMouseOut : function(e){
13886         this.rz.handleOut(this, e);
13887     }
13888 };/*
13889  * Based on:
13890  * Ext JS Library 1.1.1
13891  * Copyright(c) 2006-2007, Ext JS, LLC.
13892  *
13893  * Originally Released Under LGPL - original licence link has changed is not relivant.
13894  *
13895  * Fork - LGPL
13896  * <script type="text/javascript">
13897  */
13898
13899 /**
13900  * @class Roo.Editor
13901  * @extends Roo.Component
13902  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13903  * @constructor
13904  * Create a new Editor
13905  * @param {Roo.form.Field} field The Field object (or descendant)
13906  * @param {Object} config The config object
13907  */
13908 Roo.Editor = function(field, config){
13909     Roo.Editor.superclass.constructor.call(this, config);
13910     this.field = field;
13911     this.addEvents({
13912         /**
13913              * @event beforestartedit
13914              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13915              * false from the handler of this event.
13916              * @param {Editor} this
13917              * @param {Roo.Element} boundEl The underlying element bound to this editor
13918              * @param {Mixed} value The field value being set
13919              */
13920         "beforestartedit" : true,
13921         /**
13922              * @event startedit
13923              * Fires when this editor is displayed
13924              * @param {Roo.Element} boundEl The underlying element bound to this editor
13925              * @param {Mixed} value The starting field value
13926              */
13927         "startedit" : true,
13928         /**
13929              * @event beforecomplete
13930              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13931              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13932              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13933              * event will not fire since no edit actually occurred.
13934              * @param {Editor} this
13935              * @param {Mixed} value The current field value
13936              * @param {Mixed} startValue The original field value
13937              */
13938         "beforecomplete" : true,
13939         /**
13940              * @event complete
13941              * Fires after editing is complete and any changed value has been written to the underlying field.
13942              * @param {Editor} this
13943              * @param {Mixed} value The current field value
13944              * @param {Mixed} startValue The original field value
13945              */
13946         "complete" : true,
13947         /**
13948          * @event specialkey
13949          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13950          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13951          * @param {Roo.form.Field} this
13952          * @param {Roo.EventObject} e The event object
13953          */
13954         "specialkey" : true
13955     });
13956 };
13957
13958 Roo.extend(Roo.Editor, Roo.Component, {
13959     /**
13960      * @cfg {Boolean/String} autosize
13961      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13962      * or "height" to adopt the height only (defaults to false)
13963      */
13964     /**
13965      * @cfg {Boolean} revertInvalid
13966      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13967      * validation fails (defaults to true)
13968      */
13969     /**
13970      * @cfg {Boolean} ignoreNoChange
13971      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13972      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13973      * will never be ignored.
13974      */
13975     /**
13976      * @cfg {Boolean} hideEl
13977      * False to keep the bound element visible while the editor is displayed (defaults to true)
13978      */
13979     /**
13980      * @cfg {Mixed} value
13981      * The data value of the underlying field (defaults to "")
13982      */
13983     value : "",
13984     /**
13985      * @cfg {String} alignment
13986      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13987      */
13988     alignment: "c-c?",
13989     /**
13990      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13991      * for bottom-right shadow (defaults to "frame")
13992      */
13993     shadow : "frame",
13994     /**
13995      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13996      */
13997     constrain : false,
13998     /**
13999      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14000      */
14001     completeOnEnter : false,
14002     /**
14003      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14004      */
14005     cancelOnEsc : false,
14006     /**
14007      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14008      */
14009     updateEl : false,
14010
14011     // private
14012     onRender : function(ct, position){
14013         this.el = new Roo.Layer({
14014             shadow: this.shadow,
14015             cls: "x-editor",
14016             parentEl : ct,
14017             shim : this.shim,
14018             shadowOffset:4,
14019             id: this.id,
14020             constrain: this.constrain
14021         });
14022         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14023         if(this.field.msgTarget != 'title'){
14024             this.field.msgTarget = 'qtip';
14025         }
14026         this.field.render(this.el);
14027         if(Roo.isGecko){
14028             this.field.el.dom.setAttribute('autocomplete', 'off');
14029         }
14030         this.field.on("specialkey", this.onSpecialKey, this);
14031         if(this.swallowKeys){
14032             this.field.el.swallowEvent(['keydown','keypress']);
14033         }
14034         this.field.show();
14035         this.field.on("blur", this.onBlur, this);
14036         if(this.field.grow){
14037             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14038         }
14039     },
14040
14041     onSpecialKey : function(field, e)
14042     {
14043         //Roo.log('editor onSpecialKey');
14044         if(this.completeOnEnter && e.getKey() == e.ENTER){
14045             e.stopEvent();
14046             this.completeEdit();
14047             return;
14048         }
14049         // do not fire special key otherwise it might hide close the editor...
14050         if(e.getKey() == e.ENTER){    
14051             return;
14052         }
14053         if(this.cancelOnEsc && e.getKey() == e.ESC){
14054             this.cancelEdit();
14055             return;
14056         } 
14057         this.fireEvent('specialkey', field, e);
14058     
14059     },
14060
14061     /**
14062      * Starts the editing process and shows the editor.
14063      * @param {String/HTMLElement/Element} el The element to edit
14064      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14065       * to the innerHTML of el.
14066      */
14067     startEdit : function(el, value){
14068         if(this.editing){
14069             this.completeEdit();
14070         }
14071         this.boundEl = Roo.get(el);
14072         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14073         if(!this.rendered){
14074             this.render(this.parentEl || document.body);
14075         }
14076         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14077             return;
14078         }
14079         this.startValue = v;
14080         this.field.setValue(v);
14081         if(this.autoSize){
14082             var sz = this.boundEl.getSize();
14083             switch(this.autoSize){
14084                 case "width":
14085                 this.setSize(sz.width,  "");
14086                 break;
14087                 case "height":
14088                 this.setSize("",  sz.height);
14089                 break;
14090                 default:
14091                 this.setSize(sz.width,  sz.height);
14092             }
14093         }
14094         this.el.alignTo(this.boundEl, this.alignment);
14095         this.editing = true;
14096         if(Roo.QuickTips){
14097             Roo.QuickTips.disable();
14098         }
14099         this.show();
14100     },
14101
14102     /**
14103      * Sets the height and width of this editor.
14104      * @param {Number} width The new width
14105      * @param {Number} height The new height
14106      */
14107     setSize : function(w, h){
14108         this.field.setSize(w, h);
14109         if(this.el){
14110             this.el.sync();
14111         }
14112     },
14113
14114     /**
14115      * Realigns the editor to the bound field based on the current alignment config value.
14116      */
14117     realign : function(){
14118         this.el.alignTo(this.boundEl, this.alignment);
14119     },
14120
14121     /**
14122      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14123      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14124      */
14125     completeEdit : function(remainVisible){
14126         if(!this.editing){
14127             return;
14128         }
14129         var v = this.getValue();
14130         if(this.revertInvalid !== false && !this.field.isValid()){
14131             v = this.startValue;
14132             this.cancelEdit(true);
14133         }
14134         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14135             this.editing = false;
14136             this.hide();
14137             return;
14138         }
14139         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14140             this.editing = false;
14141             if(this.updateEl && this.boundEl){
14142                 this.boundEl.update(v);
14143             }
14144             if(remainVisible !== true){
14145                 this.hide();
14146             }
14147             this.fireEvent("complete", this, v, this.startValue);
14148         }
14149     },
14150
14151     // private
14152     onShow : function(){
14153         this.el.show();
14154         if(this.hideEl !== false){
14155             this.boundEl.hide();
14156         }
14157         this.field.show();
14158         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14159             this.fixIEFocus = true;
14160             this.deferredFocus.defer(50, this);
14161         }else{
14162             this.field.focus();
14163         }
14164         this.fireEvent("startedit", this.boundEl, this.startValue);
14165     },
14166
14167     deferredFocus : function(){
14168         if(this.editing){
14169             this.field.focus();
14170         }
14171     },
14172
14173     /**
14174      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14175      * reverted to the original starting value.
14176      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14177      * cancel (defaults to false)
14178      */
14179     cancelEdit : function(remainVisible){
14180         if(this.editing){
14181             this.setValue(this.startValue);
14182             if(remainVisible !== true){
14183                 this.hide();
14184             }
14185         }
14186     },
14187
14188     // private
14189     onBlur : function(){
14190         if(this.allowBlur !== true && this.editing){
14191             this.completeEdit();
14192         }
14193     },
14194
14195     // private
14196     onHide : function(){
14197         if(this.editing){
14198             this.completeEdit();
14199             return;
14200         }
14201         this.field.blur();
14202         if(this.field.collapse){
14203             this.field.collapse();
14204         }
14205         this.el.hide();
14206         if(this.hideEl !== false){
14207             this.boundEl.show();
14208         }
14209         if(Roo.QuickTips){
14210             Roo.QuickTips.enable();
14211         }
14212     },
14213
14214     /**
14215      * Sets the data value of the editor
14216      * @param {Mixed} value Any valid value supported by the underlying field
14217      */
14218     setValue : function(v){
14219         this.field.setValue(v);
14220     },
14221
14222     /**
14223      * Gets the data value of the editor
14224      * @return {Mixed} The data value
14225      */
14226     getValue : function(){
14227         return this.field.getValue();
14228     }
14229 });/*
14230  * Based on:
14231  * Ext JS Library 1.1.1
14232  * Copyright(c) 2006-2007, Ext JS, LLC.
14233  *
14234  * Originally Released Under LGPL - original licence link has changed is not relivant.
14235  *
14236  * Fork - LGPL
14237  * <script type="text/javascript">
14238  */
14239  
14240 /**
14241  * @class Roo.BasicDialog
14242  * @extends Roo.util.Observable
14243  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14244  * <pre><code>
14245 var dlg = new Roo.BasicDialog("my-dlg", {
14246     height: 200,
14247     width: 300,
14248     minHeight: 100,
14249     minWidth: 150,
14250     modal: true,
14251     proxyDrag: true,
14252     shadow: true
14253 });
14254 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14255 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14256 dlg.addButton('Cancel', dlg.hide, dlg);
14257 dlg.show();
14258 </code></pre>
14259   <b>A Dialog should always be a direct child of the body element.</b>
14260  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14261  * @cfg {String} title Default text to display in the title bar (defaults to null)
14262  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14263  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14264  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14265  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14266  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14267  * (defaults to null with no animation)
14268  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14269  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14270  * property for valid values (defaults to 'all')
14271  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14272  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14273  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14274  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14275  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14276  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14277  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14278  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14279  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14280  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14281  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14282  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14283  * draggable = true (defaults to false)
14284  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14285  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14286  * shadow (defaults to false)
14287  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14288  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14289  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14290  * @cfg {Array} buttons Array of buttons
14291  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14292  * @constructor
14293  * Create a new BasicDialog.
14294  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14295  * @param {Object} config Configuration options
14296  */
14297 Roo.BasicDialog = function(el, config){
14298     this.el = Roo.get(el);
14299     var dh = Roo.DomHelper;
14300     if(!this.el && config && config.autoCreate){
14301         if(typeof config.autoCreate == "object"){
14302             if(!config.autoCreate.id){
14303                 config.autoCreate.id = el;
14304             }
14305             this.el = dh.append(document.body,
14306                         config.autoCreate, true);
14307         }else{
14308             this.el = dh.append(document.body,
14309                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14310         }
14311     }
14312     el = this.el;
14313     el.setDisplayed(true);
14314     el.hide = this.hideAction;
14315     this.id = el.id;
14316     el.addClass("x-dlg");
14317
14318     Roo.apply(this, config);
14319
14320     this.proxy = el.createProxy("x-dlg-proxy");
14321     this.proxy.hide = this.hideAction;
14322     this.proxy.setOpacity(.5);
14323     this.proxy.hide();
14324
14325     if(config.width){
14326         el.setWidth(config.width);
14327     }
14328     if(config.height){
14329         el.setHeight(config.height);
14330     }
14331     this.size = el.getSize();
14332     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14333         this.xy = [config.x,config.y];
14334     }else{
14335         this.xy = el.getCenterXY(true);
14336     }
14337     /** The header element @type Roo.Element */
14338     this.header = el.child("> .x-dlg-hd");
14339     /** The body element @type Roo.Element */
14340     this.body = el.child("> .x-dlg-bd");
14341     /** The footer element @type Roo.Element */
14342     this.footer = el.child("> .x-dlg-ft");
14343
14344     if(!this.header){
14345         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14346     }
14347     if(!this.body){
14348         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14349     }
14350
14351     this.header.unselectable();
14352     if(this.title){
14353         this.header.update(this.title);
14354     }
14355     // this element allows the dialog to be focused for keyboard event
14356     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14357     this.focusEl.swallowEvent("click", true);
14358
14359     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14360
14361     // wrap the body and footer for special rendering
14362     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14363     if(this.footer){
14364         this.bwrap.dom.appendChild(this.footer.dom);
14365     }
14366
14367     this.bg = this.el.createChild({
14368         tag: "div", cls:"x-dlg-bg",
14369         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14370     });
14371     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14372
14373
14374     if(this.autoScroll !== false && !this.autoTabs){
14375         this.body.setStyle("overflow", "auto");
14376     }
14377
14378     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14379
14380     if(this.closable !== false){
14381         this.el.addClass("x-dlg-closable");
14382         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14383         this.close.on("click", this.closeClick, this);
14384         this.close.addClassOnOver("x-dlg-close-over");
14385     }
14386     if(this.collapsible !== false){
14387         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14388         this.collapseBtn.on("click", this.collapseClick, this);
14389         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14390         this.header.on("dblclick", this.collapseClick, this);
14391     }
14392     if(this.resizable !== false){
14393         this.el.addClass("x-dlg-resizable");
14394         this.resizer = new Roo.Resizable(el, {
14395             minWidth: this.minWidth || 80,
14396             minHeight:this.minHeight || 80,
14397             handles: this.resizeHandles || "all",
14398             pinned: true
14399         });
14400         this.resizer.on("beforeresize", this.beforeResize, this);
14401         this.resizer.on("resize", this.onResize, this);
14402     }
14403     if(this.draggable !== false){
14404         el.addClass("x-dlg-draggable");
14405         if (!this.proxyDrag) {
14406             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14407         }
14408         else {
14409             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14410         }
14411         dd.setHandleElId(this.header.id);
14412         dd.endDrag = this.endMove.createDelegate(this);
14413         dd.startDrag = this.startMove.createDelegate(this);
14414         dd.onDrag = this.onDrag.createDelegate(this);
14415         dd.scroll = false;
14416         this.dd = dd;
14417     }
14418     if(this.modal){
14419         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14420         this.mask.enableDisplayMode("block");
14421         this.mask.hide();
14422         this.el.addClass("x-dlg-modal");
14423     }
14424     if(this.shadow){
14425         this.shadow = new Roo.Shadow({
14426             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14427             offset : this.shadowOffset
14428         });
14429     }else{
14430         this.shadowOffset = 0;
14431     }
14432     if(Roo.useShims && this.shim !== false){
14433         this.shim = this.el.createShim();
14434         this.shim.hide = this.hideAction;
14435         this.shim.hide();
14436     }else{
14437         this.shim = false;
14438     }
14439     if(this.autoTabs){
14440         this.initTabs();
14441     }
14442     if (this.buttons) { 
14443         var bts= this.buttons;
14444         this.buttons = [];
14445         Roo.each(bts, function(b) {
14446             this.addButton(b);
14447         }, this);
14448     }
14449     
14450     
14451     this.addEvents({
14452         /**
14453          * @event keydown
14454          * Fires when a key is pressed
14455          * @param {Roo.BasicDialog} this
14456          * @param {Roo.EventObject} e
14457          */
14458         "keydown" : true,
14459         /**
14460          * @event move
14461          * Fires when this dialog is moved by the user.
14462          * @param {Roo.BasicDialog} this
14463          * @param {Number} x The new page X
14464          * @param {Number} y The new page Y
14465          */
14466         "move" : true,
14467         /**
14468          * @event resize
14469          * Fires when this dialog is resized by the user.
14470          * @param {Roo.BasicDialog} this
14471          * @param {Number} width The new width
14472          * @param {Number} height The new height
14473          */
14474         "resize" : true,
14475         /**
14476          * @event beforehide
14477          * Fires before this dialog is hidden.
14478          * @param {Roo.BasicDialog} this
14479          */
14480         "beforehide" : true,
14481         /**
14482          * @event hide
14483          * Fires when this dialog is hidden.
14484          * @param {Roo.BasicDialog} this
14485          */
14486         "hide" : true,
14487         /**
14488          * @event beforeshow
14489          * Fires before this dialog is shown.
14490          * @param {Roo.BasicDialog} this
14491          */
14492         "beforeshow" : true,
14493         /**
14494          * @event show
14495          * Fires when this dialog is shown.
14496          * @param {Roo.BasicDialog} this
14497          */
14498         "show" : true
14499     });
14500     el.on("keydown", this.onKeyDown, this);
14501     el.on("mousedown", this.toFront, this);
14502     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14503     this.el.hide();
14504     Roo.DialogManager.register(this);
14505     Roo.BasicDialog.superclass.constructor.call(this);
14506 };
14507
14508 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14509     shadowOffset: Roo.isIE ? 6 : 5,
14510     minHeight: 80,
14511     minWidth: 200,
14512     minButtonWidth: 75,
14513     defaultButton: null,
14514     buttonAlign: "right",
14515     tabTag: 'div',
14516     firstShow: true,
14517
14518     /**
14519      * Sets the dialog title text
14520      * @param {String} text The title text to display
14521      * @return {Roo.BasicDialog} this
14522      */
14523     setTitle : function(text){
14524         this.header.update(text);
14525         return this;
14526     },
14527
14528     // private
14529     closeClick : function(){
14530         this.hide();
14531     },
14532
14533     // private
14534     collapseClick : function(){
14535         this[this.collapsed ? "expand" : "collapse"]();
14536     },
14537
14538     /**
14539      * Collapses the dialog to its minimized state (only the title bar is visible).
14540      * Equivalent to the user clicking the collapse dialog button.
14541      */
14542     collapse : function(){
14543         if(!this.collapsed){
14544             this.collapsed = true;
14545             this.el.addClass("x-dlg-collapsed");
14546             this.restoreHeight = this.el.getHeight();
14547             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14548         }
14549     },
14550
14551     /**
14552      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14553      * clicking the expand dialog button.
14554      */
14555     expand : function(){
14556         if(this.collapsed){
14557             this.collapsed = false;
14558             this.el.removeClass("x-dlg-collapsed");
14559             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14560         }
14561     },
14562
14563     /**
14564      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14565      * @return {Roo.TabPanel} The tabs component
14566      */
14567     initTabs : function(){
14568         var tabs = this.getTabs();
14569         while(tabs.getTab(0)){
14570             tabs.removeTab(0);
14571         }
14572         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14573             var dom = el.dom;
14574             tabs.addTab(Roo.id(dom), dom.title);
14575             dom.title = "";
14576         });
14577         tabs.activate(0);
14578         return tabs;
14579     },
14580
14581     // private
14582     beforeResize : function(){
14583         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14584     },
14585
14586     // private
14587     onResize : function(){
14588         this.refreshSize();
14589         this.syncBodyHeight();
14590         this.adjustAssets();
14591         this.focus();
14592         this.fireEvent("resize", this, this.size.width, this.size.height);
14593     },
14594
14595     // private
14596     onKeyDown : function(e){
14597         if(this.isVisible()){
14598             this.fireEvent("keydown", this, e);
14599         }
14600     },
14601
14602     /**
14603      * Resizes the dialog.
14604      * @param {Number} width
14605      * @param {Number} height
14606      * @return {Roo.BasicDialog} this
14607      */
14608     resizeTo : function(width, height){
14609         this.el.setSize(width, height);
14610         this.size = {width: width, height: height};
14611         this.syncBodyHeight();
14612         if(this.fixedcenter){
14613             this.center();
14614         }
14615         if(this.isVisible()){
14616             this.constrainXY();
14617             this.adjustAssets();
14618         }
14619         this.fireEvent("resize", this, width, height);
14620         return this;
14621     },
14622
14623
14624     /**
14625      * Resizes the dialog to fit the specified content size.
14626      * @param {Number} width
14627      * @param {Number} height
14628      * @return {Roo.BasicDialog} this
14629      */
14630     setContentSize : function(w, h){
14631         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14632         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14633         //if(!this.el.isBorderBox()){
14634             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14635             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14636         //}
14637         if(this.tabs){
14638             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14639             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14640         }
14641         this.resizeTo(w, h);
14642         return this;
14643     },
14644
14645     /**
14646      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14647      * executed in response to a particular key being pressed while the dialog is active.
14648      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14649      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14650      * @param {Function} fn The function to call
14651      * @param {Object} scope (optional) The scope of the function
14652      * @return {Roo.BasicDialog} this
14653      */
14654     addKeyListener : function(key, fn, scope){
14655         var keyCode, shift, ctrl, alt;
14656         if(typeof key == "object" && !(key instanceof Array)){
14657             keyCode = key["key"];
14658             shift = key["shift"];
14659             ctrl = key["ctrl"];
14660             alt = key["alt"];
14661         }else{
14662             keyCode = key;
14663         }
14664         var handler = function(dlg, e){
14665             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14666                 var k = e.getKey();
14667                 if(keyCode instanceof Array){
14668                     for(var i = 0, len = keyCode.length; i < len; i++){
14669                         if(keyCode[i] == k){
14670                           fn.call(scope || window, dlg, k, e);
14671                           return;
14672                         }
14673                     }
14674                 }else{
14675                     if(k == keyCode){
14676                         fn.call(scope || window, dlg, k, e);
14677                     }
14678                 }
14679             }
14680         };
14681         this.on("keydown", handler);
14682         return this;
14683     },
14684
14685     /**
14686      * Returns the TabPanel component (creates it if it doesn't exist).
14687      * Note: If you wish to simply check for the existence of tabs without creating them,
14688      * check for a null 'tabs' property.
14689      * @return {Roo.TabPanel} The tabs component
14690      */
14691     getTabs : function(){
14692         if(!this.tabs){
14693             this.el.addClass("x-dlg-auto-tabs");
14694             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14695             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14696         }
14697         return this.tabs;
14698     },
14699
14700     /**
14701      * Adds a button to the footer section of the dialog.
14702      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14703      * object or a valid Roo.DomHelper element config
14704      * @param {Function} handler The function called when the button is clicked
14705      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14706      * @return {Roo.Button} The new button
14707      */
14708     addButton : function(config, handler, scope){
14709         var dh = Roo.DomHelper;
14710         if(!this.footer){
14711             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14712         }
14713         if(!this.btnContainer){
14714             var tb = this.footer.createChild({
14715
14716                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14717                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14718             }, null, true);
14719             this.btnContainer = tb.firstChild.firstChild.firstChild;
14720         }
14721         var bconfig = {
14722             handler: handler,
14723             scope: scope,
14724             minWidth: this.minButtonWidth,
14725             hideParent:true
14726         };
14727         if(typeof config == "string"){
14728             bconfig.text = config;
14729         }else{
14730             if(config.tag){
14731                 bconfig.dhconfig = config;
14732             }else{
14733                 Roo.apply(bconfig, config);
14734             }
14735         }
14736         var fc = false;
14737         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14738             bconfig.position = Math.max(0, bconfig.position);
14739             fc = this.btnContainer.childNodes[bconfig.position];
14740         }
14741          
14742         var btn = new Roo.Button(
14743             fc ? 
14744                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14745                 : this.btnContainer.appendChild(document.createElement("td")),
14746             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14747             bconfig
14748         );
14749         this.syncBodyHeight();
14750         if(!this.buttons){
14751             /**
14752              * Array of all the buttons that have been added to this dialog via addButton
14753              * @type Array
14754              */
14755             this.buttons = [];
14756         }
14757         this.buttons.push(btn);
14758         return btn;
14759     },
14760
14761     /**
14762      * Sets the default button to be focused when the dialog is displayed.
14763      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14764      * @return {Roo.BasicDialog} this
14765      */
14766     setDefaultButton : function(btn){
14767         this.defaultButton = btn;
14768         return this;
14769     },
14770
14771     // private
14772     getHeaderFooterHeight : function(safe){
14773         var height = 0;
14774         if(this.header){
14775            height += this.header.getHeight();
14776         }
14777         if(this.footer){
14778            var fm = this.footer.getMargins();
14779             height += (this.footer.getHeight()+fm.top+fm.bottom);
14780         }
14781         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14782         height += this.centerBg.getPadding("tb");
14783         return height;
14784     },
14785
14786     // private
14787     syncBodyHeight : function(){
14788         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14789         var height = this.size.height - this.getHeaderFooterHeight(false);
14790         bd.setHeight(height-bd.getMargins("tb"));
14791         var hh = this.header.getHeight();
14792         var h = this.size.height-hh;
14793         cb.setHeight(h);
14794         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14795         bw.setHeight(h-cb.getPadding("tb"));
14796         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14797         bd.setWidth(bw.getWidth(true));
14798         if(this.tabs){
14799             this.tabs.syncHeight();
14800             if(Roo.isIE){
14801                 this.tabs.el.repaint();
14802             }
14803         }
14804     },
14805
14806     /**
14807      * Restores the previous state of the dialog if Roo.state is configured.
14808      * @return {Roo.BasicDialog} this
14809      */
14810     restoreState : function(){
14811         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14812         if(box && box.width){
14813             this.xy = [box.x, box.y];
14814             this.resizeTo(box.width, box.height);
14815         }
14816         return this;
14817     },
14818
14819     // private
14820     beforeShow : function(){
14821         this.expand();
14822         if(this.fixedcenter){
14823             this.xy = this.el.getCenterXY(true);
14824         }
14825         if(this.modal){
14826             Roo.get(document.body).addClass("x-body-masked");
14827             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14828             this.mask.show();
14829         }
14830         this.constrainXY();
14831     },
14832
14833     // private
14834     animShow : function(){
14835         var b = Roo.get(this.animateTarget).getBox();
14836         this.proxy.setSize(b.width, b.height);
14837         this.proxy.setLocation(b.x, b.y);
14838         this.proxy.show();
14839         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14840                     true, .35, this.showEl.createDelegate(this));
14841     },
14842
14843     /**
14844      * Shows the dialog.
14845      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14846      * @return {Roo.BasicDialog} this
14847      */
14848     show : function(animateTarget){
14849         if (this.fireEvent("beforeshow", this) === false){
14850             return;
14851         }
14852         if(this.syncHeightBeforeShow){
14853             this.syncBodyHeight();
14854         }else if(this.firstShow){
14855             this.firstShow = false;
14856             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14857         }
14858         this.animateTarget = animateTarget || this.animateTarget;
14859         if(!this.el.isVisible()){
14860             this.beforeShow();
14861             if(this.animateTarget && Roo.get(this.animateTarget)){
14862                 this.animShow();
14863             }else{
14864                 this.showEl();
14865             }
14866         }
14867         return this;
14868     },
14869
14870     // private
14871     showEl : function(){
14872         this.proxy.hide();
14873         this.el.setXY(this.xy);
14874         this.el.show();
14875         this.adjustAssets(true);
14876         this.toFront();
14877         this.focus();
14878         // IE peekaboo bug - fix found by Dave Fenwick
14879         if(Roo.isIE){
14880             this.el.repaint();
14881         }
14882         this.fireEvent("show", this);
14883     },
14884
14885     /**
14886      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14887      * dialog itself will receive focus.
14888      */
14889     focus : function(){
14890         if(this.defaultButton){
14891             this.defaultButton.focus();
14892         }else{
14893             this.focusEl.focus();
14894         }
14895     },
14896
14897     // private
14898     constrainXY : function(){
14899         if(this.constraintoviewport !== false){
14900             if(!this.viewSize){
14901                 if(this.container){
14902                     var s = this.container.getSize();
14903                     this.viewSize = [s.width, s.height];
14904                 }else{
14905                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14906                 }
14907             }
14908             var s = Roo.get(this.container||document).getScroll();
14909
14910             var x = this.xy[0], y = this.xy[1];
14911             var w = this.size.width, h = this.size.height;
14912             var vw = this.viewSize[0], vh = this.viewSize[1];
14913             // only move it if it needs it
14914             var moved = false;
14915             // first validate right/bottom
14916             if(x + w > vw+s.left){
14917                 x = vw - w;
14918                 moved = true;
14919             }
14920             if(y + h > vh+s.top){
14921                 y = vh - h;
14922                 moved = true;
14923             }
14924             // then make sure top/left isn't negative
14925             if(x < s.left){
14926                 x = s.left;
14927                 moved = true;
14928             }
14929             if(y < s.top){
14930                 y = s.top;
14931                 moved = true;
14932             }
14933             if(moved){
14934                 // cache xy
14935                 this.xy = [x, y];
14936                 if(this.isVisible()){
14937                     this.el.setLocation(x, y);
14938                     this.adjustAssets();
14939                 }
14940             }
14941         }
14942     },
14943
14944     // private
14945     onDrag : function(){
14946         if(!this.proxyDrag){
14947             this.xy = this.el.getXY();
14948             this.adjustAssets();
14949         }
14950     },
14951
14952     // private
14953     adjustAssets : function(doShow){
14954         var x = this.xy[0], y = this.xy[1];
14955         var w = this.size.width, h = this.size.height;
14956         if(doShow === true){
14957             if(this.shadow){
14958                 this.shadow.show(this.el);
14959             }
14960             if(this.shim){
14961                 this.shim.show();
14962             }
14963         }
14964         if(this.shadow && this.shadow.isVisible()){
14965             this.shadow.show(this.el);
14966         }
14967         if(this.shim && this.shim.isVisible()){
14968             this.shim.setBounds(x, y, w, h);
14969         }
14970     },
14971
14972     // private
14973     adjustViewport : function(w, h){
14974         if(!w || !h){
14975             w = Roo.lib.Dom.getViewWidth();
14976             h = Roo.lib.Dom.getViewHeight();
14977         }
14978         // cache the size
14979         this.viewSize = [w, h];
14980         if(this.modal && this.mask.isVisible()){
14981             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14982             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14983         }
14984         if(this.isVisible()){
14985             this.constrainXY();
14986         }
14987     },
14988
14989     /**
14990      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14991      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14992      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14993      */
14994     destroy : function(removeEl){
14995         if(this.isVisible()){
14996             this.animateTarget = null;
14997             this.hide();
14998         }
14999         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15000         if(this.tabs){
15001             this.tabs.destroy(removeEl);
15002         }
15003         Roo.destroy(
15004              this.shim,
15005              this.proxy,
15006              this.resizer,
15007              this.close,
15008              this.mask
15009         );
15010         if(this.dd){
15011             this.dd.unreg();
15012         }
15013         if(this.buttons){
15014            for(var i = 0, len = this.buttons.length; i < len; i++){
15015                this.buttons[i].destroy();
15016            }
15017         }
15018         this.el.removeAllListeners();
15019         if(removeEl === true){
15020             this.el.update("");
15021             this.el.remove();
15022         }
15023         Roo.DialogManager.unregister(this);
15024     },
15025
15026     // private
15027     startMove : function(){
15028         if(this.proxyDrag){
15029             this.proxy.show();
15030         }
15031         if(this.constraintoviewport !== false){
15032             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15033         }
15034     },
15035
15036     // private
15037     endMove : function(){
15038         if(!this.proxyDrag){
15039             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15040         }else{
15041             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15042             this.proxy.hide();
15043         }
15044         this.refreshSize();
15045         this.adjustAssets();
15046         this.focus();
15047         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15048     },
15049
15050     /**
15051      * Brings this dialog to the front of any other visible dialogs
15052      * @return {Roo.BasicDialog} this
15053      */
15054     toFront : function(){
15055         Roo.DialogManager.bringToFront(this);
15056         return this;
15057     },
15058
15059     /**
15060      * Sends this dialog to the back (under) of any other visible dialogs
15061      * @return {Roo.BasicDialog} this
15062      */
15063     toBack : function(){
15064         Roo.DialogManager.sendToBack(this);
15065         return this;
15066     },
15067
15068     /**
15069      * Centers this dialog in the viewport
15070      * @return {Roo.BasicDialog} this
15071      */
15072     center : function(){
15073         var xy = this.el.getCenterXY(true);
15074         this.moveTo(xy[0], xy[1]);
15075         return this;
15076     },
15077
15078     /**
15079      * Moves the dialog's top-left corner to the specified point
15080      * @param {Number} x
15081      * @param {Number} y
15082      * @return {Roo.BasicDialog} this
15083      */
15084     moveTo : function(x, y){
15085         this.xy = [x,y];
15086         if(this.isVisible()){
15087             this.el.setXY(this.xy);
15088             this.adjustAssets();
15089         }
15090         return this;
15091     },
15092
15093     /**
15094      * Aligns the dialog to the specified element
15095      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15096      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15097      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15098      * @return {Roo.BasicDialog} this
15099      */
15100     alignTo : function(element, position, offsets){
15101         this.xy = this.el.getAlignToXY(element, position, offsets);
15102         if(this.isVisible()){
15103             this.el.setXY(this.xy);
15104             this.adjustAssets();
15105         }
15106         return this;
15107     },
15108
15109     /**
15110      * Anchors an element to another element and realigns it when the window is resized.
15111      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15112      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15113      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15114      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15115      * is a number, it is used as the buffer delay (defaults to 50ms).
15116      * @return {Roo.BasicDialog} this
15117      */
15118     anchorTo : function(el, alignment, offsets, monitorScroll){
15119         var action = function(){
15120             this.alignTo(el, alignment, offsets);
15121         };
15122         Roo.EventManager.onWindowResize(action, this);
15123         var tm = typeof monitorScroll;
15124         if(tm != 'undefined'){
15125             Roo.EventManager.on(window, 'scroll', action, this,
15126                 {buffer: tm == 'number' ? monitorScroll : 50});
15127         }
15128         action.call(this);
15129         return this;
15130     },
15131
15132     /**
15133      * Returns true if the dialog is visible
15134      * @return {Boolean}
15135      */
15136     isVisible : function(){
15137         return this.el.isVisible();
15138     },
15139
15140     // private
15141     animHide : function(callback){
15142         var b = Roo.get(this.animateTarget).getBox();
15143         this.proxy.show();
15144         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15145         this.el.hide();
15146         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15147                     this.hideEl.createDelegate(this, [callback]));
15148     },
15149
15150     /**
15151      * Hides the dialog.
15152      * @param {Function} callback (optional) Function to call when the dialog is hidden
15153      * @return {Roo.BasicDialog} this
15154      */
15155     hide : function(callback){
15156         if (this.fireEvent("beforehide", this) === false){
15157             return;
15158         }
15159         if(this.shadow){
15160             this.shadow.hide();
15161         }
15162         if(this.shim) {
15163           this.shim.hide();
15164         }
15165         // sometimes animateTarget seems to get set.. causing problems...
15166         // this just double checks..
15167         if(this.animateTarget && Roo.get(this.animateTarget)) {
15168            this.animHide(callback);
15169         }else{
15170             this.el.hide();
15171             this.hideEl(callback);
15172         }
15173         return this;
15174     },
15175
15176     // private
15177     hideEl : function(callback){
15178         this.proxy.hide();
15179         if(this.modal){
15180             this.mask.hide();
15181             Roo.get(document.body).removeClass("x-body-masked");
15182         }
15183         this.fireEvent("hide", this);
15184         if(typeof callback == "function"){
15185             callback();
15186         }
15187     },
15188
15189     // private
15190     hideAction : function(){
15191         this.setLeft("-10000px");
15192         this.setTop("-10000px");
15193         this.setStyle("visibility", "hidden");
15194     },
15195
15196     // private
15197     refreshSize : function(){
15198         this.size = this.el.getSize();
15199         this.xy = this.el.getXY();
15200         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15201     },
15202
15203     // private
15204     // z-index is managed by the DialogManager and may be overwritten at any time
15205     setZIndex : function(index){
15206         if(this.modal){
15207             this.mask.setStyle("z-index", index);
15208         }
15209         if(this.shim){
15210             this.shim.setStyle("z-index", ++index);
15211         }
15212         if(this.shadow){
15213             this.shadow.setZIndex(++index);
15214         }
15215         this.el.setStyle("z-index", ++index);
15216         if(this.proxy){
15217             this.proxy.setStyle("z-index", ++index);
15218         }
15219         if(this.resizer){
15220             this.resizer.proxy.setStyle("z-index", ++index);
15221         }
15222
15223         this.lastZIndex = index;
15224     },
15225
15226     /**
15227      * Returns the element for this dialog
15228      * @return {Roo.Element} The underlying dialog Element
15229      */
15230     getEl : function(){
15231         return this.el;
15232     }
15233 });
15234
15235 /**
15236  * @class Roo.DialogManager
15237  * Provides global access to BasicDialogs that have been created and
15238  * support for z-indexing (layering) multiple open dialogs.
15239  */
15240 Roo.DialogManager = function(){
15241     var list = {};
15242     var accessList = [];
15243     var front = null;
15244
15245     // private
15246     var sortDialogs = function(d1, d2){
15247         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15248     };
15249
15250     // private
15251     var orderDialogs = function(){
15252         accessList.sort(sortDialogs);
15253         var seed = Roo.DialogManager.zseed;
15254         for(var i = 0, len = accessList.length; i < len; i++){
15255             var dlg = accessList[i];
15256             if(dlg){
15257                 dlg.setZIndex(seed + (i*10));
15258             }
15259         }
15260     };
15261
15262     return {
15263         /**
15264          * The starting z-index for BasicDialogs (defaults to 9000)
15265          * @type Number The z-index value
15266          */
15267         zseed : 9000,
15268
15269         // private
15270         register : function(dlg){
15271             list[dlg.id] = dlg;
15272             accessList.push(dlg);
15273         },
15274
15275         // private
15276         unregister : function(dlg){
15277             delete list[dlg.id];
15278             var i=0;
15279             var len=0;
15280             if(!accessList.indexOf){
15281                 for(  i = 0, len = accessList.length; i < len; i++){
15282                     if(accessList[i] == dlg){
15283                         accessList.splice(i, 1);
15284                         return;
15285                     }
15286                 }
15287             }else{
15288                  i = accessList.indexOf(dlg);
15289                 if(i != -1){
15290                     accessList.splice(i, 1);
15291                 }
15292             }
15293         },
15294
15295         /**
15296          * Gets a registered dialog by id
15297          * @param {String/Object} id The id of the dialog or a dialog
15298          * @return {Roo.BasicDialog} this
15299          */
15300         get : function(id){
15301             return typeof id == "object" ? id : list[id];
15302         },
15303
15304         /**
15305          * Brings the specified dialog to the front
15306          * @param {String/Object} dlg The id of the dialog or a dialog
15307          * @return {Roo.BasicDialog} this
15308          */
15309         bringToFront : function(dlg){
15310             dlg = this.get(dlg);
15311             if(dlg != front){
15312                 front = dlg;
15313                 dlg._lastAccess = new Date().getTime();
15314                 orderDialogs();
15315             }
15316             return dlg;
15317         },
15318
15319         /**
15320          * Sends the specified dialog to the back
15321          * @param {String/Object} dlg The id of the dialog or a dialog
15322          * @return {Roo.BasicDialog} this
15323          */
15324         sendToBack : function(dlg){
15325             dlg = this.get(dlg);
15326             dlg._lastAccess = -(new Date().getTime());
15327             orderDialogs();
15328             return dlg;
15329         },
15330
15331         /**
15332          * Hides all dialogs
15333          */
15334         hideAll : function(){
15335             for(var id in list){
15336                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15337                     list[id].hide();
15338                 }
15339             }
15340         }
15341     };
15342 }();
15343
15344 /**
15345  * @class Roo.LayoutDialog
15346  * @extends Roo.BasicDialog
15347  * Dialog which provides adjustments for working with a layout in a Dialog.
15348  * Add your necessary layout config options to the dialog's config.<br>
15349  * Example usage (including a nested layout):
15350  * <pre><code>
15351 if(!dialog){
15352     dialog = new Roo.LayoutDialog("download-dlg", {
15353         modal: true,
15354         width:600,
15355         height:450,
15356         shadow:true,
15357         minWidth:500,
15358         minHeight:350,
15359         autoTabs:true,
15360         proxyDrag:true,
15361         // layout config merges with the dialog config
15362         center:{
15363             tabPosition: "top",
15364             alwaysShowTabs: true
15365         }
15366     });
15367     dialog.addKeyListener(27, dialog.hide, dialog);
15368     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15369     dialog.addButton("Build It!", this.getDownload, this);
15370
15371     // we can even add nested layouts
15372     var innerLayout = new Roo.BorderLayout("dl-inner", {
15373         east: {
15374             initialSize: 200,
15375             autoScroll:true,
15376             split:true
15377         },
15378         center: {
15379             autoScroll:true
15380         }
15381     });
15382     innerLayout.beginUpdate();
15383     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15384     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15385     innerLayout.endUpdate(true);
15386
15387     var layout = dialog.getLayout();
15388     layout.beginUpdate();
15389     layout.add("center", new Roo.ContentPanel("standard-panel",
15390                         {title: "Download the Source", fitToFrame:true}));
15391     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15392                {title: "Build your own roo.js"}));
15393     layout.getRegion("center").showPanel(sp);
15394     layout.endUpdate();
15395 }
15396 </code></pre>
15397     * @constructor
15398     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15399     * @param {Object} config configuration options
15400   */
15401 Roo.LayoutDialog = function(el, cfg){
15402     
15403     var config=  cfg;
15404     if (typeof(cfg) == 'undefined') {
15405         config = Roo.apply({}, el);
15406         // not sure why we use documentElement here.. - it should always be body.
15407         // IE7 borks horribly if we use documentElement.
15408         // webkit also does not like documentElement - it creates a body element...
15409         el = Roo.get( document.body || document.documentElement ).createChild();
15410         //config.autoCreate = true;
15411     }
15412     
15413     
15414     config.autoTabs = false;
15415     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15416     this.body.setStyle({overflow:"hidden", position:"relative"});
15417     this.layout = new Roo.BorderLayout(this.body.dom, config);
15418     this.layout.monitorWindowResize = false;
15419     this.el.addClass("x-dlg-auto-layout");
15420     // fix case when center region overwrites center function
15421     this.center = Roo.BasicDialog.prototype.center;
15422     this.on("show", this.layout.layout, this.layout, true);
15423     if (config.items) {
15424         var xitems = config.items;
15425         delete config.items;
15426         Roo.each(xitems, this.addxtype, this);
15427     }
15428     
15429     
15430 };
15431 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15432     /**
15433      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15434      * @deprecated
15435      */
15436     endUpdate : function(){
15437         this.layout.endUpdate();
15438     },
15439
15440     /**
15441      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15442      *  @deprecated
15443      */
15444     beginUpdate : function(){
15445         this.layout.beginUpdate();
15446     },
15447
15448     /**
15449      * Get the BorderLayout for this dialog
15450      * @return {Roo.BorderLayout}
15451      */
15452     getLayout : function(){
15453         return this.layout;
15454     },
15455
15456     showEl : function(){
15457         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15458         if(Roo.isIE7){
15459             this.layout.layout();
15460         }
15461     },
15462
15463     // private
15464     // Use the syncHeightBeforeShow config option to control this automatically
15465     syncBodyHeight : function(){
15466         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15467         if(this.layout){this.layout.layout();}
15468     },
15469     
15470       /**
15471      * Add an xtype element (actually adds to the layout.)
15472      * @return {Object} xdata xtype object data.
15473      */
15474     
15475     addxtype : function(c) {
15476         return this.layout.addxtype(c);
15477     }
15478 });/*
15479  * Based on:
15480  * Ext JS Library 1.1.1
15481  * Copyright(c) 2006-2007, Ext JS, LLC.
15482  *
15483  * Originally Released Under LGPL - original licence link has changed is not relivant.
15484  *
15485  * Fork - LGPL
15486  * <script type="text/javascript">
15487  */
15488  
15489 /**
15490  * @class Roo.MessageBox
15491  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15492  * Example usage:
15493  *<pre><code>
15494 // Basic alert:
15495 Roo.Msg.alert('Status', 'Changes saved successfully.');
15496
15497 // Prompt for user data:
15498 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15499     if (btn == 'ok'){
15500         // process text value...
15501     }
15502 });
15503
15504 // Show a dialog using config options:
15505 Roo.Msg.show({
15506    title:'Save Changes?',
15507    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15508    buttons: Roo.Msg.YESNOCANCEL,
15509    fn: processResult,
15510    animEl: 'elId'
15511 });
15512 </code></pre>
15513  * @singleton
15514  */
15515 Roo.MessageBox = function(){
15516     var dlg, opt, mask, waitTimer;
15517     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15518     var buttons, activeTextEl, bwidth;
15519
15520     // private
15521     var handleButton = function(button){
15522         dlg.hide();
15523         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15524     };
15525
15526     // private
15527     var handleHide = function(){
15528         if(opt && opt.cls){
15529             dlg.el.removeClass(opt.cls);
15530         }
15531         if(waitTimer){
15532             Roo.TaskMgr.stop(waitTimer);
15533             waitTimer = null;
15534         }
15535     };
15536
15537     // private
15538     var updateButtons = function(b){
15539         var width = 0;
15540         if(!b){
15541             buttons["ok"].hide();
15542             buttons["cancel"].hide();
15543             buttons["yes"].hide();
15544             buttons["no"].hide();
15545             dlg.footer.dom.style.display = 'none';
15546             return width;
15547         }
15548         dlg.footer.dom.style.display = '';
15549         for(var k in buttons){
15550             if(typeof buttons[k] != "function"){
15551                 if(b[k]){
15552                     buttons[k].show();
15553                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15554                     width += buttons[k].el.getWidth()+15;
15555                 }else{
15556                     buttons[k].hide();
15557                 }
15558             }
15559         }
15560         return width;
15561     };
15562
15563     // private
15564     var handleEsc = function(d, k, e){
15565         if(opt && opt.closable !== false){
15566             dlg.hide();
15567         }
15568         if(e){
15569             e.stopEvent();
15570         }
15571     };
15572
15573     return {
15574         /**
15575          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15576          * @return {Roo.BasicDialog} The BasicDialog element
15577          */
15578         getDialog : function(){
15579            if(!dlg){
15580                 dlg = new Roo.BasicDialog("x-msg-box", {
15581                     autoCreate : true,
15582                     shadow: true,
15583                     draggable: true,
15584                     resizable:false,
15585                     constraintoviewport:false,
15586                     fixedcenter:true,
15587                     collapsible : false,
15588                     shim:true,
15589                     modal: true,
15590                     width:400, height:100,
15591                     buttonAlign:"center",
15592                     closeClick : function(){
15593                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15594                             handleButton("no");
15595                         }else{
15596                             handleButton("cancel");
15597                         }
15598                     }
15599                 });
15600                 dlg.on("hide", handleHide);
15601                 mask = dlg.mask;
15602                 dlg.addKeyListener(27, handleEsc);
15603                 buttons = {};
15604                 var bt = this.buttonText;
15605                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15606                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15607                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15608                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15609                 bodyEl = dlg.body.createChild({
15610
15611                     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>'
15612                 });
15613                 msgEl = bodyEl.dom.firstChild;
15614                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15615                 textboxEl.enableDisplayMode();
15616                 textboxEl.addKeyListener([10,13], function(){
15617                     if(dlg.isVisible() && opt && opt.buttons){
15618                         if(opt.buttons.ok){
15619                             handleButton("ok");
15620                         }else if(opt.buttons.yes){
15621                             handleButton("yes");
15622                         }
15623                     }
15624                 });
15625                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15626                 textareaEl.enableDisplayMode();
15627                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15628                 progressEl.enableDisplayMode();
15629                 var pf = progressEl.dom.firstChild;
15630                 if (pf) {
15631                     pp = Roo.get(pf.firstChild);
15632                     pp.setHeight(pf.offsetHeight);
15633                 }
15634                 
15635             }
15636             return dlg;
15637         },
15638
15639         /**
15640          * Updates the message box body text
15641          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15642          * the XHTML-compliant non-breaking space character '&amp;#160;')
15643          * @return {Roo.MessageBox} This message box
15644          */
15645         updateText : function(text){
15646             if(!dlg.isVisible() && !opt.width){
15647                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15648             }
15649             msgEl.innerHTML = text || '&#160;';
15650       
15651             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15652             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15653             var w = Math.max(
15654                     Math.min(opt.width || cw , this.maxWidth), 
15655                     Math.max(opt.minWidth || this.minWidth, bwidth)
15656             );
15657             if(opt.prompt){
15658                 activeTextEl.setWidth(w);
15659             }
15660             if(dlg.isVisible()){
15661                 dlg.fixedcenter = false;
15662             }
15663             // to big, make it scroll. = But as usual stupid IE does not support
15664             // !important..
15665             
15666             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15667                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15668                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15669             } else {
15670                 bodyEl.dom.style.height = '';
15671                 bodyEl.dom.style.overflowY = '';
15672             }
15673             if (cw > w) {
15674                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15675             } else {
15676                 bodyEl.dom.style.overflowX = '';
15677             }
15678             
15679             dlg.setContentSize(w, bodyEl.getHeight());
15680             if(dlg.isVisible()){
15681                 dlg.fixedcenter = true;
15682             }
15683             return this;
15684         },
15685
15686         /**
15687          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15688          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15689          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15690          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15691          * @return {Roo.MessageBox} This message box
15692          */
15693         updateProgress : function(value, text){
15694             if(text){
15695                 this.updateText(text);
15696             }
15697             if (pp) { // weird bug on my firefox - for some reason this is not defined
15698                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15699             }
15700             return this;
15701         },        
15702
15703         /**
15704          * Returns true if the message box is currently displayed
15705          * @return {Boolean} True if the message box is visible, else false
15706          */
15707         isVisible : function(){
15708             return dlg && dlg.isVisible();  
15709         },
15710
15711         /**
15712          * Hides the message box if it is displayed
15713          */
15714         hide : function(){
15715             if(this.isVisible()){
15716                 dlg.hide();
15717             }  
15718         },
15719
15720         /**
15721          * Displays a new message box, or reinitializes an existing message box, based on the config options
15722          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15723          * The following config object properties are supported:
15724          * <pre>
15725 Property    Type             Description
15726 ----------  ---------------  ------------------------------------------------------------------------------------
15727 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15728                                    closes (defaults to undefined)
15729 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15730                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15731 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15732                                    progress and wait dialogs will ignore this property and always hide the
15733                                    close button as they can only be closed programmatically.
15734 cls               String           A custom CSS class to apply to the message box element
15735 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15736                                    displayed (defaults to 75)
15737 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15738                                    function will be btn (the name of the button that was clicked, if applicable,
15739                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15740                                    Progress and wait dialogs will ignore this option since they do not respond to
15741                                    user actions and can only be closed programmatically, so any required function
15742                                    should be called by the same code after it closes the dialog.
15743 icon              String           A CSS class that provides a background image to be used as an icon for
15744                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15745 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15746 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15747 modal             Boolean          False to allow user interaction with the page while the message box is
15748                                    displayed (defaults to true)
15749 msg               String           A string that will replace the existing message box body text (defaults
15750                                    to the XHTML-compliant non-breaking space character '&#160;')
15751 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15752 progress          Boolean          True to display a progress bar (defaults to false)
15753 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15754 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15755 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15756 title             String           The title text
15757 value             String           The string value to set into the active textbox element if displayed
15758 wait              Boolean          True to display a progress bar (defaults to false)
15759 width             Number           The width of the dialog in pixels
15760 </pre>
15761          *
15762          * Example usage:
15763          * <pre><code>
15764 Roo.Msg.show({
15765    title: 'Address',
15766    msg: 'Please enter your address:',
15767    width: 300,
15768    buttons: Roo.MessageBox.OKCANCEL,
15769    multiline: true,
15770    fn: saveAddress,
15771    animEl: 'addAddressBtn'
15772 });
15773 </code></pre>
15774          * @param {Object} config Configuration options
15775          * @return {Roo.MessageBox} This message box
15776          */
15777         show : function(options)
15778         {
15779             
15780             // this causes nightmares if you show one dialog after another
15781             // especially on callbacks..
15782              
15783             if(this.isVisible()){
15784                 
15785                 this.hide();
15786                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15787                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15788                 Roo.log("New Dialog Message:" +  options.msg )
15789                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15790                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15791                 
15792             }
15793             var d = this.getDialog();
15794             opt = options;
15795             d.setTitle(opt.title || "&#160;");
15796             d.close.setDisplayed(opt.closable !== false);
15797             activeTextEl = textboxEl;
15798             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15799             if(opt.prompt){
15800                 if(opt.multiline){
15801                     textboxEl.hide();
15802                     textareaEl.show();
15803                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15804                         opt.multiline : this.defaultTextHeight);
15805                     activeTextEl = textareaEl;
15806                 }else{
15807                     textboxEl.show();
15808                     textareaEl.hide();
15809                 }
15810             }else{
15811                 textboxEl.hide();
15812                 textareaEl.hide();
15813             }
15814             progressEl.setDisplayed(opt.progress === true);
15815             this.updateProgress(0);
15816             activeTextEl.dom.value = opt.value || "";
15817             if(opt.prompt){
15818                 dlg.setDefaultButton(activeTextEl);
15819             }else{
15820                 var bs = opt.buttons;
15821                 var db = null;
15822                 if(bs && bs.ok){
15823                     db = buttons["ok"];
15824                 }else if(bs && bs.yes){
15825                     db = buttons["yes"];
15826                 }
15827                 dlg.setDefaultButton(db);
15828             }
15829             bwidth = updateButtons(opt.buttons);
15830             this.updateText(opt.msg);
15831             if(opt.cls){
15832                 d.el.addClass(opt.cls);
15833             }
15834             d.proxyDrag = opt.proxyDrag === true;
15835             d.modal = opt.modal !== false;
15836             d.mask = opt.modal !== false ? mask : false;
15837             if(!d.isVisible()){
15838                 // force it to the end of the z-index stack so it gets a cursor in FF
15839                 document.body.appendChild(dlg.el.dom);
15840                 d.animateTarget = null;
15841                 d.show(options.animEl);
15842             }
15843             return this;
15844         },
15845
15846         /**
15847          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15848          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15849          * and closing the message box when the process is complete.
15850          * @param {String} title The title bar text
15851          * @param {String} msg The message box body text
15852          * @return {Roo.MessageBox} This message box
15853          */
15854         progress : function(title, msg){
15855             this.show({
15856                 title : title,
15857                 msg : msg,
15858                 buttons: false,
15859                 progress:true,
15860                 closable:false,
15861                 minWidth: this.minProgressWidth,
15862                 modal : true
15863             });
15864             return this;
15865         },
15866
15867         /**
15868          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15869          * If a callback function is passed it will be called after the user clicks the button, and the
15870          * id of the button that was clicked will be passed as the only parameter to the callback
15871          * (could also be the top-right close button).
15872          * @param {String} title The title bar text
15873          * @param {String} msg The message box body text
15874          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15875          * @param {Object} scope (optional) The scope of the callback function
15876          * @return {Roo.MessageBox} This message box
15877          */
15878         alert : function(title, msg, fn, scope){
15879             this.show({
15880                 title : title,
15881                 msg : msg,
15882                 buttons: this.OK,
15883                 fn: fn,
15884                 scope : scope,
15885                 modal : true
15886             });
15887             return this;
15888         },
15889
15890         /**
15891          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15892          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15893          * You are responsible for closing the message box when the process is complete.
15894          * @param {String} msg The message box body text
15895          * @param {String} title (optional) The title bar text
15896          * @return {Roo.MessageBox} This message box
15897          */
15898         wait : function(msg, title){
15899             this.show({
15900                 title : title,
15901                 msg : msg,
15902                 buttons: false,
15903                 closable:false,
15904                 progress:true,
15905                 modal:true,
15906                 width:300,
15907                 wait:true
15908             });
15909             waitTimer = Roo.TaskMgr.start({
15910                 run: function(i){
15911                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15912                 },
15913                 interval: 1000
15914             });
15915             return this;
15916         },
15917
15918         /**
15919          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15920          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15921          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15922          * @param {String} title The title bar text
15923          * @param {String} msg The message box body text
15924          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15925          * @param {Object} scope (optional) The scope of the callback function
15926          * @return {Roo.MessageBox} This message box
15927          */
15928         confirm : function(title, msg, fn, scope){
15929             this.show({
15930                 title : title,
15931                 msg : msg,
15932                 buttons: this.YESNO,
15933                 fn: fn,
15934                 scope : scope,
15935                 modal : true
15936             });
15937             return this;
15938         },
15939
15940         /**
15941          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15942          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15943          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15944          * (could also be the top-right close button) and the text that was entered will be passed as the two
15945          * parameters to the callback.
15946          * @param {String} title The title bar text
15947          * @param {String} msg The message box body text
15948          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15949          * @param {Object} scope (optional) The scope of the callback function
15950          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15951          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15952          * @return {Roo.MessageBox} This message box
15953          */
15954         prompt : function(title, msg, fn, scope, multiline){
15955             this.show({
15956                 title : title,
15957                 msg : msg,
15958                 buttons: this.OKCANCEL,
15959                 fn: fn,
15960                 minWidth:250,
15961                 scope : scope,
15962                 prompt:true,
15963                 multiline: multiline,
15964                 modal : true
15965             });
15966             return this;
15967         },
15968
15969         /**
15970          * Button config that displays a single OK button
15971          * @type Object
15972          */
15973         OK : {ok:true},
15974         /**
15975          * Button config that displays Yes and No buttons
15976          * @type Object
15977          */
15978         YESNO : {yes:true, no:true},
15979         /**
15980          * Button config that displays OK and Cancel buttons
15981          * @type Object
15982          */
15983         OKCANCEL : {ok:true, cancel:true},
15984         /**
15985          * Button config that displays Yes, No and Cancel buttons
15986          * @type Object
15987          */
15988         YESNOCANCEL : {yes:true, no:true, cancel:true},
15989
15990         /**
15991          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15992          * @type Number
15993          */
15994         defaultTextHeight : 75,
15995         /**
15996          * The maximum width in pixels of the message box (defaults to 600)
15997          * @type Number
15998          */
15999         maxWidth : 600,
16000         /**
16001          * The minimum width in pixels of the message box (defaults to 100)
16002          * @type Number
16003          */
16004         minWidth : 100,
16005         /**
16006          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16007          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16008          * @type Number
16009          */
16010         minProgressWidth : 250,
16011         /**
16012          * An object containing the default button text strings that can be overriden for localized language support.
16013          * Supported properties are: ok, cancel, yes and no.
16014          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16015          * @type Object
16016          */
16017         buttonText : {
16018             ok : "OK",
16019             cancel : "Cancel",
16020             yes : "Yes",
16021             no : "No"
16022         }
16023     };
16024 }();
16025
16026 /**
16027  * Shorthand for {@link Roo.MessageBox}
16028  */
16029 Roo.Msg = Roo.MessageBox;/*
16030  * Based on:
16031  * Ext JS Library 1.1.1
16032  * Copyright(c) 2006-2007, Ext JS, LLC.
16033  *
16034  * Originally Released Under LGPL - original licence link has changed is not relivant.
16035  *
16036  * Fork - LGPL
16037  * <script type="text/javascript">
16038  */
16039 /**
16040  * @class Roo.QuickTips
16041  * Provides attractive and customizable tooltips for any element.
16042  * @singleton
16043  */
16044 Roo.QuickTips = function(){
16045     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16046     var ce, bd, xy, dd;
16047     var visible = false, disabled = true, inited = false;
16048     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16049     
16050     var onOver = function(e){
16051         if(disabled){
16052             return;
16053         }
16054         var t = e.getTarget();
16055         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16056             return;
16057         }
16058         if(ce && t == ce.el){
16059             clearTimeout(hideProc);
16060             return;
16061         }
16062         if(t && tagEls[t.id]){
16063             tagEls[t.id].el = t;
16064             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16065             return;
16066         }
16067         var ttp, et = Roo.fly(t);
16068         var ns = cfg.namespace;
16069         if(tm.interceptTitles && t.title){
16070             ttp = t.title;
16071             t.qtip = ttp;
16072             t.removeAttribute("title");
16073             e.preventDefault();
16074         }else{
16075             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16076         }
16077         if(ttp){
16078             showProc = show.defer(tm.showDelay, tm, [{
16079                 el: t, 
16080                 text: ttp, 
16081                 width: et.getAttributeNS(ns, cfg.width),
16082                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16083                 title: et.getAttributeNS(ns, cfg.title),
16084                     cls: et.getAttributeNS(ns, cfg.cls)
16085             }]);
16086         }
16087     };
16088     
16089     var onOut = function(e){
16090         clearTimeout(showProc);
16091         var t = e.getTarget();
16092         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16093             hideProc = setTimeout(hide, tm.hideDelay);
16094         }
16095     };
16096     
16097     var onMove = function(e){
16098         if(disabled){
16099             return;
16100         }
16101         xy = e.getXY();
16102         xy[1] += 18;
16103         if(tm.trackMouse && ce){
16104             el.setXY(xy);
16105         }
16106     };
16107     
16108     var onDown = function(e){
16109         clearTimeout(showProc);
16110         clearTimeout(hideProc);
16111         if(!e.within(el)){
16112             if(tm.hideOnClick){
16113                 hide();
16114                 tm.disable();
16115                 tm.enable.defer(100, tm);
16116             }
16117         }
16118     };
16119     
16120     var getPad = function(){
16121         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16122     };
16123
16124     var show = function(o){
16125         if(disabled){
16126             return;
16127         }
16128         clearTimeout(dismissProc);
16129         ce = o;
16130         if(removeCls){ // in case manually hidden
16131             el.removeClass(removeCls);
16132             removeCls = null;
16133         }
16134         if(ce.cls){
16135             el.addClass(ce.cls);
16136             removeCls = ce.cls;
16137         }
16138         if(ce.title){
16139             tipTitle.update(ce.title);
16140             tipTitle.show();
16141         }else{
16142             tipTitle.update('');
16143             tipTitle.hide();
16144         }
16145         el.dom.style.width  = tm.maxWidth+'px';
16146         //tipBody.dom.style.width = '';
16147         tipBodyText.update(o.text);
16148         var p = getPad(), w = ce.width;
16149         if(!w){
16150             var td = tipBodyText.dom;
16151             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16152             if(aw > tm.maxWidth){
16153                 w = tm.maxWidth;
16154             }else if(aw < tm.minWidth){
16155                 w = tm.minWidth;
16156             }else{
16157                 w = aw;
16158             }
16159         }
16160         //tipBody.setWidth(w);
16161         el.setWidth(parseInt(w, 10) + p);
16162         if(ce.autoHide === false){
16163             close.setDisplayed(true);
16164             if(dd){
16165                 dd.unlock();
16166             }
16167         }else{
16168             close.setDisplayed(false);
16169             if(dd){
16170                 dd.lock();
16171             }
16172         }
16173         if(xy){
16174             el.avoidY = xy[1]-18;
16175             el.setXY(xy);
16176         }
16177         if(tm.animate){
16178             el.setOpacity(.1);
16179             el.setStyle("visibility", "visible");
16180             el.fadeIn({callback: afterShow});
16181         }else{
16182             afterShow();
16183         }
16184     };
16185     
16186     var afterShow = function(){
16187         if(ce){
16188             el.show();
16189             esc.enable();
16190             if(tm.autoDismiss && ce.autoHide !== false){
16191                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16192             }
16193         }
16194     };
16195     
16196     var hide = function(noanim){
16197         clearTimeout(dismissProc);
16198         clearTimeout(hideProc);
16199         ce = null;
16200         if(el.isVisible()){
16201             esc.disable();
16202             if(noanim !== true && tm.animate){
16203                 el.fadeOut({callback: afterHide});
16204             }else{
16205                 afterHide();
16206             } 
16207         }
16208     };
16209     
16210     var afterHide = function(){
16211         el.hide();
16212         if(removeCls){
16213             el.removeClass(removeCls);
16214             removeCls = null;
16215         }
16216     };
16217     
16218     return {
16219         /**
16220         * @cfg {Number} minWidth
16221         * The minimum width of the quick tip (defaults to 40)
16222         */
16223        minWidth : 40,
16224         /**
16225         * @cfg {Number} maxWidth
16226         * The maximum width of the quick tip (defaults to 300)
16227         */
16228        maxWidth : 300,
16229         /**
16230         * @cfg {Boolean} interceptTitles
16231         * True to automatically use the element's DOM title value if available (defaults to false)
16232         */
16233        interceptTitles : false,
16234         /**
16235         * @cfg {Boolean} trackMouse
16236         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16237         */
16238        trackMouse : false,
16239         /**
16240         * @cfg {Boolean} hideOnClick
16241         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16242         */
16243        hideOnClick : true,
16244         /**
16245         * @cfg {Number} showDelay
16246         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16247         */
16248        showDelay : 500,
16249         /**
16250         * @cfg {Number} hideDelay
16251         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16252         */
16253        hideDelay : 200,
16254         /**
16255         * @cfg {Boolean} autoHide
16256         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16257         * Used in conjunction with hideDelay.
16258         */
16259        autoHide : true,
16260         /**
16261         * @cfg {Boolean}
16262         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16263         * (defaults to true).  Used in conjunction with autoDismissDelay.
16264         */
16265        autoDismiss : true,
16266         /**
16267         * @cfg {Number}
16268         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16269         */
16270        autoDismissDelay : 5000,
16271        /**
16272         * @cfg {Boolean} animate
16273         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16274         */
16275        animate : false,
16276
16277        /**
16278         * @cfg {String} title
16279         * Title text to display (defaults to '').  This can be any valid HTML markup.
16280         */
16281         title: '',
16282        /**
16283         * @cfg {String} text
16284         * Body text to display (defaults to '').  This can be any valid HTML markup.
16285         */
16286         text : '',
16287        /**
16288         * @cfg {String} cls
16289         * A CSS class to apply to the base quick tip element (defaults to '').
16290         */
16291         cls : '',
16292        /**
16293         * @cfg {Number} width
16294         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16295         * minWidth or maxWidth.
16296         */
16297         width : null,
16298
16299     /**
16300      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16301      * or display QuickTips in a page.
16302      */
16303        init : function(){
16304           tm = Roo.QuickTips;
16305           cfg = tm.tagConfig;
16306           if(!inited){
16307               if(!Roo.isReady){ // allow calling of init() before onReady
16308                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16309                   return;
16310               }
16311               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16312               el.fxDefaults = {stopFx: true};
16313               // maximum custom styling
16314               //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>');
16315               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>');              
16316               tipTitle = el.child('h3');
16317               tipTitle.enableDisplayMode("block");
16318               tipBody = el.child('div.x-tip-bd');
16319               tipBodyText = el.child('div.x-tip-bd-inner');
16320               //bdLeft = el.child('div.x-tip-bd-left');
16321               //bdRight = el.child('div.x-tip-bd-right');
16322               close = el.child('div.x-tip-close');
16323               close.enableDisplayMode("block");
16324               close.on("click", hide);
16325               var d = Roo.get(document);
16326               d.on("mousedown", onDown);
16327               d.on("mouseover", onOver);
16328               d.on("mouseout", onOut);
16329               d.on("mousemove", onMove);
16330               esc = d.addKeyListener(27, hide);
16331               esc.disable();
16332               if(Roo.dd.DD){
16333                   dd = el.initDD("default", null, {
16334                       onDrag : function(){
16335                           el.sync();  
16336                       }
16337                   });
16338                   dd.setHandleElId(tipTitle.id);
16339                   dd.lock();
16340               }
16341               inited = true;
16342           }
16343           this.enable(); 
16344        },
16345
16346     /**
16347      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16348      * are supported:
16349      * <pre>
16350 Property    Type                   Description
16351 ----------  ---------------------  ------------------------------------------------------------------------
16352 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16353      * </ul>
16354      * @param {Object} config The config object
16355      */
16356        register : function(config){
16357            var cs = config instanceof Array ? config : arguments;
16358            for(var i = 0, len = cs.length; i < len; i++) {
16359                var c = cs[i];
16360                var target = c.target;
16361                if(target){
16362                    if(target instanceof Array){
16363                        for(var j = 0, jlen = target.length; j < jlen; j++){
16364                            tagEls[target[j]] = c;
16365                        }
16366                    }else{
16367                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16368                    }
16369                }
16370            }
16371        },
16372
16373     /**
16374      * Removes this quick tip from its element and destroys it.
16375      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16376      */
16377        unregister : function(el){
16378            delete tagEls[Roo.id(el)];
16379        },
16380
16381     /**
16382      * Enable this quick tip.
16383      */
16384        enable : function(){
16385            if(inited && disabled){
16386                locks.pop();
16387                if(locks.length < 1){
16388                    disabled = false;
16389                }
16390            }
16391        },
16392
16393     /**
16394      * Disable this quick tip.
16395      */
16396        disable : function(){
16397           disabled = true;
16398           clearTimeout(showProc);
16399           clearTimeout(hideProc);
16400           clearTimeout(dismissProc);
16401           if(ce){
16402               hide(true);
16403           }
16404           locks.push(1);
16405        },
16406
16407     /**
16408      * Returns true if the quick tip is enabled, else false.
16409      */
16410        isEnabled : function(){
16411             return !disabled;
16412        },
16413
16414         // private
16415        tagConfig : {
16416            namespace : "ext",
16417            attribute : "qtip",
16418            width : "width",
16419            target : "target",
16420            title : "qtitle",
16421            hide : "hide",
16422            cls : "qclass"
16423        }
16424    };
16425 }();
16426
16427 // backwards compat
16428 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16429  * Based on:
16430  * Ext JS Library 1.1.1
16431  * Copyright(c) 2006-2007, Ext JS, LLC.
16432  *
16433  * Originally Released Under LGPL - original licence link has changed is not relivant.
16434  *
16435  * Fork - LGPL
16436  * <script type="text/javascript">
16437  */
16438  
16439
16440 /**
16441  * @class Roo.tree.TreePanel
16442  * @extends Roo.data.Tree
16443
16444  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16445  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16446  * @cfg {Boolean} enableDD true to enable drag and drop
16447  * @cfg {Boolean} enableDrag true to enable just drag
16448  * @cfg {Boolean} enableDrop true to enable just drop
16449  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16450  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16451  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16452  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16453  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16454  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16455  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16456  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16457  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16458  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16459  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16460  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16461  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16462  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16463  * @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>
16464  * @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>
16465  * 
16466  * @constructor
16467  * @param {String/HTMLElement/Element} el The container element
16468  * @param {Object} config
16469  */
16470 Roo.tree.TreePanel = function(el, config){
16471     var root = false;
16472     var loader = false;
16473     if (config.root) {
16474         root = config.root;
16475         delete config.root;
16476     }
16477     if (config.loader) {
16478         loader = config.loader;
16479         delete config.loader;
16480     }
16481     
16482     Roo.apply(this, config);
16483     Roo.tree.TreePanel.superclass.constructor.call(this);
16484     this.el = Roo.get(el);
16485     this.el.addClass('x-tree');
16486     //console.log(root);
16487     if (root) {
16488         this.setRootNode( Roo.factory(root, Roo.tree));
16489     }
16490     if (loader) {
16491         this.loader = Roo.factory(loader, Roo.tree);
16492     }
16493    /**
16494     * Read-only. The id of the container element becomes this TreePanel's id.
16495     */
16496     this.id = this.el.id;
16497     this.addEvents({
16498         /**
16499         * @event beforeload
16500         * Fires before a node is loaded, return false to cancel
16501         * @param {Node} node The node being loaded
16502         */
16503         "beforeload" : true,
16504         /**
16505         * @event load
16506         * Fires when a node is loaded
16507         * @param {Node} node The node that was loaded
16508         */
16509         "load" : true,
16510         /**
16511         * @event textchange
16512         * Fires when the text for a node is changed
16513         * @param {Node} node The node
16514         * @param {String} text The new text
16515         * @param {String} oldText The old text
16516         */
16517         "textchange" : true,
16518         /**
16519         * @event beforeexpand
16520         * Fires before a node is expanded, return false to cancel.
16521         * @param {Node} node The node
16522         * @param {Boolean} deep
16523         * @param {Boolean} anim
16524         */
16525         "beforeexpand" : true,
16526         /**
16527         * @event beforecollapse
16528         * Fires before a node is collapsed, return false to cancel.
16529         * @param {Node} node The node
16530         * @param {Boolean} deep
16531         * @param {Boolean} anim
16532         */
16533         "beforecollapse" : true,
16534         /**
16535         * @event expand
16536         * Fires when a node is expanded
16537         * @param {Node} node The node
16538         */
16539         "expand" : true,
16540         /**
16541         * @event disabledchange
16542         * Fires when the disabled status of a node changes
16543         * @param {Node} node The node
16544         * @param {Boolean} disabled
16545         */
16546         "disabledchange" : true,
16547         /**
16548         * @event collapse
16549         * Fires when a node is collapsed
16550         * @param {Node} node The node
16551         */
16552         "collapse" : true,
16553         /**
16554         * @event beforeclick
16555         * Fires before click processing on a node. Return false to cancel the default action.
16556         * @param {Node} node The node
16557         * @param {Roo.EventObject} e The event object
16558         */
16559         "beforeclick":true,
16560         /**
16561         * @event checkchange
16562         * Fires when a node with a checkbox's checked property changes
16563         * @param {Node} this This node
16564         * @param {Boolean} checked
16565         */
16566         "checkchange":true,
16567         /**
16568         * @event click
16569         * Fires when a node is clicked
16570         * @param {Node} node The node
16571         * @param {Roo.EventObject} e The event object
16572         */
16573         "click":true,
16574         /**
16575         * @event dblclick
16576         * Fires when a node is double clicked
16577         * @param {Node} node The node
16578         * @param {Roo.EventObject} e The event object
16579         */
16580         "dblclick":true,
16581         /**
16582         * @event contextmenu
16583         * Fires when a node is right clicked
16584         * @param {Node} node The node
16585         * @param {Roo.EventObject} e The event object
16586         */
16587         "contextmenu":true,
16588         /**
16589         * @event beforechildrenrendered
16590         * Fires right before the child nodes for a node are rendered
16591         * @param {Node} node The node
16592         */
16593         "beforechildrenrendered":true,
16594         /**
16595         * @event startdrag
16596         * Fires when a node starts being dragged
16597         * @param {Roo.tree.TreePanel} this
16598         * @param {Roo.tree.TreeNode} node
16599         * @param {event} e The raw browser event
16600         */ 
16601        "startdrag" : true,
16602        /**
16603         * @event enddrag
16604         * Fires when a drag operation is complete
16605         * @param {Roo.tree.TreePanel} this
16606         * @param {Roo.tree.TreeNode} node
16607         * @param {event} e The raw browser event
16608         */
16609        "enddrag" : true,
16610        /**
16611         * @event dragdrop
16612         * Fires when a dragged node is dropped on a valid DD target
16613         * @param {Roo.tree.TreePanel} this
16614         * @param {Roo.tree.TreeNode} node
16615         * @param {DD} dd The dd it was dropped on
16616         * @param {event} e The raw browser event
16617         */
16618        "dragdrop" : true,
16619        /**
16620         * @event beforenodedrop
16621         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16622         * passed to handlers has the following properties:<br />
16623         * <ul style="padding:5px;padding-left:16px;">
16624         * <li>tree - The TreePanel</li>
16625         * <li>target - The node being targeted for the drop</li>
16626         * <li>data - The drag data from the drag source</li>
16627         * <li>point - The point of the drop - append, above or below</li>
16628         * <li>source - The drag source</li>
16629         * <li>rawEvent - Raw mouse event</li>
16630         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16631         * to be inserted by setting them on this object.</li>
16632         * <li>cancel - Set this to true to cancel the drop.</li>
16633         * </ul>
16634         * @param {Object} dropEvent
16635         */
16636        "beforenodedrop" : true,
16637        /**
16638         * @event nodedrop
16639         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16640         * passed to handlers has the following properties:<br />
16641         * <ul style="padding:5px;padding-left:16px;">
16642         * <li>tree - The TreePanel</li>
16643         * <li>target - The node being targeted for the drop</li>
16644         * <li>data - The drag data from the drag source</li>
16645         * <li>point - The point of the drop - append, above or below</li>
16646         * <li>source - The drag source</li>
16647         * <li>rawEvent - Raw mouse event</li>
16648         * <li>dropNode - Dropped node(s).</li>
16649         * </ul>
16650         * @param {Object} dropEvent
16651         */
16652        "nodedrop" : true,
16653         /**
16654         * @event nodedragover
16655         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16656         * passed to handlers has the following properties:<br />
16657         * <ul style="padding:5px;padding-left:16px;">
16658         * <li>tree - The TreePanel</li>
16659         * <li>target - The node being targeted for the drop</li>
16660         * <li>data - The drag data from the drag source</li>
16661         * <li>point - The point of the drop - append, above or below</li>
16662         * <li>source - The drag source</li>
16663         * <li>rawEvent - Raw mouse event</li>
16664         * <li>dropNode - Drop node(s) provided by the source.</li>
16665         * <li>cancel - Set this to true to signal drop not allowed.</li>
16666         * </ul>
16667         * @param {Object} dragOverEvent
16668         */
16669        "nodedragover" : true
16670         
16671     });
16672     if(this.singleExpand){
16673        this.on("beforeexpand", this.restrictExpand, this);
16674     }
16675     if (this.editor) {
16676         this.editor.tree = this;
16677         this.editor = Roo.factory(this.editor, Roo.tree);
16678     }
16679     
16680     if (this.selModel) {
16681         this.selModel = Roo.factory(this.selModel, Roo.tree);
16682     }
16683    
16684 };
16685 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16686     rootVisible : true,
16687     animate: Roo.enableFx,
16688     lines : true,
16689     enableDD : false,
16690     hlDrop : Roo.enableFx,
16691   
16692     renderer: false,
16693     
16694     rendererTip: false,
16695     // private
16696     restrictExpand : function(node){
16697         var p = node.parentNode;
16698         if(p){
16699             if(p.expandedChild && p.expandedChild.parentNode == p){
16700                 p.expandedChild.collapse();
16701             }
16702             p.expandedChild = node;
16703         }
16704     },
16705
16706     // private override
16707     setRootNode : function(node){
16708         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16709         if(!this.rootVisible){
16710             node.ui = new Roo.tree.RootTreeNodeUI(node);
16711         }
16712         return node;
16713     },
16714
16715     /**
16716      * Returns the container element for this TreePanel
16717      */
16718     getEl : function(){
16719         return this.el;
16720     },
16721
16722     /**
16723      * Returns the default TreeLoader for this TreePanel
16724      */
16725     getLoader : function(){
16726         return this.loader;
16727     },
16728
16729     /**
16730      * Expand all nodes
16731      */
16732     expandAll : function(){
16733         this.root.expand(true);
16734     },
16735
16736     /**
16737      * Collapse all nodes
16738      */
16739     collapseAll : function(){
16740         this.root.collapse(true);
16741     },
16742
16743     /**
16744      * Returns the selection model used by this TreePanel
16745      */
16746     getSelectionModel : function(){
16747         if(!this.selModel){
16748             this.selModel = new Roo.tree.DefaultSelectionModel();
16749         }
16750         return this.selModel;
16751     },
16752
16753     /**
16754      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16755      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16756      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16757      * @return {Array}
16758      */
16759     getChecked : function(a, startNode){
16760         startNode = startNode || this.root;
16761         var r = [];
16762         var f = function(){
16763             if(this.attributes.checked){
16764                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16765             }
16766         }
16767         startNode.cascade(f);
16768         return r;
16769     },
16770
16771     /**
16772      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16773      * @param {String} path
16774      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16775      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16776      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16777      */
16778     expandPath : function(path, attr, callback){
16779         attr = attr || "id";
16780         var keys = path.split(this.pathSeparator);
16781         var curNode = this.root;
16782         if(curNode.attributes[attr] != keys[1]){ // invalid root
16783             if(callback){
16784                 callback(false, null);
16785             }
16786             return;
16787         }
16788         var index = 1;
16789         var f = function(){
16790             if(++index == keys.length){
16791                 if(callback){
16792                     callback(true, curNode);
16793                 }
16794                 return;
16795             }
16796             var c = curNode.findChild(attr, keys[index]);
16797             if(!c){
16798                 if(callback){
16799                     callback(false, curNode);
16800                 }
16801                 return;
16802             }
16803             curNode = c;
16804             c.expand(false, false, f);
16805         };
16806         curNode.expand(false, false, f);
16807     },
16808
16809     /**
16810      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16811      * @param {String} path
16812      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16813      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16814      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16815      */
16816     selectPath : function(path, attr, callback){
16817         attr = attr || "id";
16818         var keys = path.split(this.pathSeparator);
16819         var v = keys.pop();
16820         if(keys.length > 0){
16821             var f = function(success, node){
16822                 if(success && node){
16823                     var n = node.findChild(attr, v);
16824                     if(n){
16825                         n.select();
16826                         if(callback){
16827                             callback(true, n);
16828                         }
16829                     }else if(callback){
16830                         callback(false, n);
16831                     }
16832                 }else{
16833                     if(callback){
16834                         callback(false, n);
16835                     }
16836                 }
16837             };
16838             this.expandPath(keys.join(this.pathSeparator), attr, f);
16839         }else{
16840             this.root.select();
16841             if(callback){
16842                 callback(true, this.root);
16843             }
16844         }
16845     },
16846
16847     getTreeEl : function(){
16848         return this.el;
16849     },
16850
16851     /**
16852      * Trigger rendering of this TreePanel
16853      */
16854     render : function(){
16855         if (this.innerCt) {
16856             return this; // stop it rendering more than once!!
16857         }
16858         
16859         this.innerCt = this.el.createChild({tag:"ul",
16860                cls:"x-tree-root-ct " +
16861                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16862
16863         if(this.containerScroll){
16864             Roo.dd.ScrollManager.register(this.el);
16865         }
16866         if((this.enableDD || this.enableDrop) && !this.dropZone){
16867            /**
16868             * The dropZone used by this tree if drop is enabled
16869             * @type Roo.tree.TreeDropZone
16870             */
16871              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16872                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16873            });
16874         }
16875         if((this.enableDD || this.enableDrag) && !this.dragZone){
16876            /**
16877             * The dragZone used by this tree if drag is enabled
16878             * @type Roo.tree.TreeDragZone
16879             */
16880             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16881                ddGroup: this.ddGroup || "TreeDD",
16882                scroll: this.ddScroll
16883            });
16884         }
16885         this.getSelectionModel().init(this);
16886         if (!this.root) {
16887             Roo.log("ROOT not set in tree");
16888             return this;
16889         }
16890         this.root.render();
16891         if(!this.rootVisible){
16892             this.root.renderChildren();
16893         }
16894         return this;
16895     }
16896 });/*
16897  * Based on:
16898  * Ext JS Library 1.1.1
16899  * Copyright(c) 2006-2007, Ext JS, LLC.
16900  *
16901  * Originally Released Under LGPL - original licence link has changed is not relivant.
16902  *
16903  * Fork - LGPL
16904  * <script type="text/javascript">
16905  */
16906  
16907
16908 /**
16909  * @class Roo.tree.DefaultSelectionModel
16910  * @extends Roo.util.Observable
16911  * The default single selection for a TreePanel.
16912  * @param {Object} cfg Configuration
16913  */
16914 Roo.tree.DefaultSelectionModel = function(cfg){
16915    this.selNode = null;
16916    
16917    
16918    
16919    this.addEvents({
16920        /**
16921         * @event selectionchange
16922         * Fires when the selected node changes
16923         * @param {DefaultSelectionModel} this
16924         * @param {TreeNode} node the new selection
16925         */
16926        "selectionchange" : true,
16927
16928        /**
16929         * @event beforeselect
16930         * Fires before the selected node changes, return false to cancel the change
16931         * @param {DefaultSelectionModel} this
16932         * @param {TreeNode} node the new selection
16933         * @param {TreeNode} node the old selection
16934         */
16935        "beforeselect" : true
16936    });
16937    
16938     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16939 };
16940
16941 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16942     init : function(tree){
16943         this.tree = tree;
16944         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16945         tree.on("click", this.onNodeClick, this);
16946     },
16947     
16948     onNodeClick : function(node, e){
16949         if (e.ctrlKey && this.selNode == node)  {
16950             this.unselect(node);
16951             return;
16952         }
16953         this.select(node);
16954     },
16955     
16956     /**
16957      * Select a node.
16958      * @param {TreeNode} node The node to select
16959      * @return {TreeNode} The selected node
16960      */
16961     select : function(node){
16962         var last = this.selNode;
16963         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16964             if(last){
16965                 last.ui.onSelectedChange(false);
16966             }
16967             this.selNode = node;
16968             node.ui.onSelectedChange(true);
16969             this.fireEvent("selectionchange", this, node, last);
16970         }
16971         return node;
16972     },
16973     
16974     /**
16975      * Deselect a node.
16976      * @param {TreeNode} node The node to unselect
16977      */
16978     unselect : function(node){
16979         if(this.selNode == node){
16980             this.clearSelections();
16981         }    
16982     },
16983     
16984     /**
16985      * Clear all selections
16986      */
16987     clearSelections : function(){
16988         var n = this.selNode;
16989         if(n){
16990             n.ui.onSelectedChange(false);
16991             this.selNode = null;
16992             this.fireEvent("selectionchange", this, null);
16993         }
16994         return n;
16995     },
16996     
16997     /**
16998      * Get the selected node
16999      * @return {TreeNode} The selected node
17000      */
17001     getSelectedNode : function(){
17002         return this.selNode;    
17003     },
17004     
17005     /**
17006      * Returns true if the node is selected
17007      * @param {TreeNode} node The node to check
17008      * @return {Boolean}
17009      */
17010     isSelected : function(node){
17011         return this.selNode == node;  
17012     },
17013
17014     /**
17015      * Selects the node above the selected node in the tree, intelligently walking the nodes
17016      * @return TreeNode The new selection
17017      */
17018     selectPrevious : function(){
17019         var s = this.selNode || this.lastSelNode;
17020         if(!s){
17021             return null;
17022         }
17023         var ps = s.previousSibling;
17024         if(ps){
17025             if(!ps.isExpanded() || ps.childNodes.length < 1){
17026                 return this.select(ps);
17027             } else{
17028                 var lc = ps.lastChild;
17029                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17030                     lc = lc.lastChild;
17031                 }
17032                 return this.select(lc);
17033             }
17034         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17035             return this.select(s.parentNode);
17036         }
17037         return null;
17038     },
17039
17040     /**
17041      * Selects the node above the selected node in the tree, intelligently walking the nodes
17042      * @return TreeNode The new selection
17043      */
17044     selectNext : function(){
17045         var s = this.selNode || this.lastSelNode;
17046         if(!s){
17047             return null;
17048         }
17049         if(s.firstChild && s.isExpanded()){
17050              return this.select(s.firstChild);
17051          }else if(s.nextSibling){
17052              return this.select(s.nextSibling);
17053          }else if(s.parentNode){
17054             var newS = null;
17055             s.parentNode.bubble(function(){
17056                 if(this.nextSibling){
17057                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17058                     return false;
17059                 }
17060             });
17061             return newS;
17062          }
17063         return null;
17064     },
17065
17066     onKeyDown : function(e){
17067         var s = this.selNode || this.lastSelNode;
17068         // undesirable, but required
17069         var sm = this;
17070         if(!s){
17071             return;
17072         }
17073         var k = e.getKey();
17074         switch(k){
17075              case e.DOWN:
17076                  e.stopEvent();
17077                  this.selectNext();
17078              break;
17079              case e.UP:
17080                  e.stopEvent();
17081                  this.selectPrevious();
17082              break;
17083              case e.RIGHT:
17084                  e.preventDefault();
17085                  if(s.hasChildNodes()){
17086                      if(!s.isExpanded()){
17087                          s.expand();
17088                      }else if(s.firstChild){
17089                          this.select(s.firstChild, e);
17090                      }
17091                  }
17092              break;
17093              case e.LEFT:
17094                  e.preventDefault();
17095                  if(s.hasChildNodes() && s.isExpanded()){
17096                      s.collapse();
17097                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17098                      this.select(s.parentNode, e);
17099                  }
17100              break;
17101         };
17102     }
17103 });
17104
17105 /**
17106  * @class Roo.tree.MultiSelectionModel
17107  * @extends Roo.util.Observable
17108  * Multi selection for a TreePanel.
17109  * @param {Object} cfg Configuration
17110  */
17111 Roo.tree.MultiSelectionModel = function(){
17112    this.selNodes = [];
17113    this.selMap = {};
17114    this.addEvents({
17115        /**
17116         * @event selectionchange
17117         * Fires when the selected nodes change
17118         * @param {MultiSelectionModel} this
17119         * @param {Array} nodes Array of the selected nodes
17120         */
17121        "selectionchange" : true
17122    });
17123    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17124    
17125 };
17126
17127 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17128     init : function(tree){
17129         this.tree = tree;
17130         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17131         tree.on("click", this.onNodeClick, this);
17132     },
17133     
17134     onNodeClick : function(node, e){
17135         this.select(node, e, e.ctrlKey);
17136     },
17137     
17138     /**
17139      * Select a node.
17140      * @param {TreeNode} node The node to select
17141      * @param {EventObject} e (optional) An event associated with the selection
17142      * @param {Boolean} keepExisting True to retain existing selections
17143      * @return {TreeNode} The selected node
17144      */
17145     select : function(node, e, keepExisting){
17146         if(keepExisting !== true){
17147             this.clearSelections(true);
17148         }
17149         if(this.isSelected(node)){
17150             this.lastSelNode = node;
17151             return node;
17152         }
17153         this.selNodes.push(node);
17154         this.selMap[node.id] = node;
17155         this.lastSelNode = node;
17156         node.ui.onSelectedChange(true);
17157         this.fireEvent("selectionchange", this, this.selNodes);
17158         return node;
17159     },
17160     
17161     /**
17162      * Deselect a node.
17163      * @param {TreeNode} node The node to unselect
17164      */
17165     unselect : function(node){
17166         if(this.selMap[node.id]){
17167             node.ui.onSelectedChange(false);
17168             var sn = this.selNodes;
17169             var index = -1;
17170             if(sn.indexOf){
17171                 index = sn.indexOf(node);
17172             }else{
17173                 for(var i = 0, len = sn.length; i < len; i++){
17174                     if(sn[i] == node){
17175                         index = i;
17176                         break;
17177                     }
17178                 }
17179             }
17180             if(index != -1){
17181                 this.selNodes.splice(index, 1);
17182             }
17183             delete this.selMap[node.id];
17184             this.fireEvent("selectionchange", this, this.selNodes);
17185         }
17186     },
17187     
17188     /**
17189      * Clear all selections
17190      */
17191     clearSelections : function(suppressEvent){
17192         var sn = this.selNodes;
17193         if(sn.length > 0){
17194             for(var i = 0, len = sn.length; i < len; i++){
17195                 sn[i].ui.onSelectedChange(false);
17196             }
17197             this.selNodes = [];
17198             this.selMap = {};
17199             if(suppressEvent !== true){
17200                 this.fireEvent("selectionchange", this, this.selNodes);
17201             }
17202         }
17203     },
17204     
17205     /**
17206      * Returns true if the node is selected
17207      * @param {TreeNode} node The node to check
17208      * @return {Boolean}
17209      */
17210     isSelected : function(node){
17211         return this.selMap[node.id] ? true : false;  
17212     },
17213     
17214     /**
17215      * Returns an array of the selected nodes
17216      * @return {Array}
17217      */
17218     getSelectedNodes : function(){
17219         return this.selNodes;    
17220     },
17221
17222     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17223
17224     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17225
17226     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17227 });/*
17228  * Based on:
17229  * Ext JS Library 1.1.1
17230  * Copyright(c) 2006-2007, Ext JS, LLC.
17231  *
17232  * Originally Released Under LGPL - original licence link has changed is not relivant.
17233  *
17234  * Fork - LGPL
17235  * <script type="text/javascript">
17236  */
17237  
17238 /**
17239  * @class Roo.tree.TreeNode
17240  * @extends Roo.data.Node
17241  * @cfg {String} text The text for this node
17242  * @cfg {Boolean} expanded true to start the node expanded
17243  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17244  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17245  * @cfg {Boolean} disabled true to start the node disabled
17246  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17247  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17248  * @cfg {String} cls A css class to be added to the node
17249  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17250  * @cfg {String} href URL of the link used for the node (defaults to #)
17251  * @cfg {String} hrefTarget target frame for the link
17252  * @cfg {String} qtip An Ext QuickTip for the node
17253  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17254  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17255  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17256  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17257  * (defaults to undefined with no checkbox rendered)
17258  * @constructor
17259  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17260  */
17261 Roo.tree.TreeNode = function(attributes){
17262     attributes = attributes || {};
17263     if(typeof attributes == "string"){
17264         attributes = {text: attributes};
17265     }
17266     this.childrenRendered = false;
17267     this.rendered = false;
17268     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17269     this.expanded = attributes.expanded === true;
17270     this.isTarget = attributes.isTarget !== false;
17271     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17272     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17273
17274     /**
17275      * Read-only. The text for this node. To change it use setText().
17276      * @type String
17277      */
17278     this.text = attributes.text;
17279     /**
17280      * True if this node is disabled.
17281      * @type Boolean
17282      */
17283     this.disabled = attributes.disabled === true;
17284
17285     this.addEvents({
17286         /**
17287         * @event textchange
17288         * Fires when the text for this node is changed
17289         * @param {Node} this This node
17290         * @param {String} text The new text
17291         * @param {String} oldText The old text
17292         */
17293         "textchange" : true,
17294         /**
17295         * @event beforeexpand
17296         * Fires before this node is expanded, return false to cancel.
17297         * @param {Node} this This node
17298         * @param {Boolean} deep
17299         * @param {Boolean} anim
17300         */
17301         "beforeexpand" : true,
17302         /**
17303         * @event beforecollapse
17304         * Fires before this node is collapsed, return false to cancel.
17305         * @param {Node} this This node
17306         * @param {Boolean} deep
17307         * @param {Boolean} anim
17308         */
17309         "beforecollapse" : true,
17310         /**
17311         * @event expand
17312         * Fires when this node is expanded
17313         * @param {Node} this This node
17314         */
17315         "expand" : true,
17316         /**
17317         * @event disabledchange
17318         * Fires when the disabled status of this node changes
17319         * @param {Node} this This node
17320         * @param {Boolean} disabled
17321         */
17322         "disabledchange" : true,
17323         /**
17324         * @event collapse
17325         * Fires when this node is collapsed
17326         * @param {Node} this This node
17327         */
17328         "collapse" : true,
17329         /**
17330         * @event beforeclick
17331         * Fires before click processing. Return false to cancel the default action.
17332         * @param {Node} this This node
17333         * @param {Roo.EventObject} e The event object
17334         */
17335         "beforeclick":true,
17336         /**
17337         * @event checkchange
17338         * Fires when a node with a checkbox's checked property changes
17339         * @param {Node} this This node
17340         * @param {Boolean} checked
17341         */
17342         "checkchange":true,
17343         /**
17344         * @event click
17345         * Fires when this node is clicked
17346         * @param {Node} this This node
17347         * @param {Roo.EventObject} e The event object
17348         */
17349         "click":true,
17350         /**
17351         * @event dblclick
17352         * Fires when this node is double clicked
17353         * @param {Node} this This node
17354         * @param {Roo.EventObject} e The event object
17355         */
17356         "dblclick":true,
17357         /**
17358         * @event contextmenu
17359         * Fires when this node is right clicked
17360         * @param {Node} this This node
17361         * @param {Roo.EventObject} e The event object
17362         */
17363         "contextmenu":true,
17364         /**
17365         * @event beforechildrenrendered
17366         * Fires right before the child nodes for this node are rendered
17367         * @param {Node} this This node
17368         */
17369         "beforechildrenrendered":true
17370     });
17371
17372     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17373
17374     /**
17375      * Read-only. The UI for this node
17376      * @type TreeNodeUI
17377      */
17378     this.ui = new uiClass(this);
17379     
17380     // finally support items[]
17381     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17382         return;
17383     }
17384     
17385     
17386     Roo.each(this.attributes.items, function(c) {
17387         this.appendChild(Roo.factory(c,Roo.Tree));
17388     }, this);
17389     delete this.attributes.items;
17390     
17391     
17392     
17393 };
17394 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17395     preventHScroll: true,
17396     /**
17397      * Returns true if this node is expanded
17398      * @return {Boolean}
17399      */
17400     isExpanded : function(){
17401         return this.expanded;
17402     },
17403
17404     /**
17405      * Returns the UI object for this node
17406      * @return {TreeNodeUI}
17407      */
17408     getUI : function(){
17409         return this.ui;
17410     },
17411
17412     // private override
17413     setFirstChild : function(node){
17414         var of = this.firstChild;
17415         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17416         if(this.childrenRendered && of && node != of){
17417             of.renderIndent(true, true);
17418         }
17419         if(this.rendered){
17420             this.renderIndent(true, true);
17421         }
17422     },
17423
17424     // private override
17425     setLastChild : function(node){
17426         var ol = this.lastChild;
17427         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17428         if(this.childrenRendered && ol && node != ol){
17429             ol.renderIndent(true, true);
17430         }
17431         if(this.rendered){
17432             this.renderIndent(true, true);
17433         }
17434     },
17435
17436     // these methods are overridden to provide lazy rendering support
17437     // private override
17438     appendChild : function()
17439     {
17440         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17441         if(node && this.childrenRendered){
17442             node.render();
17443         }
17444         this.ui.updateExpandIcon();
17445         return node;
17446     },
17447
17448     // private override
17449     removeChild : function(node){
17450         this.ownerTree.getSelectionModel().unselect(node);
17451         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17452         // if it's been rendered remove dom node
17453         if(this.childrenRendered){
17454             node.ui.remove();
17455         }
17456         if(this.childNodes.length < 1){
17457             this.collapse(false, false);
17458         }else{
17459             this.ui.updateExpandIcon();
17460         }
17461         if(!this.firstChild) {
17462             this.childrenRendered = false;
17463         }
17464         return node;
17465     },
17466
17467     // private override
17468     insertBefore : function(node, refNode){
17469         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17470         if(newNode && refNode && this.childrenRendered){
17471             node.render();
17472         }
17473         this.ui.updateExpandIcon();
17474         return newNode;
17475     },
17476
17477     /**
17478      * Sets the text for this node
17479      * @param {String} text
17480      */
17481     setText : function(text){
17482         var oldText = this.text;
17483         this.text = text;
17484         this.attributes.text = text;
17485         if(this.rendered){ // event without subscribing
17486             this.ui.onTextChange(this, text, oldText);
17487         }
17488         this.fireEvent("textchange", this, text, oldText);
17489     },
17490
17491     /**
17492      * Triggers selection of this node
17493      */
17494     select : function(){
17495         this.getOwnerTree().getSelectionModel().select(this);
17496     },
17497
17498     /**
17499      * Triggers deselection of this node
17500      */
17501     unselect : function(){
17502         this.getOwnerTree().getSelectionModel().unselect(this);
17503     },
17504
17505     /**
17506      * Returns true if this node is selected
17507      * @return {Boolean}
17508      */
17509     isSelected : function(){
17510         return this.getOwnerTree().getSelectionModel().isSelected(this);
17511     },
17512
17513     /**
17514      * Expand this node.
17515      * @param {Boolean} deep (optional) True to expand all children as well
17516      * @param {Boolean} anim (optional) false to cancel the default animation
17517      * @param {Function} callback (optional) A callback to be called when
17518      * expanding this node completes (does not wait for deep expand to complete).
17519      * Called with 1 parameter, this node.
17520      */
17521     expand : function(deep, anim, callback){
17522         if(!this.expanded){
17523             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17524                 return;
17525             }
17526             if(!this.childrenRendered){
17527                 this.renderChildren();
17528             }
17529             this.expanded = true;
17530             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17531                 this.ui.animExpand(function(){
17532                     this.fireEvent("expand", this);
17533                     if(typeof callback == "function"){
17534                         callback(this);
17535                     }
17536                     if(deep === true){
17537                         this.expandChildNodes(true);
17538                     }
17539                 }.createDelegate(this));
17540                 return;
17541             }else{
17542                 this.ui.expand();
17543                 this.fireEvent("expand", this);
17544                 if(typeof callback == "function"){
17545                     callback(this);
17546                 }
17547             }
17548         }else{
17549            if(typeof callback == "function"){
17550                callback(this);
17551            }
17552         }
17553         if(deep === true){
17554             this.expandChildNodes(true);
17555         }
17556     },
17557
17558     isHiddenRoot : function(){
17559         return this.isRoot && !this.getOwnerTree().rootVisible;
17560     },
17561
17562     /**
17563      * Collapse this node.
17564      * @param {Boolean} deep (optional) True to collapse all children as well
17565      * @param {Boolean} anim (optional) false to cancel the default animation
17566      */
17567     collapse : function(deep, anim){
17568         if(this.expanded && !this.isHiddenRoot()){
17569             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17570                 return;
17571             }
17572             this.expanded = false;
17573             if((this.getOwnerTree().animate && anim !== false) || anim){
17574                 this.ui.animCollapse(function(){
17575                     this.fireEvent("collapse", this);
17576                     if(deep === true){
17577                         this.collapseChildNodes(true);
17578                     }
17579                 }.createDelegate(this));
17580                 return;
17581             }else{
17582                 this.ui.collapse();
17583                 this.fireEvent("collapse", this);
17584             }
17585         }
17586         if(deep === true){
17587             var cs = this.childNodes;
17588             for(var i = 0, len = cs.length; i < len; i++) {
17589                 cs[i].collapse(true, false);
17590             }
17591         }
17592     },
17593
17594     // private
17595     delayedExpand : function(delay){
17596         if(!this.expandProcId){
17597             this.expandProcId = this.expand.defer(delay, this);
17598         }
17599     },
17600
17601     // private
17602     cancelExpand : function(){
17603         if(this.expandProcId){
17604             clearTimeout(this.expandProcId);
17605         }
17606         this.expandProcId = false;
17607     },
17608
17609     /**
17610      * Toggles expanded/collapsed state of the node
17611      */
17612     toggle : function(){
17613         if(this.expanded){
17614             this.collapse();
17615         }else{
17616             this.expand();
17617         }
17618     },
17619
17620     /**
17621      * Ensures all parent nodes are expanded
17622      */
17623     ensureVisible : function(callback){
17624         var tree = this.getOwnerTree();
17625         tree.expandPath(this.parentNode.getPath(), false, function(){
17626             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17627             Roo.callback(callback);
17628         }.createDelegate(this));
17629     },
17630
17631     /**
17632      * Expand all child nodes
17633      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17634      */
17635     expandChildNodes : function(deep){
17636         var cs = this.childNodes;
17637         for(var i = 0, len = cs.length; i < len; i++) {
17638                 cs[i].expand(deep);
17639         }
17640     },
17641
17642     /**
17643      * Collapse all child nodes
17644      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17645      */
17646     collapseChildNodes : function(deep){
17647         var cs = this.childNodes;
17648         for(var i = 0, len = cs.length; i < len; i++) {
17649                 cs[i].collapse(deep);
17650         }
17651     },
17652
17653     /**
17654      * Disables this node
17655      */
17656     disable : function(){
17657         this.disabled = true;
17658         this.unselect();
17659         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17660             this.ui.onDisableChange(this, true);
17661         }
17662         this.fireEvent("disabledchange", this, true);
17663     },
17664
17665     /**
17666      * Enables this node
17667      */
17668     enable : function(){
17669         this.disabled = false;
17670         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17671             this.ui.onDisableChange(this, false);
17672         }
17673         this.fireEvent("disabledchange", this, false);
17674     },
17675
17676     // private
17677     renderChildren : function(suppressEvent){
17678         if(suppressEvent !== false){
17679             this.fireEvent("beforechildrenrendered", this);
17680         }
17681         var cs = this.childNodes;
17682         for(var i = 0, len = cs.length; i < len; i++){
17683             cs[i].render(true);
17684         }
17685         this.childrenRendered = true;
17686     },
17687
17688     // private
17689     sort : function(fn, scope){
17690         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17691         if(this.childrenRendered){
17692             var cs = this.childNodes;
17693             for(var i = 0, len = cs.length; i < len; i++){
17694                 cs[i].render(true);
17695             }
17696         }
17697     },
17698
17699     // private
17700     render : function(bulkRender){
17701         this.ui.render(bulkRender);
17702         if(!this.rendered){
17703             this.rendered = true;
17704             if(this.expanded){
17705                 this.expanded = false;
17706                 this.expand(false, false);
17707             }
17708         }
17709     },
17710
17711     // private
17712     renderIndent : function(deep, refresh){
17713         if(refresh){
17714             this.ui.childIndent = null;
17715         }
17716         this.ui.renderIndent();
17717         if(deep === true && this.childrenRendered){
17718             var cs = this.childNodes;
17719             for(var i = 0, len = cs.length; i < len; i++){
17720                 cs[i].renderIndent(true, refresh);
17721             }
17722         }
17723     }
17724 });/*
17725  * Based on:
17726  * Ext JS Library 1.1.1
17727  * Copyright(c) 2006-2007, Ext JS, LLC.
17728  *
17729  * Originally Released Under LGPL - original licence link has changed is not relivant.
17730  *
17731  * Fork - LGPL
17732  * <script type="text/javascript">
17733  */
17734  
17735 /**
17736  * @class Roo.tree.AsyncTreeNode
17737  * @extends Roo.tree.TreeNode
17738  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17739  * @constructor
17740  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17741  */
17742  Roo.tree.AsyncTreeNode = function(config){
17743     this.loaded = false;
17744     this.loading = false;
17745     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17746     /**
17747     * @event beforeload
17748     * Fires before this node is loaded, return false to cancel
17749     * @param {Node} this This node
17750     */
17751     this.addEvents({'beforeload':true, 'load': true});
17752     /**
17753     * @event load
17754     * Fires when this node is loaded
17755     * @param {Node} this This node
17756     */
17757     /**
17758      * The loader used by this node (defaults to using the tree's defined loader)
17759      * @type TreeLoader
17760      * @property loader
17761      */
17762 };
17763 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17764     expand : function(deep, anim, callback){
17765         if(this.loading){ // if an async load is already running, waiting til it's done
17766             var timer;
17767             var f = function(){
17768                 if(!this.loading){ // done loading
17769                     clearInterval(timer);
17770                     this.expand(deep, anim, callback);
17771                 }
17772             }.createDelegate(this);
17773             timer = setInterval(f, 200);
17774             return;
17775         }
17776         if(!this.loaded){
17777             if(this.fireEvent("beforeload", this) === false){
17778                 return;
17779             }
17780             this.loading = true;
17781             this.ui.beforeLoad(this);
17782             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17783             if(loader){
17784                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17785                 return;
17786             }
17787         }
17788         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17789     },
17790     
17791     /**
17792      * Returns true if this node is currently loading
17793      * @return {Boolean}
17794      */
17795     isLoading : function(){
17796         return this.loading;  
17797     },
17798     
17799     loadComplete : function(deep, anim, callback){
17800         this.loading = false;
17801         this.loaded = true;
17802         this.ui.afterLoad(this);
17803         this.fireEvent("load", this);
17804         this.expand(deep, anim, callback);
17805     },
17806     
17807     /**
17808      * Returns true if this node has been loaded
17809      * @return {Boolean}
17810      */
17811     isLoaded : function(){
17812         return this.loaded;
17813     },
17814     
17815     hasChildNodes : function(){
17816         if(!this.isLeaf() && !this.loaded){
17817             return true;
17818         }else{
17819             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17820         }
17821     },
17822
17823     /**
17824      * Trigger a reload for this node
17825      * @param {Function} callback
17826      */
17827     reload : function(callback){
17828         this.collapse(false, false);
17829         while(this.firstChild){
17830             this.removeChild(this.firstChild);
17831         }
17832         this.childrenRendered = false;
17833         this.loaded = false;
17834         if(this.isHiddenRoot()){
17835             this.expanded = false;
17836         }
17837         this.expand(false, false, callback);
17838     }
17839 });/*
17840  * Based on:
17841  * Ext JS Library 1.1.1
17842  * Copyright(c) 2006-2007, Ext JS, LLC.
17843  *
17844  * Originally Released Under LGPL - original licence link has changed is not relivant.
17845  *
17846  * Fork - LGPL
17847  * <script type="text/javascript">
17848  */
17849  
17850 /**
17851  * @class Roo.tree.TreeNodeUI
17852  * @constructor
17853  * @param {Object} node The node to render
17854  * The TreeNode UI implementation is separate from the
17855  * tree implementation. Unless you are customizing the tree UI,
17856  * you should never have to use this directly.
17857  */
17858 Roo.tree.TreeNodeUI = function(node){
17859     this.node = node;
17860     this.rendered = false;
17861     this.animating = false;
17862     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17863 };
17864
17865 Roo.tree.TreeNodeUI.prototype = {
17866     removeChild : function(node){
17867         if(this.rendered){
17868             this.ctNode.removeChild(node.ui.getEl());
17869         }
17870     },
17871
17872     beforeLoad : function(){
17873          this.addClass("x-tree-node-loading");
17874     },
17875
17876     afterLoad : function(){
17877          this.removeClass("x-tree-node-loading");
17878     },
17879
17880     onTextChange : function(node, text, oldText){
17881         if(this.rendered){
17882             this.textNode.innerHTML = text;
17883         }
17884     },
17885
17886     onDisableChange : function(node, state){
17887         this.disabled = state;
17888         if(state){
17889             this.addClass("x-tree-node-disabled");
17890         }else{
17891             this.removeClass("x-tree-node-disabled");
17892         }
17893     },
17894
17895     onSelectedChange : function(state){
17896         if(state){
17897             this.focus();
17898             this.addClass("x-tree-selected");
17899         }else{
17900             //this.blur();
17901             this.removeClass("x-tree-selected");
17902         }
17903     },
17904
17905     onMove : function(tree, node, oldParent, newParent, index, refNode){
17906         this.childIndent = null;
17907         if(this.rendered){
17908             var targetNode = newParent.ui.getContainer();
17909             if(!targetNode){//target not rendered
17910                 this.holder = document.createElement("div");
17911                 this.holder.appendChild(this.wrap);
17912                 return;
17913             }
17914             var insertBefore = refNode ? refNode.ui.getEl() : null;
17915             if(insertBefore){
17916                 targetNode.insertBefore(this.wrap, insertBefore);
17917             }else{
17918                 targetNode.appendChild(this.wrap);
17919             }
17920             this.node.renderIndent(true);
17921         }
17922     },
17923
17924     addClass : function(cls){
17925         if(this.elNode){
17926             Roo.fly(this.elNode).addClass(cls);
17927         }
17928     },
17929
17930     removeClass : function(cls){
17931         if(this.elNode){
17932             Roo.fly(this.elNode).removeClass(cls);
17933         }
17934     },
17935
17936     remove : function(){
17937         if(this.rendered){
17938             this.holder = document.createElement("div");
17939             this.holder.appendChild(this.wrap);
17940         }
17941     },
17942
17943     fireEvent : function(){
17944         return this.node.fireEvent.apply(this.node, arguments);
17945     },
17946
17947     initEvents : function(){
17948         this.node.on("move", this.onMove, this);
17949         var E = Roo.EventManager;
17950         var a = this.anchor;
17951
17952         var el = Roo.fly(a, '_treeui');
17953
17954         if(Roo.isOpera){ // opera render bug ignores the CSS
17955             el.setStyle("text-decoration", "none");
17956         }
17957
17958         el.on("click", this.onClick, this);
17959         el.on("dblclick", this.onDblClick, this);
17960
17961         if(this.checkbox){
17962             Roo.EventManager.on(this.checkbox,
17963                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17964         }
17965
17966         el.on("contextmenu", this.onContextMenu, this);
17967
17968         var icon = Roo.fly(this.iconNode);
17969         icon.on("click", this.onClick, this);
17970         icon.on("dblclick", this.onDblClick, this);
17971         icon.on("contextmenu", this.onContextMenu, this);
17972         E.on(this.ecNode, "click", this.ecClick, this, true);
17973
17974         if(this.node.disabled){
17975             this.addClass("x-tree-node-disabled");
17976         }
17977         if(this.node.hidden){
17978             this.addClass("x-tree-node-disabled");
17979         }
17980         var ot = this.node.getOwnerTree();
17981         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17982         if(dd && (!this.node.isRoot || ot.rootVisible)){
17983             Roo.dd.Registry.register(this.elNode, {
17984                 node: this.node,
17985                 handles: this.getDDHandles(),
17986                 isHandle: false
17987             });
17988         }
17989     },
17990
17991     getDDHandles : function(){
17992         return [this.iconNode, this.textNode];
17993     },
17994
17995     hide : function(){
17996         if(this.rendered){
17997             this.wrap.style.display = "none";
17998         }
17999     },
18000
18001     show : function(){
18002         if(this.rendered){
18003             this.wrap.style.display = "";
18004         }
18005     },
18006
18007     onContextMenu : function(e){
18008         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18009             e.preventDefault();
18010             this.focus();
18011             this.fireEvent("contextmenu", this.node, e);
18012         }
18013     },
18014
18015     onClick : function(e){
18016         if(this.dropping){
18017             e.stopEvent();
18018             return;
18019         }
18020         if(this.fireEvent("beforeclick", this.node, e) !== false){
18021             if(!this.disabled && this.node.attributes.href){
18022                 this.fireEvent("click", this.node, e);
18023                 return;
18024             }
18025             e.preventDefault();
18026             if(this.disabled){
18027                 return;
18028             }
18029
18030             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18031                 this.node.toggle();
18032             }
18033
18034             this.fireEvent("click", this.node, e);
18035         }else{
18036             e.stopEvent();
18037         }
18038     },
18039
18040     onDblClick : function(e){
18041         e.preventDefault();
18042         if(this.disabled){
18043             return;
18044         }
18045         if(this.checkbox){
18046             this.toggleCheck();
18047         }
18048         if(!this.animating && this.node.hasChildNodes()){
18049             this.node.toggle();
18050         }
18051         this.fireEvent("dblclick", this.node, e);
18052     },
18053
18054     onCheckChange : function(){
18055         var checked = this.checkbox.checked;
18056         this.node.attributes.checked = checked;
18057         this.fireEvent('checkchange', this.node, checked);
18058     },
18059
18060     ecClick : function(e){
18061         if(!this.animating && this.node.hasChildNodes()){
18062             this.node.toggle();
18063         }
18064     },
18065
18066     startDrop : function(){
18067         this.dropping = true;
18068     },
18069
18070     // delayed drop so the click event doesn't get fired on a drop
18071     endDrop : function(){
18072        setTimeout(function(){
18073            this.dropping = false;
18074        }.createDelegate(this), 50);
18075     },
18076
18077     expand : function(){
18078         this.updateExpandIcon();
18079         this.ctNode.style.display = "";
18080     },
18081
18082     focus : function(){
18083         if(!this.node.preventHScroll){
18084             try{this.anchor.focus();
18085             }catch(e){}
18086         }else if(!Roo.isIE){
18087             try{
18088                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18089                 var l = noscroll.scrollLeft;
18090                 this.anchor.focus();
18091                 noscroll.scrollLeft = l;
18092             }catch(e){}
18093         }
18094     },
18095
18096     toggleCheck : function(value){
18097         var cb = this.checkbox;
18098         if(cb){
18099             cb.checked = (value === undefined ? !cb.checked : value);
18100         }
18101     },
18102
18103     blur : function(){
18104         try{
18105             this.anchor.blur();
18106         }catch(e){}
18107     },
18108
18109     animExpand : function(callback){
18110         var ct = Roo.get(this.ctNode);
18111         ct.stopFx();
18112         if(!this.node.hasChildNodes()){
18113             this.updateExpandIcon();
18114             this.ctNode.style.display = "";
18115             Roo.callback(callback);
18116             return;
18117         }
18118         this.animating = true;
18119         this.updateExpandIcon();
18120
18121         ct.slideIn('t', {
18122            callback : function(){
18123                this.animating = false;
18124                Roo.callback(callback);
18125             },
18126             scope: this,
18127             duration: this.node.ownerTree.duration || .25
18128         });
18129     },
18130
18131     highlight : function(){
18132         var tree = this.node.getOwnerTree();
18133         Roo.fly(this.wrap).highlight(
18134             tree.hlColor || "C3DAF9",
18135             {endColor: tree.hlBaseColor}
18136         );
18137     },
18138
18139     collapse : function(){
18140         this.updateExpandIcon();
18141         this.ctNode.style.display = "none";
18142     },
18143
18144     animCollapse : function(callback){
18145         var ct = Roo.get(this.ctNode);
18146         ct.enableDisplayMode('block');
18147         ct.stopFx();
18148
18149         this.animating = true;
18150         this.updateExpandIcon();
18151
18152         ct.slideOut('t', {
18153             callback : function(){
18154                this.animating = false;
18155                Roo.callback(callback);
18156             },
18157             scope: this,
18158             duration: this.node.ownerTree.duration || .25
18159         });
18160     },
18161
18162     getContainer : function(){
18163         return this.ctNode;
18164     },
18165
18166     getEl : function(){
18167         return this.wrap;
18168     },
18169
18170     appendDDGhost : function(ghostNode){
18171         ghostNode.appendChild(this.elNode.cloneNode(true));
18172     },
18173
18174     getDDRepairXY : function(){
18175         return Roo.lib.Dom.getXY(this.iconNode);
18176     },
18177
18178     onRender : function(){
18179         this.render();
18180     },
18181
18182     render : function(bulkRender){
18183         var n = this.node, a = n.attributes;
18184         var targetNode = n.parentNode ?
18185               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18186
18187         if(!this.rendered){
18188             this.rendered = true;
18189
18190             this.renderElements(n, a, targetNode, bulkRender);
18191
18192             if(a.qtip){
18193                if(this.textNode.setAttributeNS){
18194                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18195                    if(a.qtipTitle){
18196                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18197                    }
18198                }else{
18199                    this.textNode.setAttribute("ext:qtip", a.qtip);
18200                    if(a.qtipTitle){
18201                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18202                    }
18203                }
18204             }else if(a.qtipCfg){
18205                 a.qtipCfg.target = Roo.id(this.textNode);
18206                 Roo.QuickTips.register(a.qtipCfg);
18207             }
18208             this.initEvents();
18209             if(!this.node.expanded){
18210                 this.updateExpandIcon();
18211             }
18212         }else{
18213             if(bulkRender === true) {
18214                 targetNode.appendChild(this.wrap);
18215             }
18216         }
18217     },
18218
18219     renderElements : function(n, a, targetNode, bulkRender)
18220     {
18221         // add some indent caching, this helps performance when rendering a large tree
18222         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18223         var t = n.getOwnerTree();
18224         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18225         if (typeof(n.attributes.html) != 'undefined') {
18226             txt = n.attributes.html;
18227         }
18228         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18229         var cb = typeof a.checked == 'boolean';
18230         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18231         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18232             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18233             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18234             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18235             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18236             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18237              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18238                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18239             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18240             "</li>"];
18241
18242         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18243             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18244                                 n.nextSibling.ui.getEl(), buf.join(""));
18245         }else{
18246             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18247         }
18248
18249         this.elNode = this.wrap.childNodes[0];
18250         this.ctNode = this.wrap.childNodes[1];
18251         var cs = this.elNode.childNodes;
18252         this.indentNode = cs[0];
18253         this.ecNode = cs[1];
18254         this.iconNode = cs[2];
18255         var index = 3;
18256         if(cb){
18257             this.checkbox = cs[3];
18258             index++;
18259         }
18260         this.anchor = cs[index];
18261         this.textNode = cs[index].firstChild;
18262     },
18263
18264     getAnchor : function(){
18265         return this.anchor;
18266     },
18267
18268     getTextEl : function(){
18269         return this.textNode;
18270     },
18271
18272     getIconEl : function(){
18273         return this.iconNode;
18274     },
18275
18276     isChecked : function(){
18277         return this.checkbox ? this.checkbox.checked : false;
18278     },
18279
18280     updateExpandIcon : function(){
18281         if(this.rendered){
18282             var n = this.node, c1, c2;
18283             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18284             var hasChild = n.hasChildNodes();
18285             if(hasChild){
18286                 if(n.expanded){
18287                     cls += "-minus";
18288                     c1 = "x-tree-node-collapsed";
18289                     c2 = "x-tree-node-expanded";
18290                 }else{
18291                     cls += "-plus";
18292                     c1 = "x-tree-node-expanded";
18293                     c2 = "x-tree-node-collapsed";
18294                 }
18295                 if(this.wasLeaf){
18296                     this.removeClass("x-tree-node-leaf");
18297                     this.wasLeaf = false;
18298                 }
18299                 if(this.c1 != c1 || this.c2 != c2){
18300                     Roo.fly(this.elNode).replaceClass(c1, c2);
18301                     this.c1 = c1; this.c2 = c2;
18302                 }
18303             }else{
18304                 // this changes non-leafs into leafs if they have no children.
18305                 // it's not very rational behaviour..
18306                 
18307                 if(!this.wasLeaf && this.node.leaf){
18308                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18309                     delete this.c1;
18310                     delete this.c2;
18311                     this.wasLeaf = true;
18312                 }
18313             }
18314             var ecc = "x-tree-ec-icon "+cls;
18315             if(this.ecc != ecc){
18316                 this.ecNode.className = ecc;
18317                 this.ecc = ecc;
18318             }
18319         }
18320     },
18321
18322     getChildIndent : function(){
18323         if(!this.childIndent){
18324             var buf = [];
18325             var p = this.node;
18326             while(p){
18327                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18328                     if(!p.isLast()) {
18329                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18330                     } else {
18331                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18332                     }
18333                 }
18334                 p = p.parentNode;
18335             }
18336             this.childIndent = buf.join("");
18337         }
18338         return this.childIndent;
18339     },
18340
18341     renderIndent : function(){
18342         if(this.rendered){
18343             var indent = "";
18344             var p = this.node.parentNode;
18345             if(p){
18346                 indent = p.ui.getChildIndent();
18347             }
18348             if(this.indentMarkup != indent){ // don't rerender if not required
18349                 this.indentNode.innerHTML = indent;
18350                 this.indentMarkup = indent;
18351             }
18352             this.updateExpandIcon();
18353         }
18354     }
18355 };
18356
18357 Roo.tree.RootTreeNodeUI = function(){
18358     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18359 };
18360 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18361     render : function(){
18362         if(!this.rendered){
18363             var targetNode = this.node.ownerTree.innerCt.dom;
18364             this.node.expanded = true;
18365             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18366             this.wrap = this.ctNode = targetNode.firstChild;
18367         }
18368     },
18369     collapse : function(){
18370     },
18371     expand : function(){
18372     }
18373 });/*
18374  * Based on:
18375  * Ext JS Library 1.1.1
18376  * Copyright(c) 2006-2007, Ext JS, LLC.
18377  *
18378  * Originally Released Under LGPL - original licence link has changed is not relivant.
18379  *
18380  * Fork - LGPL
18381  * <script type="text/javascript">
18382  */
18383 /**
18384  * @class Roo.tree.TreeLoader
18385  * @extends Roo.util.Observable
18386  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18387  * nodes from a specified URL. The response must be a javascript Array definition
18388  * who's elements are node definition objects. eg:
18389  * <pre><code>
18390 {  success : true,
18391    data :      [
18392    
18393     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18394     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18395     ]
18396 }
18397
18398
18399 </code></pre>
18400  * <br><br>
18401  * The old style respose with just an array is still supported, but not recommended.
18402  * <br><br>
18403  *
18404  * A server request is sent, and child nodes are loaded only when a node is expanded.
18405  * The loading node's id is passed to the server under the parameter name "node" to
18406  * enable the server to produce the correct child nodes.
18407  * <br><br>
18408  * To pass extra parameters, an event handler may be attached to the "beforeload"
18409  * event, and the parameters specified in the TreeLoader's baseParams property:
18410  * <pre><code>
18411     myTreeLoader.on("beforeload", function(treeLoader, node) {
18412         this.baseParams.category = node.attributes.category;
18413     }, this);
18414 </code></pre><
18415  * This would pass an HTTP parameter called "category" to the server containing
18416  * the value of the Node's "category" attribute.
18417  * @constructor
18418  * Creates a new Treeloader.
18419  * @param {Object} config A config object containing config properties.
18420  */
18421 Roo.tree.TreeLoader = function(config){
18422     this.baseParams = {};
18423     this.requestMethod = "POST";
18424     Roo.apply(this, config);
18425
18426     this.addEvents({
18427     
18428         /**
18429          * @event beforeload
18430          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18431          * @param {Object} This TreeLoader object.
18432          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18433          * @param {Object} callback The callback function specified in the {@link #load} call.
18434          */
18435         beforeload : true,
18436         /**
18437          * @event load
18438          * Fires when the node has been successfuly loaded.
18439          * @param {Object} This TreeLoader object.
18440          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18441          * @param {Object} response The response object containing the data from the server.
18442          */
18443         load : true,
18444         /**
18445          * @event loadexception
18446          * Fires if the network request failed.
18447          * @param {Object} This TreeLoader object.
18448          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18449          * @param {Object} response The response object containing the data from the server.
18450          */
18451         loadexception : true,
18452         /**
18453          * @event create
18454          * Fires before a node is created, enabling you to return custom Node types 
18455          * @param {Object} This TreeLoader object.
18456          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18457          */
18458         create : true
18459     });
18460
18461     Roo.tree.TreeLoader.superclass.constructor.call(this);
18462 };
18463
18464 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18465     /**
18466     * @cfg {String} dataUrl The URL from which to request a Json string which
18467     * specifies an array of node definition object representing the child nodes
18468     * to be loaded.
18469     */
18470     /**
18471     * @cfg {Object} baseParams (optional) An object containing properties which
18472     * specify HTTP parameters to be passed to each request for child nodes.
18473     */
18474     /**
18475     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18476     * created by this loader. If the attributes sent by the server have an attribute in this object,
18477     * they take priority.
18478     */
18479     /**
18480     * @cfg {Object} uiProviders (optional) An object containing properties which
18481     * 
18482     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18483     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18484     * <i>uiProvider</i> attribute of a returned child node is a string rather
18485     * than a reference to a TreeNodeUI implementation, this that string value
18486     * is used as a property name in the uiProviders object. You can define the provider named
18487     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18488     */
18489     uiProviders : {},
18490
18491     /**
18492     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18493     * child nodes before loading.
18494     */
18495     clearOnLoad : true,
18496
18497     /**
18498     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18499     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18500     * Grid query { data : [ .....] }
18501     */
18502     
18503     root : false,
18504      /**
18505     * @cfg {String} queryParam (optional) 
18506     * Name of the query as it will be passed on the querystring (defaults to 'node')
18507     * eg. the request will be ?node=[id]
18508     */
18509     
18510     
18511     queryParam: false,
18512     
18513     /**
18514      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18515      * This is called automatically when a node is expanded, but may be used to reload
18516      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18517      * @param {Roo.tree.TreeNode} node
18518      * @param {Function} callback
18519      */
18520     load : function(node, callback){
18521         if(this.clearOnLoad){
18522             while(node.firstChild){
18523                 node.removeChild(node.firstChild);
18524             }
18525         }
18526         if(node.attributes.children){ // preloaded json children
18527             var cs = node.attributes.children;
18528             for(var i = 0, len = cs.length; i < len; i++){
18529                 node.appendChild(this.createNode(cs[i]));
18530             }
18531             if(typeof callback == "function"){
18532                 callback();
18533             }
18534         }else if(this.dataUrl){
18535             this.requestData(node, callback);
18536         }
18537     },
18538
18539     getParams: function(node){
18540         var buf = [], bp = this.baseParams;
18541         for(var key in bp){
18542             if(typeof bp[key] != "function"){
18543                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18544             }
18545         }
18546         var n = this.queryParam === false ? 'node' : this.queryParam;
18547         buf.push(n + "=", encodeURIComponent(node.id));
18548         return buf.join("");
18549     },
18550
18551     requestData : function(node, callback){
18552         if(this.fireEvent("beforeload", this, node, callback) !== false){
18553             this.transId = Roo.Ajax.request({
18554                 method:this.requestMethod,
18555                 url: this.dataUrl||this.url,
18556                 success: this.handleResponse,
18557                 failure: this.handleFailure,
18558                 scope: this,
18559                 argument: {callback: callback, node: node},
18560                 params: this.getParams(node)
18561             });
18562         }else{
18563             // if the load is cancelled, make sure we notify
18564             // the node that we are done
18565             if(typeof callback == "function"){
18566                 callback();
18567             }
18568         }
18569     },
18570
18571     isLoading : function(){
18572         return this.transId ? true : false;
18573     },
18574
18575     abort : function(){
18576         if(this.isLoading()){
18577             Roo.Ajax.abort(this.transId);
18578         }
18579     },
18580
18581     // private
18582     createNode : function(attr)
18583     {
18584         // apply baseAttrs, nice idea Corey!
18585         if(this.baseAttrs){
18586             Roo.applyIf(attr, this.baseAttrs);
18587         }
18588         if(this.applyLoader !== false){
18589             attr.loader = this;
18590         }
18591         // uiProvider = depreciated..
18592         
18593         if(typeof(attr.uiProvider) == 'string'){
18594            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18595                 /**  eval:var:attr */ eval(attr.uiProvider);
18596         }
18597         if(typeof(this.uiProviders['default']) != 'undefined') {
18598             attr.uiProvider = this.uiProviders['default'];
18599         }
18600         
18601         this.fireEvent('create', this, attr);
18602         
18603         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18604         return(attr.leaf ?
18605                         new Roo.tree.TreeNode(attr) :
18606                         new Roo.tree.AsyncTreeNode(attr));
18607     },
18608
18609     processResponse : function(response, node, callback)
18610     {
18611         var json = response.responseText;
18612         try {
18613             
18614             var o = Roo.decode(json);
18615             
18616             if (this.root === false && typeof(o.success) != undefined) {
18617                 this.root = 'data'; // the default behaviour for list like data..
18618                 }
18619                 
18620             if (this.root !== false &&  !o.success) {
18621                 // it's a failure condition.
18622                 var a = response.argument;
18623                 this.fireEvent("loadexception", this, a.node, response);
18624                 Roo.log("Load failed - should have a handler really");
18625                 return;
18626             }
18627             
18628             
18629             
18630             if (this.root !== false) {
18631                  o = o[this.root];
18632             }
18633             
18634             for(var i = 0, len = o.length; i < len; i++){
18635                 var n = this.createNode(o[i]);
18636                 if(n){
18637                     node.appendChild(n);
18638                 }
18639             }
18640             if(typeof callback == "function"){
18641                 callback(this, node);
18642             }
18643         }catch(e){
18644             this.handleFailure(response);
18645         }
18646     },
18647
18648     handleResponse : function(response){
18649         this.transId = false;
18650         var a = response.argument;
18651         this.processResponse(response, a.node, a.callback);
18652         this.fireEvent("load", this, a.node, response);
18653     },
18654
18655     handleFailure : function(response)
18656     {
18657         // should handle failure better..
18658         this.transId = false;
18659         var a = response.argument;
18660         this.fireEvent("loadexception", this, a.node, response);
18661         if(typeof a.callback == "function"){
18662             a.callback(this, a.node);
18663         }
18664     }
18665 });/*
18666  * Based on:
18667  * Ext JS Library 1.1.1
18668  * Copyright(c) 2006-2007, Ext JS, LLC.
18669  *
18670  * Originally Released Under LGPL - original licence link has changed is not relivant.
18671  *
18672  * Fork - LGPL
18673  * <script type="text/javascript">
18674  */
18675
18676 /**
18677 * @class Roo.tree.TreeFilter
18678 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18679 * @param {TreePanel} tree
18680 * @param {Object} config (optional)
18681  */
18682 Roo.tree.TreeFilter = function(tree, config){
18683     this.tree = tree;
18684     this.filtered = {};
18685     Roo.apply(this, config);
18686 };
18687
18688 Roo.tree.TreeFilter.prototype = {
18689     clearBlank:false,
18690     reverse:false,
18691     autoClear:false,
18692     remove:false,
18693
18694      /**
18695      * Filter the data by a specific attribute.
18696      * @param {String/RegExp} value Either string that the attribute value
18697      * should start with or a RegExp to test against the attribute
18698      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18699      * @param {TreeNode} startNode (optional) The node to start the filter at.
18700      */
18701     filter : function(value, attr, startNode){
18702         attr = attr || "text";
18703         var f;
18704         if(typeof value == "string"){
18705             var vlen = value.length;
18706             // auto clear empty filter
18707             if(vlen == 0 && this.clearBlank){
18708                 this.clear();
18709                 return;
18710             }
18711             value = value.toLowerCase();
18712             f = function(n){
18713                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18714             };
18715         }else if(value.exec){ // regex?
18716             f = function(n){
18717                 return value.test(n.attributes[attr]);
18718             };
18719         }else{
18720             throw 'Illegal filter type, must be string or regex';
18721         }
18722         this.filterBy(f, null, startNode);
18723         },
18724
18725     /**
18726      * Filter by a function. The passed function will be called with each
18727      * node in the tree (or from the startNode). If the function returns true, the node is kept
18728      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18729      * @param {Function} fn The filter function
18730      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18731      */
18732     filterBy : function(fn, scope, startNode){
18733         startNode = startNode || this.tree.root;
18734         if(this.autoClear){
18735             this.clear();
18736         }
18737         var af = this.filtered, rv = this.reverse;
18738         var f = function(n){
18739             if(n == startNode){
18740                 return true;
18741             }
18742             if(af[n.id]){
18743                 return false;
18744             }
18745             var m = fn.call(scope || n, n);
18746             if(!m || rv){
18747                 af[n.id] = n;
18748                 n.ui.hide();
18749                 return false;
18750             }
18751             return true;
18752         };
18753         startNode.cascade(f);
18754         if(this.remove){
18755            for(var id in af){
18756                if(typeof id != "function"){
18757                    var n = af[id];
18758                    if(n && n.parentNode){
18759                        n.parentNode.removeChild(n);
18760                    }
18761                }
18762            }
18763         }
18764     },
18765
18766     /**
18767      * Clears the current filter. Note: with the "remove" option
18768      * set a filter cannot be cleared.
18769      */
18770     clear : function(){
18771         var t = this.tree;
18772         var af = this.filtered;
18773         for(var id in af){
18774             if(typeof id != "function"){
18775                 var n = af[id];
18776                 if(n){
18777                     n.ui.show();
18778                 }
18779             }
18780         }
18781         this.filtered = {};
18782     }
18783 };
18784 /*
18785  * Based on:
18786  * Ext JS Library 1.1.1
18787  * Copyright(c) 2006-2007, Ext JS, LLC.
18788  *
18789  * Originally Released Under LGPL - original licence link has changed is not relivant.
18790  *
18791  * Fork - LGPL
18792  * <script type="text/javascript">
18793  */
18794  
18795
18796 /**
18797  * @class Roo.tree.TreeSorter
18798  * Provides sorting of nodes in a TreePanel
18799  * 
18800  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18801  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18802  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18803  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18804  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18805  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18806  * @constructor
18807  * @param {TreePanel} tree
18808  * @param {Object} config
18809  */
18810 Roo.tree.TreeSorter = function(tree, config){
18811     Roo.apply(this, config);
18812     tree.on("beforechildrenrendered", this.doSort, this);
18813     tree.on("append", this.updateSort, this);
18814     tree.on("insert", this.updateSort, this);
18815     
18816     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18817     var p = this.property || "text";
18818     var sortType = this.sortType;
18819     var fs = this.folderSort;
18820     var cs = this.caseSensitive === true;
18821     var leafAttr = this.leafAttr || 'leaf';
18822
18823     this.sortFn = function(n1, n2){
18824         if(fs){
18825             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18826                 return 1;
18827             }
18828             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18829                 return -1;
18830             }
18831         }
18832         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18833         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18834         if(v1 < v2){
18835                         return dsc ? +1 : -1;
18836                 }else if(v1 > v2){
18837                         return dsc ? -1 : +1;
18838         }else{
18839                 return 0;
18840         }
18841     };
18842 };
18843
18844 Roo.tree.TreeSorter.prototype = {
18845     doSort : function(node){
18846         node.sort(this.sortFn);
18847     },
18848     
18849     compareNodes : function(n1, n2){
18850         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18851     },
18852     
18853     updateSort : function(tree, node){
18854         if(node.childrenRendered){
18855             this.doSort.defer(1, this, [node]);
18856         }
18857     }
18858 };/*
18859  * Based on:
18860  * Ext JS Library 1.1.1
18861  * Copyright(c) 2006-2007, Ext JS, LLC.
18862  *
18863  * Originally Released Under LGPL - original licence link has changed is not relivant.
18864  *
18865  * Fork - LGPL
18866  * <script type="text/javascript">
18867  */
18868
18869 if(Roo.dd.DropZone){
18870     
18871 Roo.tree.TreeDropZone = function(tree, config){
18872     this.allowParentInsert = false;
18873     this.allowContainerDrop = false;
18874     this.appendOnly = false;
18875     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18876     this.tree = tree;
18877     this.lastInsertClass = "x-tree-no-status";
18878     this.dragOverData = {};
18879 };
18880
18881 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18882     ddGroup : "TreeDD",
18883     
18884     expandDelay : 1000,
18885     
18886     expandNode : function(node){
18887         if(node.hasChildNodes() && !node.isExpanded()){
18888             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18889         }
18890     },
18891     
18892     queueExpand : function(node){
18893         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18894     },
18895     
18896     cancelExpand : function(){
18897         if(this.expandProcId){
18898             clearTimeout(this.expandProcId);
18899             this.expandProcId = false;
18900         }
18901     },
18902     
18903     isValidDropPoint : function(n, pt, dd, e, data){
18904         if(!n || !data){ return false; }
18905         var targetNode = n.node;
18906         var dropNode = data.node;
18907         // default drop rules
18908         if(!(targetNode && targetNode.isTarget && pt)){
18909             return false;
18910         }
18911         if(pt == "append" && targetNode.allowChildren === false){
18912             return false;
18913         }
18914         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18915             return false;
18916         }
18917         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18918             return false;
18919         }
18920         // reuse the object
18921         var overEvent = this.dragOverData;
18922         overEvent.tree = this.tree;
18923         overEvent.target = targetNode;
18924         overEvent.data = data;
18925         overEvent.point = pt;
18926         overEvent.source = dd;
18927         overEvent.rawEvent = e;
18928         overEvent.dropNode = dropNode;
18929         overEvent.cancel = false;  
18930         var result = this.tree.fireEvent("nodedragover", overEvent);
18931         return overEvent.cancel === false && result !== false;
18932     },
18933     
18934     getDropPoint : function(e, n, dd){
18935         var tn = n.node;
18936         if(tn.isRoot){
18937             return tn.allowChildren !== false ? "append" : false; // always append for root
18938         }
18939         var dragEl = n.ddel;
18940         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18941         var y = Roo.lib.Event.getPageY(e);
18942         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18943         
18944         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18945         var noAppend = tn.allowChildren === false;
18946         if(this.appendOnly || tn.parentNode.allowChildren === false){
18947             return noAppend ? false : "append";
18948         }
18949         var noBelow = false;
18950         if(!this.allowParentInsert){
18951             noBelow = tn.hasChildNodes() && tn.isExpanded();
18952         }
18953         var q = (b - t) / (noAppend ? 2 : 3);
18954         if(y >= t && y < (t + q)){
18955             return "above";
18956         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18957             return "below";
18958         }else{
18959             return "append";
18960         }
18961     },
18962     
18963     onNodeEnter : function(n, dd, e, data){
18964         this.cancelExpand();
18965     },
18966     
18967     onNodeOver : function(n, dd, e, data){
18968         var pt = this.getDropPoint(e, n, dd);
18969         var node = n.node;
18970         
18971         // auto node expand check
18972         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18973             this.queueExpand(node);
18974         }else if(pt != "append"){
18975             this.cancelExpand();
18976         }
18977         
18978         // set the insert point style on the target node
18979         var returnCls = this.dropNotAllowed;
18980         if(this.isValidDropPoint(n, pt, dd, e, data)){
18981            if(pt){
18982                var el = n.ddel;
18983                var cls;
18984                if(pt == "above"){
18985                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18986                    cls = "x-tree-drag-insert-above";
18987                }else if(pt == "below"){
18988                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18989                    cls = "x-tree-drag-insert-below";
18990                }else{
18991                    returnCls = "x-tree-drop-ok-append";
18992                    cls = "x-tree-drag-append";
18993                }
18994                if(this.lastInsertClass != cls){
18995                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18996                    this.lastInsertClass = cls;
18997                }
18998            }
18999        }
19000        return returnCls;
19001     },
19002     
19003     onNodeOut : function(n, dd, e, data){
19004         this.cancelExpand();
19005         this.removeDropIndicators(n);
19006     },
19007     
19008     onNodeDrop : function(n, dd, e, data){
19009         var point = this.getDropPoint(e, n, dd);
19010         var targetNode = n.node;
19011         targetNode.ui.startDrop();
19012         if(!this.isValidDropPoint(n, point, dd, e, data)){
19013             targetNode.ui.endDrop();
19014             return false;
19015         }
19016         // first try to find the drop node
19017         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19018         var dropEvent = {
19019             tree : this.tree,
19020             target: targetNode,
19021             data: data,
19022             point: point,
19023             source: dd,
19024             rawEvent: e,
19025             dropNode: dropNode,
19026             cancel: !dropNode   
19027         };
19028         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19029         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19030             targetNode.ui.endDrop();
19031             return false;
19032         }
19033         // allow target changing
19034         targetNode = dropEvent.target;
19035         if(point == "append" && !targetNode.isExpanded()){
19036             targetNode.expand(false, null, function(){
19037                 this.completeDrop(dropEvent);
19038             }.createDelegate(this));
19039         }else{
19040             this.completeDrop(dropEvent);
19041         }
19042         return true;
19043     },
19044     
19045     completeDrop : function(de){
19046         var ns = de.dropNode, p = de.point, t = de.target;
19047         if(!(ns instanceof Array)){
19048             ns = [ns];
19049         }
19050         var n;
19051         for(var i = 0, len = ns.length; i < len; i++){
19052             n = ns[i];
19053             if(p == "above"){
19054                 t.parentNode.insertBefore(n, t);
19055             }else if(p == "below"){
19056                 t.parentNode.insertBefore(n, t.nextSibling);
19057             }else{
19058                 t.appendChild(n);
19059             }
19060         }
19061         n.ui.focus();
19062         if(this.tree.hlDrop){
19063             n.ui.highlight();
19064         }
19065         t.ui.endDrop();
19066         this.tree.fireEvent("nodedrop", de);
19067     },
19068     
19069     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19070         if(this.tree.hlDrop){
19071             dropNode.ui.focus();
19072             dropNode.ui.highlight();
19073         }
19074         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19075     },
19076     
19077     getTree : function(){
19078         return this.tree;
19079     },
19080     
19081     removeDropIndicators : function(n){
19082         if(n && n.ddel){
19083             var el = n.ddel;
19084             Roo.fly(el).removeClass([
19085                     "x-tree-drag-insert-above",
19086                     "x-tree-drag-insert-below",
19087                     "x-tree-drag-append"]);
19088             this.lastInsertClass = "_noclass";
19089         }
19090     },
19091     
19092     beforeDragDrop : function(target, e, id){
19093         this.cancelExpand();
19094         return true;
19095     },
19096     
19097     afterRepair : function(data){
19098         if(data && Roo.enableFx){
19099             data.node.ui.highlight();
19100         }
19101         this.hideProxy();
19102     }    
19103 });
19104
19105 }
19106 /*
19107  * Based on:
19108  * Ext JS Library 1.1.1
19109  * Copyright(c) 2006-2007, Ext JS, LLC.
19110  *
19111  * Originally Released Under LGPL - original licence link has changed is not relivant.
19112  *
19113  * Fork - LGPL
19114  * <script type="text/javascript">
19115  */
19116  
19117
19118 if(Roo.dd.DragZone){
19119 Roo.tree.TreeDragZone = function(tree, config){
19120     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19121     this.tree = tree;
19122 };
19123
19124 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19125     ddGroup : "TreeDD",
19126     
19127     onBeforeDrag : function(data, e){
19128         var n = data.node;
19129         return n && n.draggable && !n.disabled;
19130     },
19131     
19132     onInitDrag : function(e){
19133         var data = this.dragData;
19134         this.tree.getSelectionModel().select(data.node);
19135         this.proxy.update("");
19136         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19137         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19138     },
19139     
19140     getRepairXY : function(e, data){
19141         return data.node.ui.getDDRepairXY();
19142     },
19143     
19144     onEndDrag : function(data, e){
19145         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19146     },
19147     
19148     onValidDrop : function(dd, e, id){
19149         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19150         this.hideProxy();
19151     },
19152     
19153     beforeInvalidDrop : function(e, id){
19154         // this scrolls the original position back into view
19155         var sm = this.tree.getSelectionModel();
19156         sm.clearSelections();
19157         sm.select(this.dragData.node);
19158     }
19159 });
19160 }/*
19161  * Based on:
19162  * Ext JS Library 1.1.1
19163  * Copyright(c) 2006-2007, Ext JS, LLC.
19164  *
19165  * Originally Released Under LGPL - original licence link has changed is not relivant.
19166  *
19167  * Fork - LGPL
19168  * <script type="text/javascript">
19169  */
19170 /**
19171  * @class Roo.tree.TreeEditor
19172  * @extends Roo.Editor
19173  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19174  * as the editor field.
19175  * @constructor
19176  * @param {Object} config (used to be the tree panel.)
19177  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19178  * 
19179  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19180  * @cfg {Roo.form.TextField|Object} field The field configuration
19181  *
19182  * 
19183  */
19184 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19185     var tree = config;
19186     var field;
19187     if (oldconfig) { // old style..
19188         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19189     } else {
19190         // new style..
19191         tree = config.tree;
19192         config.field = config.field  || {};
19193         config.field.xtype = 'TextField';
19194         field = Roo.factory(config.field, Roo.form);
19195     }
19196     config = config || {};
19197     
19198     
19199     this.addEvents({
19200         /**
19201          * @event beforenodeedit
19202          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19203          * false from the handler of this event.
19204          * @param {Editor} this
19205          * @param {Roo.tree.Node} node 
19206          */
19207         "beforenodeedit" : true
19208     });
19209     
19210     //Roo.log(config);
19211     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19212
19213     this.tree = tree;
19214
19215     tree.on('beforeclick', this.beforeNodeClick, this);
19216     tree.getTreeEl().on('mousedown', this.hide, this);
19217     this.on('complete', this.updateNode, this);
19218     this.on('beforestartedit', this.fitToTree, this);
19219     this.on('startedit', this.bindScroll, this, {delay:10});
19220     this.on('specialkey', this.onSpecialKey, this);
19221 };
19222
19223 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19224     /**
19225      * @cfg {String} alignment
19226      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19227      */
19228     alignment: "l-l",
19229     // inherit
19230     autoSize: false,
19231     /**
19232      * @cfg {Boolean} hideEl
19233      * True to hide the bound element while the editor is displayed (defaults to false)
19234      */
19235     hideEl : false,
19236     /**
19237      * @cfg {String} cls
19238      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19239      */
19240     cls: "x-small-editor x-tree-editor",
19241     /**
19242      * @cfg {Boolean} shim
19243      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19244      */
19245     shim:false,
19246     // inherit
19247     shadow:"frame",
19248     /**
19249      * @cfg {Number} maxWidth
19250      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19251      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19252      * scroll and client offsets into account prior to each edit.
19253      */
19254     maxWidth: 250,
19255
19256     editDelay : 350,
19257
19258     // private
19259     fitToTree : function(ed, el){
19260         var td = this.tree.getTreeEl().dom, nd = el.dom;
19261         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19262             td.scrollLeft = nd.offsetLeft;
19263         }
19264         var w = Math.min(
19265                 this.maxWidth,
19266                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19267         this.setSize(w, '');
19268         
19269         return this.fireEvent('beforenodeedit', this, this.editNode);
19270         
19271     },
19272
19273     // private
19274     triggerEdit : function(node){
19275         this.completeEdit();
19276         this.editNode = node;
19277         this.startEdit(node.ui.textNode, node.text);
19278     },
19279
19280     // private
19281     bindScroll : function(){
19282         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19283     },
19284
19285     // private
19286     beforeNodeClick : function(node, e){
19287         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19288         this.lastClick = new Date();
19289         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19290             e.stopEvent();
19291             this.triggerEdit(node);
19292             return false;
19293         }
19294         return true;
19295     },
19296
19297     // private
19298     updateNode : function(ed, value){
19299         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19300         this.editNode.setText(value);
19301     },
19302
19303     // private
19304     onHide : function(){
19305         Roo.tree.TreeEditor.superclass.onHide.call(this);
19306         if(this.editNode){
19307             this.editNode.ui.focus();
19308         }
19309     },
19310
19311     // private
19312     onSpecialKey : function(field, e){
19313         var k = e.getKey();
19314         if(k == e.ESC){
19315             e.stopEvent();
19316             this.cancelEdit();
19317         }else if(k == e.ENTER && !e.hasModifier()){
19318             e.stopEvent();
19319             this.completeEdit();
19320         }
19321     }
19322 });//<Script type="text/javascript">
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 /**
19335  * Not documented??? - probably should be...
19336  */
19337
19338 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19339     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19340     
19341     renderElements : function(n, a, targetNode, bulkRender){
19342         //consel.log("renderElements?");
19343         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19344
19345         var t = n.getOwnerTree();
19346         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19347         
19348         var cols = t.columns;
19349         var bw = t.borderWidth;
19350         var c = cols[0];
19351         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19352          var cb = typeof a.checked == "boolean";
19353         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19354         var colcls = 'x-t-' + tid + '-c0';
19355         var buf = [
19356             '<li class="x-tree-node">',
19357             
19358                 
19359                 '<div class="x-tree-node-el ', a.cls,'">',
19360                     // extran...
19361                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19362                 
19363                 
19364                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19365                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19366                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19367                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19368                            (a.iconCls ? ' '+a.iconCls : ''),
19369                            '" unselectable="on" />',
19370                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19371                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19372                              
19373                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19374                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19375                             '<span unselectable="on" qtip="' + tx + '">',
19376                              tx,
19377                              '</span></a>' ,
19378                     '</div>',
19379                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19380                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19381                  ];
19382         for(var i = 1, len = cols.length; i < len; i++){
19383             c = cols[i];
19384             colcls = 'x-t-' + tid + '-c' +i;
19385             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19386             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19387                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19388                       "</div>");
19389          }
19390          
19391          buf.push(
19392             '</a>',
19393             '<div class="x-clear"></div></div>',
19394             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19395             "</li>");
19396         
19397         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19398             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19399                                 n.nextSibling.ui.getEl(), buf.join(""));
19400         }else{
19401             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19402         }
19403         var el = this.wrap.firstChild;
19404         this.elRow = el;
19405         this.elNode = el.firstChild;
19406         this.ranchor = el.childNodes[1];
19407         this.ctNode = this.wrap.childNodes[1];
19408         var cs = el.firstChild.childNodes;
19409         this.indentNode = cs[0];
19410         this.ecNode = cs[1];
19411         this.iconNode = cs[2];
19412         var index = 3;
19413         if(cb){
19414             this.checkbox = cs[3];
19415             index++;
19416         }
19417         this.anchor = cs[index];
19418         
19419         this.textNode = cs[index].firstChild;
19420         
19421         //el.on("click", this.onClick, this);
19422         //el.on("dblclick", this.onDblClick, this);
19423         
19424         
19425        // console.log(this);
19426     },
19427     initEvents : function(){
19428         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19429         
19430             
19431         var a = this.ranchor;
19432
19433         var el = Roo.get(a);
19434
19435         if(Roo.isOpera){ // opera render bug ignores the CSS
19436             el.setStyle("text-decoration", "none");
19437         }
19438
19439         el.on("click", this.onClick, this);
19440         el.on("dblclick", this.onDblClick, this);
19441         el.on("contextmenu", this.onContextMenu, this);
19442         
19443     },
19444     
19445     /*onSelectedChange : function(state){
19446         if(state){
19447             this.focus();
19448             this.addClass("x-tree-selected");
19449         }else{
19450             //this.blur();
19451             this.removeClass("x-tree-selected");
19452         }
19453     },*/
19454     addClass : function(cls){
19455         if(this.elRow){
19456             Roo.fly(this.elRow).addClass(cls);
19457         }
19458         
19459     },
19460     
19461     
19462     removeClass : function(cls){
19463         if(this.elRow){
19464             Roo.fly(this.elRow).removeClass(cls);
19465         }
19466     }
19467
19468     
19469     
19470 });//<Script type="text/javascript">
19471
19472 /*
19473  * Based on:
19474  * Ext JS Library 1.1.1
19475  * Copyright(c) 2006-2007, Ext JS, LLC.
19476  *
19477  * Originally Released Under LGPL - original licence link has changed is not relivant.
19478  *
19479  * Fork - LGPL
19480  * <script type="text/javascript">
19481  */
19482  
19483
19484 /**
19485  * @class Roo.tree.ColumnTree
19486  * @extends Roo.data.TreePanel
19487  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19488  * @cfg {int} borderWidth  compined right/left border allowance
19489  * @constructor
19490  * @param {String/HTMLElement/Element} el The container element
19491  * @param {Object} config
19492  */
19493 Roo.tree.ColumnTree =  function(el, config)
19494 {
19495    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19496    this.addEvents({
19497         /**
19498         * @event resize
19499         * Fire this event on a container when it resizes
19500         * @param {int} w Width
19501         * @param {int} h Height
19502         */
19503        "resize" : true
19504     });
19505     this.on('resize', this.onResize, this);
19506 };
19507
19508 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19509     //lines:false,
19510     
19511     
19512     borderWidth: Roo.isBorderBox ? 0 : 2, 
19513     headEls : false,
19514     
19515     render : function(){
19516         // add the header.....
19517        
19518         Roo.tree.ColumnTree.superclass.render.apply(this);
19519         
19520         this.el.addClass('x-column-tree');
19521         
19522         this.headers = this.el.createChild(
19523             {cls:'x-tree-headers'},this.innerCt.dom);
19524    
19525         var cols = this.columns, c;
19526         var totalWidth = 0;
19527         this.headEls = [];
19528         var  len = cols.length;
19529         for(var i = 0; i < len; i++){
19530              c = cols[i];
19531              totalWidth += c.width;
19532             this.headEls.push(this.headers.createChild({
19533                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19534                  cn: {
19535                      cls:'x-tree-hd-text',
19536                      html: c.header
19537                  },
19538                  style:'width:'+(c.width-this.borderWidth)+'px;'
19539              }));
19540         }
19541         this.headers.createChild({cls:'x-clear'});
19542         // prevent floats from wrapping when clipped
19543         this.headers.setWidth(totalWidth);
19544         //this.innerCt.setWidth(totalWidth);
19545         this.innerCt.setStyle({ overflow: 'auto' });
19546         this.onResize(this.width, this.height);
19547              
19548         
19549     },
19550     onResize : function(w,h)
19551     {
19552         this.height = h;
19553         this.width = w;
19554         // resize cols..
19555         this.innerCt.setWidth(this.width);
19556         this.innerCt.setHeight(this.height-20);
19557         
19558         // headers...
19559         var cols = this.columns, c;
19560         var totalWidth = 0;
19561         var expEl = false;
19562         var len = cols.length;
19563         for(var i = 0; i < len; i++){
19564             c = cols[i];
19565             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19566                 // it's the expander..
19567                 expEl  = this.headEls[i];
19568                 continue;
19569             }
19570             totalWidth += c.width;
19571             
19572         }
19573         if (expEl) {
19574             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19575         }
19576         this.headers.setWidth(w-20);
19577
19578         
19579         
19580         
19581     }
19582 });
19583 /*
19584  * Based on:
19585  * Ext JS Library 1.1.1
19586  * Copyright(c) 2006-2007, Ext JS, LLC.
19587  *
19588  * Originally Released Under LGPL - original licence link has changed is not relivant.
19589  *
19590  * Fork - LGPL
19591  * <script type="text/javascript">
19592  */
19593  
19594 /**
19595  * @class Roo.menu.Menu
19596  * @extends Roo.util.Observable
19597  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19598  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19599  * @constructor
19600  * Creates a new Menu
19601  * @param {Object} config Configuration options
19602  */
19603 Roo.menu.Menu = function(config){
19604     Roo.apply(this, config);
19605     this.id = this.id || Roo.id();
19606     this.addEvents({
19607         /**
19608          * @event beforeshow
19609          * Fires before this menu is displayed
19610          * @param {Roo.menu.Menu} this
19611          */
19612         beforeshow : true,
19613         /**
19614          * @event beforehide
19615          * Fires before this menu is hidden
19616          * @param {Roo.menu.Menu} this
19617          */
19618         beforehide : true,
19619         /**
19620          * @event show
19621          * Fires after this menu is displayed
19622          * @param {Roo.menu.Menu} this
19623          */
19624         show : true,
19625         /**
19626          * @event hide
19627          * Fires after this menu is hidden
19628          * @param {Roo.menu.Menu} this
19629          */
19630         hide : true,
19631         /**
19632          * @event click
19633          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19634          * @param {Roo.menu.Menu} this
19635          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19636          * @param {Roo.EventObject} e
19637          */
19638         click : true,
19639         /**
19640          * @event mouseover
19641          * Fires when the mouse is hovering over this menu
19642          * @param {Roo.menu.Menu} this
19643          * @param {Roo.EventObject} e
19644          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19645          */
19646         mouseover : true,
19647         /**
19648          * @event mouseout
19649          * Fires when the mouse exits this menu
19650          * @param {Roo.menu.Menu} this
19651          * @param {Roo.EventObject} e
19652          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19653          */
19654         mouseout : true,
19655         /**
19656          * @event itemclick
19657          * Fires when a menu item contained in this menu is clicked
19658          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19659          * @param {Roo.EventObject} e
19660          */
19661         itemclick: true
19662     });
19663     if (this.registerMenu) {
19664         Roo.menu.MenuMgr.register(this);
19665     }
19666     
19667     var mis = this.items;
19668     this.items = new Roo.util.MixedCollection();
19669     if(mis){
19670         this.add.apply(this, mis);
19671     }
19672 };
19673
19674 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19675     /**
19676      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19677      */
19678     minWidth : 120,
19679     /**
19680      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19681      * for bottom-right shadow (defaults to "sides")
19682      */
19683     shadow : "sides",
19684     /**
19685      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19686      * this menu (defaults to "tl-tr?")
19687      */
19688     subMenuAlign : "tl-tr?",
19689     /**
19690      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19691      * relative to its element of origin (defaults to "tl-bl?")
19692      */
19693     defaultAlign : "tl-bl?",
19694     /**
19695      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19696      */
19697     allowOtherMenus : false,
19698     /**
19699      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19700      */
19701     registerMenu : true,
19702
19703     hidden:true,
19704
19705     // private
19706     render : function(){
19707         if(this.el){
19708             return;
19709         }
19710         var el = this.el = new Roo.Layer({
19711             cls: "x-menu",
19712             shadow:this.shadow,
19713             constrain: false,
19714             parentEl: this.parentEl || document.body,
19715             zindex:15000
19716         });
19717
19718         this.keyNav = new Roo.menu.MenuNav(this);
19719
19720         if(this.plain){
19721             el.addClass("x-menu-plain");
19722         }
19723         if(this.cls){
19724             el.addClass(this.cls);
19725         }
19726         // generic focus element
19727         this.focusEl = el.createChild({
19728             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19729         });
19730         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19731         ul.on("click", this.onClick, this);
19732         ul.on("mouseover", this.onMouseOver, this);
19733         ul.on("mouseout", this.onMouseOut, this);
19734         this.items.each(function(item){
19735             var li = document.createElement("li");
19736             li.className = "x-menu-list-item";
19737             ul.dom.appendChild(li);
19738             item.render(li, this);
19739         }, this);
19740         this.ul = ul;
19741         this.autoWidth();
19742     },
19743
19744     // private
19745     autoWidth : function(){
19746         var el = this.el, ul = this.ul;
19747         if(!el){
19748             return;
19749         }
19750         var w = this.width;
19751         if(w){
19752             el.setWidth(w);
19753         }else if(Roo.isIE){
19754             el.setWidth(this.minWidth);
19755             var t = el.dom.offsetWidth; // force recalc
19756             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19757         }
19758     },
19759
19760     // private
19761     delayAutoWidth : function(){
19762         if(this.rendered){
19763             if(!this.awTask){
19764                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19765             }
19766             this.awTask.delay(20);
19767         }
19768     },
19769
19770     // private
19771     findTargetItem : function(e){
19772         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19773         if(t && t.menuItemId){
19774             return this.items.get(t.menuItemId);
19775         }
19776     },
19777
19778     // private
19779     onClick : function(e){
19780         var t;
19781         if(t = this.findTargetItem(e)){
19782             t.onClick(e);
19783             this.fireEvent("click", this, t, e);
19784         }
19785     },
19786
19787     // private
19788     setActiveItem : function(item, autoExpand){
19789         if(item != this.activeItem){
19790             if(this.activeItem){
19791                 this.activeItem.deactivate();
19792             }
19793             this.activeItem = item;
19794             item.activate(autoExpand);
19795         }else if(autoExpand){
19796             item.expandMenu();
19797         }
19798     },
19799
19800     // private
19801     tryActivate : function(start, step){
19802         var items = this.items;
19803         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19804             var item = items.get(i);
19805             if(!item.disabled && item.canActivate){
19806                 this.setActiveItem(item, false);
19807                 return item;
19808             }
19809         }
19810         return false;
19811     },
19812
19813     // private
19814     onMouseOver : function(e){
19815         var t;
19816         if(t = this.findTargetItem(e)){
19817             if(t.canActivate && !t.disabled){
19818                 this.setActiveItem(t, true);
19819             }
19820         }
19821         this.fireEvent("mouseover", this, e, t);
19822     },
19823
19824     // private
19825     onMouseOut : function(e){
19826         var t;
19827         if(t = this.findTargetItem(e)){
19828             if(t == this.activeItem && t.shouldDeactivate(e)){
19829                 this.activeItem.deactivate();
19830                 delete this.activeItem;
19831             }
19832         }
19833         this.fireEvent("mouseout", this, e, t);
19834     },
19835
19836     /**
19837      * Read-only.  Returns true if the menu is currently displayed, else false.
19838      * @type Boolean
19839      */
19840     isVisible : function(){
19841         return this.el && !this.hidden;
19842     },
19843
19844     /**
19845      * Displays this menu relative to another element
19846      * @param {String/HTMLElement/Roo.Element} element The element to align to
19847      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19848      * the element (defaults to this.defaultAlign)
19849      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19850      */
19851     show : function(el, pos, parentMenu){
19852         this.parentMenu = parentMenu;
19853         if(!this.el){
19854             this.render();
19855         }
19856         this.fireEvent("beforeshow", this);
19857         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19858     },
19859
19860     /**
19861      * Displays this menu at a specific xy position
19862      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19863      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19864      */
19865     showAt : function(xy, parentMenu, /* private: */_e){
19866         this.parentMenu = parentMenu;
19867         if(!this.el){
19868             this.render();
19869         }
19870         if(_e !== false){
19871             this.fireEvent("beforeshow", this);
19872             xy = this.el.adjustForConstraints(xy);
19873         }
19874         this.el.setXY(xy);
19875         this.el.show();
19876         this.hidden = false;
19877         this.focus();
19878         this.fireEvent("show", this);
19879     },
19880
19881     focus : function(){
19882         if(!this.hidden){
19883             this.doFocus.defer(50, this);
19884         }
19885     },
19886
19887     doFocus : function(){
19888         if(!this.hidden){
19889             this.focusEl.focus();
19890         }
19891     },
19892
19893     /**
19894      * Hides this menu and optionally all parent menus
19895      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19896      */
19897     hide : function(deep){
19898         if(this.el && this.isVisible()){
19899             this.fireEvent("beforehide", this);
19900             if(this.activeItem){
19901                 this.activeItem.deactivate();
19902                 this.activeItem = null;
19903             }
19904             this.el.hide();
19905             this.hidden = true;
19906             this.fireEvent("hide", this);
19907         }
19908         if(deep === true && this.parentMenu){
19909             this.parentMenu.hide(true);
19910         }
19911     },
19912
19913     /**
19914      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19915      * Any of the following are valid:
19916      * <ul>
19917      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19918      * <li>An HTMLElement object which will be converted to a menu item</li>
19919      * <li>A menu item config object that will be created as a new menu item</li>
19920      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19921      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19922      * </ul>
19923      * Usage:
19924      * <pre><code>
19925 // Create the menu
19926 var menu = new Roo.menu.Menu();
19927
19928 // Create a menu item to add by reference
19929 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19930
19931 // Add a bunch of items at once using different methods.
19932 // Only the last item added will be returned.
19933 var item = menu.add(
19934     menuItem,                // add existing item by ref
19935     'Dynamic Item',          // new TextItem
19936     '-',                     // new separator
19937     { text: 'Config Item' }  // new item by config
19938 );
19939 </code></pre>
19940      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19941      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19942      */
19943     add : function(){
19944         var a = arguments, l = a.length, item;
19945         for(var i = 0; i < l; i++){
19946             var el = a[i];
19947             if ((typeof(el) == "object") && el.xtype && el.xns) {
19948                 el = Roo.factory(el, Roo.menu);
19949             }
19950             
19951             if(el.render){ // some kind of Item
19952                 item = this.addItem(el);
19953             }else if(typeof el == "string"){ // string
19954                 if(el == "separator" || el == "-"){
19955                     item = this.addSeparator();
19956                 }else{
19957                     item = this.addText(el);
19958                 }
19959             }else if(el.tagName || el.el){ // element
19960                 item = this.addElement(el);
19961             }else if(typeof el == "object"){ // must be menu item config?
19962                 item = this.addMenuItem(el);
19963             }
19964         }
19965         return item;
19966     },
19967
19968     /**
19969      * Returns this menu's underlying {@link Roo.Element} object
19970      * @return {Roo.Element} The element
19971      */
19972     getEl : function(){
19973         if(!this.el){
19974             this.render();
19975         }
19976         return this.el;
19977     },
19978
19979     /**
19980      * Adds a separator bar to the menu
19981      * @return {Roo.menu.Item} The menu item that was added
19982      */
19983     addSeparator : function(){
19984         return this.addItem(new Roo.menu.Separator());
19985     },
19986
19987     /**
19988      * Adds an {@link Roo.Element} object to the menu
19989      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19990      * @return {Roo.menu.Item} The menu item that was added
19991      */
19992     addElement : function(el){
19993         return this.addItem(new Roo.menu.BaseItem(el));
19994     },
19995
19996     /**
19997      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19998      * @param {Roo.menu.Item} item The menu item to add
19999      * @return {Roo.menu.Item} The menu item that was added
20000      */
20001     addItem : function(item){
20002         this.items.add(item);
20003         if(this.ul){
20004             var li = document.createElement("li");
20005             li.className = "x-menu-list-item";
20006             this.ul.dom.appendChild(li);
20007             item.render(li, this);
20008             this.delayAutoWidth();
20009         }
20010         return item;
20011     },
20012
20013     /**
20014      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20015      * @param {Object} config A MenuItem config object
20016      * @return {Roo.menu.Item} The menu item that was added
20017      */
20018     addMenuItem : function(config){
20019         if(!(config instanceof Roo.menu.Item)){
20020             if(typeof config.checked == "boolean"){ // must be check menu item config?
20021                 config = new Roo.menu.CheckItem(config);
20022             }else{
20023                 config = new Roo.menu.Item(config);
20024             }
20025         }
20026         return this.addItem(config);
20027     },
20028
20029     /**
20030      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20031      * @param {String} text The text to display in the menu item
20032      * @return {Roo.menu.Item} The menu item that was added
20033      */
20034     addText : function(text){
20035         return this.addItem(new Roo.menu.TextItem({ text : text }));
20036     },
20037
20038     /**
20039      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20040      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20041      * @param {Roo.menu.Item} item The menu item to add
20042      * @return {Roo.menu.Item} The menu item that was added
20043      */
20044     insert : function(index, item){
20045         this.items.insert(index, item);
20046         if(this.ul){
20047             var li = document.createElement("li");
20048             li.className = "x-menu-list-item";
20049             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20050             item.render(li, this);
20051             this.delayAutoWidth();
20052         }
20053         return item;
20054     },
20055
20056     /**
20057      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20058      * @param {Roo.menu.Item} item The menu item to remove
20059      */
20060     remove : function(item){
20061         this.items.removeKey(item.id);
20062         item.destroy();
20063     },
20064
20065     /**
20066      * Removes and destroys all items in the menu
20067      */
20068     removeAll : function(){
20069         var f;
20070         while(f = this.items.first()){
20071             this.remove(f);
20072         }
20073     }
20074 });
20075
20076 // MenuNav is a private utility class used internally by the Menu
20077 Roo.menu.MenuNav = function(menu){
20078     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20079     this.scope = this.menu = menu;
20080 };
20081
20082 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20083     doRelay : function(e, h){
20084         var k = e.getKey();
20085         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20086             this.menu.tryActivate(0, 1);
20087             return false;
20088         }
20089         return h.call(this.scope || this, e, this.menu);
20090     },
20091
20092     up : function(e, m){
20093         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20094             m.tryActivate(m.items.length-1, -1);
20095         }
20096     },
20097
20098     down : function(e, m){
20099         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20100             m.tryActivate(0, 1);
20101         }
20102     },
20103
20104     right : function(e, m){
20105         if(m.activeItem){
20106             m.activeItem.expandMenu(true);
20107         }
20108     },
20109
20110     left : function(e, m){
20111         m.hide();
20112         if(m.parentMenu && m.parentMenu.activeItem){
20113             m.parentMenu.activeItem.activate();
20114         }
20115     },
20116
20117     enter : function(e, m){
20118         if(m.activeItem){
20119             e.stopPropagation();
20120             m.activeItem.onClick(e);
20121             m.fireEvent("click", this, m.activeItem);
20122             return true;
20123         }
20124     }
20125 });/*
20126  * Based on:
20127  * Ext JS Library 1.1.1
20128  * Copyright(c) 2006-2007, Ext JS, LLC.
20129  *
20130  * Originally Released Under LGPL - original licence link has changed is not relivant.
20131  *
20132  * Fork - LGPL
20133  * <script type="text/javascript">
20134  */
20135  
20136 /**
20137  * @class Roo.menu.MenuMgr
20138  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20139  * @singleton
20140  */
20141 Roo.menu.MenuMgr = function(){
20142    var menus, active, groups = {}, attached = false, lastShow = new Date();
20143
20144    // private - called when first menu is created
20145    function init(){
20146        menus = {};
20147        active = new Roo.util.MixedCollection();
20148        Roo.get(document).addKeyListener(27, function(){
20149            if(active.length > 0){
20150                hideAll();
20151            }
20152        });
20153    }
20154
20155    // private
20156    function hideAll(){
20157        if(active && active.length > 0){
20158            var c = active.clone();
20159            c.each(function(m){
20160                m.hide();
20161            });
20162        }
20163    }
20164
20165    // private
20166    function onHide(m){
20167        active.remove(m);
20168        if(active.length < 1){
20169            Roo.get(document).un("mousedown", onMouseDown);
20170            attached = false;
20171        }
20172    }
20173
20174    // private
20175    function onShow(m){
20176        var last = active.last();
20177        lastShow = new Date();
20178        active.add(m);
20179        if(!attached){
20180            Roo.get(document).on("mousedown", onMouseDown);
20181            attached = true;
20182        }
20183        if(m.parentMenu){
20184           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20185           m.parentMenu.activeChild = m;
20186        }else if(last && last.isVisible()){
20187           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20188        }
20189    }
20190
20191    // private
20192    function onBeforeHide(m){
20193        if(m.activeChild){
20194            m.activeChild.hide();
20195        }
20196        if(m.autoHideTimer){
20197            clearTimeout(m.autoHideTimer);
20198            delete m.autoHideTimer;
20199        }
20200    }
20201
20202    // private
20203    function onBeforeShow(m){
20204        var pm = m.parentMenu;
20205        if(!pm && !m.allowOtherMenus){
20206            hideAll();
20207        }else if(pm && pm.activeChild && active != m){
20208            pm.activeChild.hide();
20209        }
20210    }
20211
20212    // private
20213    function onMouseDown(e){
20214        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20215            hideAll();
20216        }
20217    }
20218
20219    // private
20220    function onBeforeCheck(mi, state){
20221        if(state){
20222            var g = groups[mi.group];
20223            for(var i = 0, l = g.length; i < l; i++){
20224                if(g[i] != mi){
20225                    g[i].setChecked(false);
20226                }
20227            }
20228        }
20229    }
20230
20231    return {
20232
20233        /**
20234         * Hides all menus that are currently visible
20235         */
20236        hideAll : function(){
20237             hideAll();  
20238        },
20239
20240        // private
20241        register : function(menu){
20242            if(!menus){
20243                init();
20244            }
20245            menus[menu.id] = menu;
20246            menu.on("beforehide", onBeforeHide);
20247            menu.on("hide", onHide);
20248            menu.on("beforeshow", onBeforeShow);
20249            menu.on("show", onShow);
20250            var g = menu.group;
20251            if(g && menu.events["checkchange"]){
20252                if(!groups[g]){
20253                    groups[g] = [];
20254                }
20255                groups[g].push(menu);
20256                menu.on("checkchange", onCheck);
20257            }
20258        },
20259
20260         /**
20261          * Returns a {@link Roo.menu.Menu} object
20262          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20263          * be used to generate and return a new Menu instance.
20264          */
20265        get : function(menu){
20266            if(typeof menu == "string"){ // menu id
20267                return menus[menu];
20268            }else if(menu.events){  // menu instance
20269                return menu;
20270            }else if(typeof menu.length == 'number'){ // array of menu items?
20271                return new Roo.menu.Menu({items:menu});
20272            }else{ // otherwise, must be a config
20273                return new Roo.menu.Menu(menu);
20274            }
20275        },
20276
20277        // private
20278        unregister : function(menu){
20279            delete menus[menu.id];
20280            menu.un("beforehide", onBeforeHide);
20281            menu.un("hide", onHide);
20282            menu.un("beforeshow", onBeforeShow);
20283            menu.un("show", onShow);
20284            var g = menu.group;
20285            if(g && menu.events["checkchange"]){
20286                groups[g].remove(menu);
20287                menu.un("checkchange", onCheck);
20288            }
20289        },
20290
20291        // private
20292        registerCheckable : function(menuItem){
20293            var g = menuItem.group;
20294            if(g){
20295                if(!groups[g]){
20296                    groups[g] = [];
20297                }
20298                groups[g].push(menuItem);
20299                menuItem.on("beforecheckchange", onBeforeCheck);
20300            }
20301        },
20302
20303        // private
20304        unregisterCheckable : function(menuItem){
20305            var g = menuItem.group;
20306            if(g){
20307                groups[g].remove(menuItem);
20308                menuItem.un("beforecheckchange", onBeforeCheck);
20309            }
20310        }
20311    };
20312 }();/*
20313  * Based on:
20314  * Ext JS Library 1.1.1
20315  * Copyright(c) 2006-2007, Ext JS, LLC.
20316  *
20317  * Originally Released Under LGPL - original licence link has changed is not relivant.
20318  *
20319  * Fork - LGPL
20320  * <script type="text/javascript">
20321  */
20322  
20323
20324 /**
20325  * @class Roo.menu.BaseItem
20326  * @extends Roo.Component
20327  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20328  * management and base configuration options shared by all menu components.
20329  * @constructor
20330  * Creates a new BaseItem
20331  * @param {Object} config Configuration options
20332  */
20333 Roo.menu.BaseItem = function(config){
20334     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20335
20336     this.addEvents({
20337         /**
20338          * @event click
20339          * Fires when this item is clicked
20340          * @param {Roo.menu.BaseItem} this
20341          * @param {Roo.EventObject} e
20342          */
20343         click: true,
20344         /**
20345          * @event activate
20346          * Fires when this item is activated
20347          * @param {Roo.menu.BaseItem} this
20348          */
20349         activate : true,
20350         /**
20351          * @event deactivate
20352          * Fires when this item is deactivated
20353          * @param {Roo.menu.BaseItem} this
20354          */
20355         deactivate : true
20356     });
20357
20358     if(this.handler){
20359         this.on("click", this.handler, this.scope, true);
20360     }
20361 };
20362
20363 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20364     /**
20365      * @cfg {Function} handler
20366      * A function that will handle the click event of this menu item (defaults to undefined)
20367      */
20368     /**
20369      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20370      */
20371     canActivate : false,
20372     /**
20373      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20374      */
20375     activeClass : "x-menu-item-active",
20376     /**
20377      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20378      */
20379     hideOnClick : true,
20380     /**
20381      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20382      */
20383     hideDelay : 100,
20384
20385     // private
20386     ctype: "Roo.menu.BaseItem",
20387
20388     // private
20389     actionMode : "container",
20390
20391     // private
20392     render : function(container, parentMenu){
20393         this.parentMenu = parentMenu;
20394         Roo.menu.BaseItem.superclass.render.call(this, container);
20395         this.container.menuItemId = this.id;
20396     },
20397
20398     // private
20399     onRender : function(container, position){
20400         this.el = Roo.get(this.el);
20401         container.dom.appendChild(this.el.dom);
20402     },
20403
20404     // private
20405     onClick : function(e){
20406         if(!this.disabled && this.fireEvent("click", this, e) !== false
20407                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20408             this.handleClick(e);
20409         }else{
20410             e.stopEvent();
20411         }
20412     },
20413
20414     // private
20415     activate : function(){
20416         if(this.disabled){
20417             return false;
20418         }
20419         var li = this.container;
20420         li.addClass(this.activeClass);
20421         this.region = li.getRegion().adjust(2, 2, -2, -2);
20422         this.fireEvent("activate", this);
20423         return true;
20424     },
20425
20426     // private
20427     deactivate : function(){
20428         this.container.removeClass(this.activeClass);
20429         this.fireEvent("deactivate", this);
20430     },
20431
20432     // private
20433     shouldDeactivate : function(e){
20434         return !this.region || !this.region.contains(e.getPoint());
20435     },
20436
20437     // private
20438     handleClick : function(e){
20439         if(this.hideOnClick){
20440             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20441         }
20442     },
20443
20444     // private
20445     expandMenu : function(autoActivate){
20446         // do nothing
20447     },
20448
20449     // private
20450     hideMenu : function(){
20451         // do nothing
20452     }
20453 });/*
20454  * Based on:
20455  * Ext JS Library 1.1.1
20456  * Copyright(c) 2006-2007, Ext JS, LLC.
20457  *
20458  * Originally Released Under LGPL - original licence link has changed is not relivant.
20459  *
20460  * Fork - LGPL
20461  * <script type="text/javascript">
20462  */
20463  
20464 /**
20465  * @class Roo.menu.Adapter
20466  * @extends Roo.menu.BaseItem
20467  * 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.
20468  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20469  * @constructor
20470  * Creates a new Adapter
20471  * @param {Object} config Configuration options
20472  */
20473 Roo.menu.Adapter = function(component, config){
20474     Roo.menu.Adapter.superclass.constructor.call(this, config);
20475     this.component = component;
20476 };
20477 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20478     // private
20479     canActivate : true,
20480
20481     // private
20482     onRender : function(container, position){
20483         this.component.render(container);
20484         this.el = this.component.getEl();
20485     },
20486
20487     // private
20488     activate : function(){
20489         if(this.disabled){
20490             return false;
20491         }
20492         this.component.focus();
20493         this.fireEvent("activate", this);
20494         return true;
20495     },
20496
20497     // private
20498     deactivate : function(){
20499         this.fireEvent("deactivate", this);
20500     },
20501
20502     // private
20503     disable : function(){
20504         this.component.disable();
20505         Roo.menu.Adapter.superclass.disable.call(this);
20506     },
20507
20508     // private
20509     enable : function(){
20510         this.component.enable();
20511         Roo.menu.Adapter.superclass.enable.call(this);
20512     }
20513 });/*
20514  * Based on:
20515  * Ext JS Library 1.1.1
20516  * Copyright(c) 2006-2007, Ext JS, LLC.
20517  *
20518  * Originally Released Under LGPL - original licence link has changed is not relivant.
20519  *
20520  * Fork - LGPL
20521  * <script type="text/javascript">
20522  */
20523
20524 /**
20525  * @class Roo.menu.TextItem
20526  * @extends Roo.menu.BaseItem
20527  * Adds a static text string to a menu, usually used as either a heading or group separator.
20528  * Note: old style constructor with text is still supported.
20529  * 
20530  * @constructor
20531  * Creates a new TextItem
20532  * @param {Object} cfg Configuration
20533  */
20534 Roo.menu.TextItem = function(cfg){
20535     if (typeof(cfg) == 'string') {
20536         this.text = cfg;
20537     } else {
20538         Roo.apply(this,cfg);
20539     }
20540     
20541     Roo.menu.TextItem.superclass.constructor.call(this);
20542 };
20543
20544 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20545     /**
20546      * @cfg {Boolean} text Text to show on item.
20547      */
20548     text : '',
20549     
20550     /**
20551      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20552      */
20553     hideOnClick : false,
20554     /**
20555      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20556      */
20557     itemCls : "x-menu-text",
20558
20559     // private
20560     onRender : function(){
20561         var s = document.createElement("span");
20562         s.className = this.itemCls;
20563         s.innerHTML = this.text;
20564         this.el = s;
20565         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20566     }
20567 });/*
20568  * Based on:
20569  * Ext JS Library 1.1.1
20570  * Copyright(c) 2006-2007, Ext JS, LLC.
20571  *
20572  * Originally Released Under LGPL - original licence link has changed is not relivant.
20573  *
20574  * Fork - LGPL
20575  * <script type="text/javascript">
20576  */
20577
20578 /**
20579  * @class Roo.menu.Separator
20580  * @extends Roo.menu.BaseItem
20581  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20582  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20583  * @constructor
20584  * @param {Object} config Configuration options
20585  */
20586 Roo.menu.Separator = function(config){
20587     Roo.menu.Separator.superclass.constructor.call(this, config);
20588 };
20589
20590 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20591     /**
20592      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20593      */
20594     itemCls : "x-menu-sep",
20595     /**
20596      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20597      */
20598     hideOnClick : false,
20599
20600     // private
20601     onRender : function(li){
20602         var s = document.createElement("span");
20603         s.className = this.itemCls;
20604         s.innerHTML = "&#160;";
20605         this.el = s;
20606         li.addClass("x-menu-sep-li");
20607         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20608     }
20609 });/*
20610  * Based on:
20611  * Ext JS Library 1.1.1
20612  * Copyright(c) 2006-2007, Ext JS, LLC.
20613  *
20614  * Originally Released Under LGPL - original licence link has changed is not relivant.
20615  *
20616  * Fork - LGPL
20617  * <script type="text/javascript">
20618  */
20619 /**
20620  * @class Roo.menu.Item
20621  * @extends Roo.menu.BaseItem
20622  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20623  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20624  * activation and click handling.
20625  * @constructor
20626  * Creates a new Item
20627  * @param {Object} config Configuration options
20628  */
20629 Roo.menu.Item = function(config){
20630     Roo.menu.Item.superclass.constructor.call(this, config);
20631     if(this.menu){
20632         this.menu = Roo.menu.MenuMgr.get(this.menu);
20633     }
20634 };
20635 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20636     
20637     /**
20638      * @cfg {String} text
20639      * The text to show on the menu item.
20640      */
20641     text: '',
20642      /**
20643      * @cfg {String} HTML to render in menu
20644      * The text to show on the menu item (HTML version).
20645      */
20646     html: '',
20647     /**
20648      * @cfg {String} icon
20649      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20650      */
20651     icon: undefined,
20652     /**
20653      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20654      */
20655     itemCls : "x-menu-item",
20656     /**
20657      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20658      */
20659     canActivate : true,
20660     /**
20661      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20662      */
20663     showDelay: 200,
20664     // doc'd in BaseItem
20665     hideDelay: 200,
20666
20667     // private
20668     ctype: "Roo.menu.Item",
20669     
20670     // private
20671     onRender : function(container, position){
20672         var el = document.createElement("a");
20673         el.hideFocus = true;
20674         el.unselectable = "on";
20675         el.href = this.href || "#";
20676         if(this.hrefTarget){
20677             el.target = this.hrefTarget;
20678         }
20679         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20680         
20681         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20682         
20683         el.innerHTML = String.format(
20684                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20685                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20686         this.el = el;
20687         Roo.menu.Item.superclass.onRender.call(this, container, position);
20688     },
20689
20690     /**
20691      * Sets the text to display in this menu item
20692      * @param {String} text The text to display
20693      * @param {Boolean} isHTML true to indicate text is pure html.
20694      */
20695     setText : function(text, isHTML){
20696         if (isHTML) {
20697             this.html = text;
20698         } else {
20699             this.text = text;
20700             this.html = '';
20701         }
20702         if(this.rendered){
20703             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20704      
20705             this.el.update(String.format(
20706                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20707                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20708             this.parentMenu.autoWidth();
20709         }
20710     },
20711
20712     // private
20713     handleClick : function(e){
20714         if(!this.href){ // if no link defined, stop the event automatically
20715             e.stopEvent();
20716         }
20717         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20718     },
20719
20720     // private
20721     activate : function(autoExpand){
20722         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20723             this.focus();
20724             if(autoExpand){
20725                 this.expandMenu();
20726             }
20727         }
20728         return true;
20729     },
20730
20731     // private
20732     shouldDeactivate : function(e){
20733         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20734             if(this.menu && this.menu.isVisible()){
20735                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20736             }
20737             return true;
20738         }
20739         return false;
20740     },
20741
20742     // private
20743     deactivate : function(){
20744         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20745         this.hideMenu();
20746     },
20747
20748     // private
20749     expandMenu : function(autoActivate){
20750         if(!this.disabled && this.menu){
20751             clearTimeout(this.hideTimer);
20752             delete this.hideTimer;
20753             if(!this.menu.isVisible() && !this.showTimer){
20754                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20755             }else if (this.menu.isVisible() && autoActivate){
20756                 this.menu.tryActivate(0, 1);
20757             }
20758         }
20759     },
20760
20761     // private
20762     deferExpand : function(autoActivate){
20763         delete this.showTimer;
20764         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20765         if(autoActivate){
20766             this.menu.tryActivate(0, 1);
20767         }
20768     },
20769
20770     // private
20771     hideMenu : function(){
20772         clearTimeout(this.showTimer);
20773         delete this.showTimer;
20774         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20775             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20776         }
20777     },
20778
20779     // private
20780     deferHide : function(){
20781         delete this.hideTimer;
20782         this.menu.hide();
20783     }
20784 });/*
20785  * Based on:
20786  * Ext JS Library 1.1.1
20787  * Copyright(c) 2006-2007, Ext JS, LLC.
20788  *
20789  * Originally Released Under LGPL - original licence link has changed is not relivant.
20790  *
20791  * Fork - LGPL
20792  * <script type="text/javascript">
20793  */
20794  
20795 /**
20796  * @class Roo.menu.CheckItem
20797  * @extends Roo.menu.Item
20798  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20799  * @constructor
20800  * Creates a new CheckItem
20801  * @param {Object} config Configuration options
20802  */
20803 Roo.menu.CheckItem = function(config){
20804     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20805     this.addEvents({
20806         /**
20807          * @event beforecheckchange
20808          * Fires before the checked value is set, providing an opportunity to cancel if needed
20809          * @param {Roo.menu.CheckItem} this
20810          * @param {Boolean} checked The new checked value that will be set
20811          */
20812         "beforecheckchange" : true,
20813         /**
20814          * @event checkchange
20815          * Fires after the checked value has been set
20816          * @param {Roo.menu.CheckItem} this
20817          * @param {Boolean} checked The checked value that was set
20818          */
20819         "checkchange" : true
20820     });
20821     if(this.checkHandler){
20822         this.on('checkchange', this.checkHandler, this.scope);
20823     }
20824 };
20825 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20826     /**
20827      * @cfg {String} group
20828      * All check items with the same group name will automatically be grouped into a single-select
20829      * radio button group (defaults to '')
20830      */
20831     /**
20832      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20833      */
20834     itemCls : "x-menu-item x-menu-check-item",
20835     /**
20836      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20837      */
20838     groupClass : "x-menu-group-item",
20839
20840     /**
20841      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20842      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20843      * initialized with checked = true will be rendered as checked.
20844      */
20845     checked: false,
20846
20847     // private
20848     ctype: "Roo.menu.CheckItem",
20849
20850     // private
20851     onRender : function(c){
20852         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20853         if(this.group){
20854             this.el.addClass(this.groupClass);
20855         }
20856         Roo.menu.MenuMgr.registerCheckable(this);
20857         if(this.checked){
20858             this.checked = false;
20859             this.setChecked(true, true);
20860         }
20861     },
20862
20863     // private
20864     destroy : function(){
20865         if(this.rendered){
20866             Roo.menu.MenuMgr.unregisterCheckable(this);
20867         }
20868         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20869     },
20870
20871     /**
20872      * Set the checked state of this item
20873      * @param {Boolean} checked The new checked value
20874      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20875      */
20876     setChecked : function(state, suppressEvent){
20877         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20878             if(this.container){
20879                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20880             }
20881             this.checked = state;
20882             if(suppressEvent !== true){
20883                 this.fireEvent("checkchange", this, state);
20884             }
20885         }
20886     },
20887
20888     // private
20889     handleClick : function(e){
20890        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20891            this.setChecked(!this.checked);
20892        }
20893        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20894     }
20895 });/*
20896  * Based on:
20897  * Ext JS Library 1.1.1
20898  * Copyright(c) 2006-2007, Ext JS, LLC.
20899  *
20900  * Originally Released Under LGPL - original licence link has changed is not relivant.
20901  *
20902  * Fork - LGPL
20903  * <script type="text/javascript">
20904  */
20905  
20906 /**
20907  * @class Roo.menu.DateItem
20908  * @extends Roo.menu.Adapter
20909  * A menu item that wraps the {@link Roo.DatPicker} component.
20910  * @constructor
20911  * Creates a new DateItem
20912  * @param {Object} config Configuration options
20913  */
20914 Roo.menu.DateItem = function(config){
20915     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20916     /** The Roo.DatePicker object @type Roo.DatePicker */
20917     this.picker = this.component;
20918     this.addEvents({select: true});
20919     
20920     this.picker.on("render", function(picker){
20921         picker.getEl().swallowEvent("click");
20922         picker.container.addClass("x-menu-date-item");
20923     });
20924
20925     this.picker.on("select", this.onSelect, this);
20926 };
20927
20928 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20929     // private
20930     onSelect : function(picker, date){
20931         this.fireEvent("select", this, date, picker);
20932         Roo.menu.DateItem.superclass.handleClick.call(this);
20933     }
20934 });/*
20935  * Based on:
20936  * Ext JS Library 1.1.1
20937  * Copyright(c) 2006-2007, Ext JS, LLC.
20938  *
20939  * Originally Released Under LGPL - original licence link has changed is not relivant.
20940  *
20941  * Fork - LGPL
20942  * <script type="text/javascript">
20943  */
20944  
20945 /**
20946  * @class Roo.menu.ColorItem
20947  * @extends Roo.menu.Adapter
20948  * A menu item that wraps the {@link Roo.ColorPalette} component.
20949  * @constructor
20950  * Creates a new ColorItem
20951  * @param {Object} config Configuration options
20952  */
20953 Roo.menu.ColorItem = function(config){
20954     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20955     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20956     this.palette = this.component;
20957     this.relayEvents(this.palette, ["select"]);
20958     if(this.selectHandler){
20959         this.on('select', this.selectHandler, this.scope);
20960     }
20961 };
20962 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20963  * Based on:
20964  * Ext JS Library 1.1.1
20965  * Copyright(c) 2006-2007, Ext JS, LLC.
20966  *
20967  * Originally Released Under LGPL - original licence link has changed is not relivant.
20968  *
20969  * Fork - LGPL
20970  * <script type="text/javascript">
20971  */
20972  
20973
20974 /**
20975  * @class Roo.menu.DateMenu
20976  * @extends Roo.menu.Menu
20977  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20978  * @constructor
20979  * Creates a new DateMenu
20980  * @param {Object} config Configuration options
20981  */
20982 Roo.menu.DateMenu = function(config){
20983     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20984     this.plain = true;
20985     var di = new Roo.menu.DateItem(config);
20986     this.add(di);
20987     /**
20988      * The {@link Roo.DatePicker} instance for this DateMenu
20989      * @type DatePicker
20990      */
20991     this.picker = di.picker;
20992     /**
20993      * @event select
20994      * @param {DatePicker} picker
20995      * @param {Date} date
20996      */
20997     this.relayEvents(di, ["select"]);
20998
20999     this.on('beforeshow', function(){
21000         if(this.picker){
21001             this.picker.hideMonthPicker(true);
21002         }
21003     }, this);
21004 };
21005 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21006     cls:'x-date-menu'
21007 });/*
21008  * Based on:
21009  * Ext JS Library 1.1.1
21010  * Copyright(c) 2006-2007, Ext JS, LLC.
21011  *
21012  * Originally Released Under LGPL - original licence link has changed is not relivant.
21013  *
21014  * Fork - LGPL
21015  * <script type="text/javascript">
21016  */
21017  
21018
21019 /**
21020  * @class Roo.menu.ColorMenu
21021  * @extends Roo.menu.Menu
21022  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21023  * @constructor
21024  * Creates a new ColorMenu
21025  * @param {Object} config Configuration options
21026  */
21027 Roo.menu.ColorMenu = function(config){
21028     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21029     this.plain = true;
21030     var ci = new Roo.menu.ColorItem(config);
21031     this.add(ci);
21032     /**
21033      * The {@link Roo.ColorPalette} instance for this ColorMenu
21034      * @type ColorPalette
21035      */
21036     this.palette = ci.palette;
21037     /**
21038      * @event select
21039      * @param {ColorPalette} palette
21040      * @param {String} color
21041      */
21042     this.relayEvents(ci, ["select"]);
21043 };
21044 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21045  * Based on:
21046  * Ext JS Library 1.1.1
21047  * Copyright(c) 2006-2007, Ext JS, LLC.
21048  *
21049  * Originally Released Under LGPL - original licence link has changed is not relivant.
21050  *
21051  * Fork - LGPL
21052  * <script type="text/javascript">
21053  */
21054  
21055 /**
21056  * @class Roo.form.Field
21057  * @extends Roo.BoxComponent
21058  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21059  * @constructor
21060  * Creates a new Field
21061  * @param {Object} config Configuration options
21062  */
21063 Roo.form.Field = function(config){
21064     Roo.form.Field.superclass.constructor.call(this, config);
21065 };
21066
21067 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21068     /**
21069      * @cfg {String} fieldLabel Label to use when rendering a form.
21070      */
21071        /**
21072      * @cfg {String} qtip Mouse over tip
21073      */
21074      
21075     /**
21076      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21077      */
21078     invalidClass : "x-form-invalid",
21079     /**
21080      * @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")
21081      */
21082     invalidText : "The value in this field is invalid",
21083     /**
21084      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21085      */
21086     focusClass : "x-form-focus",
21087     /**
21088      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21089       automatic validation (defaults to "keyup").
21090      */
21091     validationEvent : "keyup",
21092     /**
21093      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21094      */
21095     validateOnBlur : true,
21096     /**
21097      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21098      */
21099     validationDelay : 250,
21100     /**
21101      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21102      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21103      */
21104     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21105     /**
21106      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21107      */
21108     fieldClass : "x-form-field",
21109     /**
21110      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21111      *<pre>
21112 Value         Description
21113 -----------   ----------------------------------------------------------------------
21114 qtip          Display a quick tip when the user hovers over the field
21115 title         Display a default browser title attribute popup
21116 under         Add a block div beneath the field containing the error text
21117 side          Add an error icon to the right of the field with a popup on hover
21118 [element id]  Add the error text directly to the innerHTML of the specified element
21119 </pre>
21120      */
21121     msgTarget : 'qtip',
21122     /**
21123      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21124      */
21125     msgFx : 'normal',
21126
21127     /**
21128      * @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.
21129      */
21130     readOnly : false,
21131
21132     /**
21133      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21134      */
21135     disabled : false,
21136
21137     /**
21138      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21139      */
21140     inputType : undefined,
21141     
21142     /**
21143      * @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).
21144          */
21145         tabIndex : undefined,
21146         
21147     // private
21148     isFormField : true,
21149
21150     // private
21151     hasFocus : false,
21152     /**
21153      * @property {Roo.Element} fieldEl
21154      * Element Containing the rendered Field (with label etc.)
21155      */
21156     /**
21157      * @cfg {Mixed} value A value to initialize this field with.
21158      */
21159     value : undefined,
21160
21161     /**
21162      * @cfg {String} name The field's HTML name attribute.
21163      */
21164     /**
21165      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21166      */
21167
21168         // private ??
21169         initComponent : function(){
21170         Roo.form.Field.superclass.initComponent.call(this);
21171         this.addEvents({
21172             /**
21173              * @event focus
21174              * Fires when this field receives input focus.
21175              * @param {Roo.form.Field} this
21176              */
21177             focus : true,
21178             /**
21179              * @event blur
21180              * Fires when this field loses input focus.
21181              * @param {Roo.form.Field} this
21182              */
21183             blur : true,
21184             /**
21185              * @event specialkey
21186              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21187              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21188              * @param {Roo.form.Field} this
21189              * @param {Roo.EventObject} e The event object
21190              */
21191             specialkey : true,
21192             /**
21193              * @event change
21194              * Fires just before the field blurs if the field value has changed.
21195              * @param {Roo.form.Field} this
21196              * @param {Mixed} newValue The new value
21197              * @param {Mixed} oldValue The original value
21198              */
21199             change : true,
21200             /**
21201              * @event invalid
21202              * Fires after the field has been marked as invalid.
21203              * @param {Roo.form.Field} this
21204              * @param {String} msg The validation message
21205              */
21206             invalid : true,
21207             /**
21208              * @event valid
21209              * Fires after the field has been validated with no errors.
21210              * @param {Roo.form.Field} this
21211              */
21212             valid : true,
21213              /**
21214              * @event keyup
21215              * Fires after the key up
21216              * @param {Roo.form.Field} this
21217              * @param {Roo.EventObject}  e The event Object
21218              */
21219             keyup : true
21220         });
21221     },
21222
21223     /**
21224      * Returns the name attribute of the field if available
21225      * @return {String} name The field name
21226      */
21227     getName: function(){
21228          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21229     },
21230
21231     // private
21232     onRender : function(ct, position){
21233         Roo.form.Field.superclass.onRender.call(this, ct, position);
21234         if(!this.el){
21235             var cfg = this.getAutoCreate();
21236             if(!cfg.name){
21237                 cfg.name = this.name || this.id;
21238             }
21239             if(this.inputType){
21240                 cfg.type = this.inputType;
21241             }
21242             this.el = ct.createChild(cfg, position);
21243         }
21244         var type = this.el.dom.type;
21245         if(type){
21246             if(type == 'password'){
21247                 type = 'text';
21248             }
21249             this.el.addClass('x-form-'+type);
21250         }
21251         if(this.readOnly){
21252             this.el.dom.readOnly = true;
21253         }
21254         if(this.tabIndex !== undefined){
21255             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21256         }
21257
21258         this.el.addClass([this.fieldClass, this.cls]);
21259         this.initValue();
21260     },
21261
21262     /**
21263      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21264      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21265      * @return {Roo.form.Field} this
21266      */
21267     applyTo : function(target){
21268         this.allowDomMove = false;
21269         this.el = Roo.get(target);
21270         this.render(this.el.dom.parentNode);
21271         return this;
21272     },
21273
21274     // private
21275     initValue : function(){
21276         if(this.value !== undefined){
21277             this.setValue(this.value);
21278         }else if(this.el.dom.value.length > 0){
21279             this.setValue(this.el.dom.value);
21280         }
21281     },
21282
21283     /**
21284      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21285      */
21286     isDirty : function() {
21287         if(this.disabled) {
21288             return false;
21289         }
21290         return String(this.getValue()) !== String(this.originalValue);
21291     },
21292
21293     // private
21294     afterRender : function(){
21295         Roo.form.Field.superclass.afterRender.call(this);
21296         this.initEvents();
21297     },
21298
21299     // private
21300     fireKey : function(e){
21301         //Roo.log('field ' + e.getKey());
21302         if(e.isNavKeyPress()){
21303             this.fireEvent("specialkey", this, e);
21304         }
21305     },
21306
21307     /**
21308      * Resets the current field value to the originally loaded value and clears any validation messages
21309      */
21310     reset : function(){
21311         this.setValue(this.originalValue);
21312         this.clearInvalid();
21313     },
21314
21315     // private
21316     initEvents : function(){
21317         // safari killled keypress - so keydown is now used..
21318         this.el.on("keydown" , this.fireKey,  this);
21319         this.el.on("focus", this.onFocus,  this);
21320         this.el.on("blur", this.onBlur,  this);
21321         this.el.relayEvent('keyup', this);
21322
21323         // reference to original value for reset
21324         this.originalValue = this.getValue();
21325     },
21326
21327     // private
21328     onFocus : function(){
21329         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21330             this.el.addClass(this.focusClass);
21331         }
21332         if(!this.hasFocus){
21333             this.hasFocus = true;
21334             this.startValue = this.getValue();
21335             this.fireEvent("focus", this);
21336         }
21337     },
21338
21339     beforeBlur : Roo.emptyFn,
21340
21341     // private
21342     onBlur : function(){
21343         this.beforeBlur();
21344         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21345             this.el.removeClass(this.focusClass);
21346         }
21347         this.hasFocus = false;
21348         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21349             this.validate();
21350         }
21351         var v = this.getValue();
21352         if(String(v) !== String(this.startValue)){
21353             this.fireEvent('change', this, v, this.startValue);
21354         }
21355         this.fireEvent("blur", this);
21356     },
21357
21358     /**
21359      * Returns whether or not the field value is currently valid
21360      * @param {Boolean} preventMark True to disable marking the field invalid
21361      * @return {Boolean} True if the value is valid, else false
21362      */
21363     isValid : function(preventMark){
21364         if(this.disabled){
21365             return true;
21366         }
21367         var restore = this.preventMark;
21368         this.preventMark = preventMark === true;
21369         var v = this.validateValue(this.processValue(this.getRawValue()));
21370         this.preventMark = restore;
21371         return v;
21372     },
21373
21374     /**
21375      * Validates the field value
21376      * @return {Boolean} True if the value is valid, else false
21377      */
21378     validate : function(){
21379         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21380             this.clearInvalid();
21381             return true;
21382         }
21383         return false;
21384     },
21385
21386     processValue : function(value){
21387         return value;
21388     },
21389
21390     // private
21391     // Subclasses should provide the validation implementation by overriding this
21392     validateValue : function(value){
21393         return true;
21394     },
21395
21396     /**
21397      * Mark this field as invalid
21398      * @param {String} msg The validation message
21399      */
21400     markInvalid : function(msg){
21401         if(!this.rendered || this.preventMark){ // not rendered
21402             return;
21403         }
21404         this.el.addClass(this.invalidClass);
21405         msg = msg || this.invalidText;
21406         switch(this.msgTarget){
21407             case 'qtip':
21408                 this.el.dom.qtip = msg;
21409                 this.el.dom.qclass = 'x-form-invalid-tip';
21410                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21411                     Roo.QuickTips.enable();
21412                 }
21413                 break;
21414             case 'title':
21415                 this.el.dom.title = msg;
21416                 break;
21417             case 'under':
21418                 if(!this.errorEl){
21419                     var elp = this.el.findParent('.x-form-element', 5, true);
21420                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21421                     this.errorEl.setWidth(elp.getWidth(true)-20);
21422                 }
21423                 this.errorEl.update(msg);
21424                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21425                 break;
21426             case 'side':
21427                 if(!this.errorIcon){
21428                     var elp = this.el.findParent('.x-form-element', 5, true);
21429                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21430                 }
21431                 this.alignErrorIcon();
21432                 this.errorIcon.dom.qtip = msg;
21433                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21434                 this.errorIcon.show();
21435                 this.on('resize', this.alignErrorIcon, this);
21436                 break;
21437             default:
21438                 var t = Roo.getDom(this.msgTarget);
21439                 t.innerHTML = msg;
21440                 t.style.display = this.msgDisplay;
21441                 break;
21442         }
21443         this.fireEvent('invalid', this, msg);
21444     },
21445
21446     // private
21447     alignErrorIcon : function(){
21448         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21449     },
21450
21451     /**
21452      * Clear any invalid styles/messages for this field
21453      */
21454     clearInvalid : function(){
21455         if(!this.rendered || this.preventMark){ // not rendered
21456             return;
21457         }
21458         this.el.removeClass(this.invalidClass);
21459         switch(this.msgTarget){
21460             case 'qtip':
21461                 this.el.dom.qtip = '';
21462                 break;
21463             case 'title':
21464                 this.el.dom.title = '';
21465                 break;
21466             case 'under':
21467                 if(this.errorEl){
21468                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21469                 }
21470                 break;
21471             case 'side':
21472                 if(this.errorIcon){
21473                     this.errorIcon.dom.qtip = '';
21474                     this.errorIcon.hide();
21475                     this.un('resize', this.alignErrorIcon, this);
21476                 }
21477                 break;
21478             default:
21479                 var t = Roo.getDom(this.msgTarget);
21480                 t.innerHTML = '';
21481                 t.style.display = 'none';
21482                 break;
21483         }
21484         this.fireEvent('valid', this);
21485     },
21486
21487     /**
21488      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21489      * @return {Mixed} value The field value
21490      */
21491     getRawValue : function(){
21492         var v = this.el.getValue();
21493         if(v === this.emptyText){
21494             v = '';
21495         }
21496         return v;
21497     },
21498
21499     /**
21500      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21501      * @return {Mixed} value The field value
21502      */
21503     getValue : function(){
21504         var v = this.el.getValue();
21505         if(v === this.emptyText || v === undefined){
21506             v = '';
21507         }
21508         return v;
21509     },
21510
21511     /**
21512      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21513      * @param {Mixed} value The value to set
21514      */
21515     setRawValue : function(v){
21516         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21517     },
21518
21519     /**
21520      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21521      * @param {Mixed} value The value to set
21522      */
21523     setValue : function(v){
21524         this.value = v;
21525         if(this.rendered){
21526             this.el.dom.value = (v === null || v === undefined ? '' : v);
21527              this.validate();
21528         }
21529     },
21530
21531     adjustSize : function(w, h){
21532         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21533         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21534         return s;
21535     },
21536
21537     adjustWidth : function(tag, w){
21538         tag = tag.toLowerCase();
21539         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21540             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21541                 if(tag == 'input'){
21542                     return w + 2;
21543                 }
21544                 if(tag = 'textarea'){
21545                     return w-2;
21546                 }
21547             }else if(Roo.isOpera){
21548                 if(tag == 'input'){
21549                     return w + 2;
21550                 }
21551                 if(tag = 'textarea'){
21552                     return w-2;
21553                 }
21554             }
21555         }
21556         return w;
21557     }
21558 });
21559
21560
21561 // anything other than normal should be considered experimental
21562 Roo.form.Field.msgFx = {
21563     normal : {
21564         show: function(msgEl, f){
21565             msgEl.setDisplayed('block');
21566         },
21567
21568         hide : function(msgEl, f){
21569             msgEl.setDisplayed(false).update('');
21570         }
21571     },
21572
21573     slide : {
21574         show: function(msgEl, f){
21575             msgEl.slideIn('t', {stopFx:true});
21576         },
21577
21578         hide : function(msgEl, f){
21579             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21580         }
21581     },
21582
21583     slideRight : {
21584         show: function(msgEl, f){
21585             msgEl.fixDisplay();
21586             msgEl.alignTo(f.el, 'tl-tr');
21587             msgEl.slideIn('l', {stopFx:true});
21588         },
21589
21590         hide : function(msgEl, f){
21591             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21592         }
21593     }
21594 };/*
21595  * Based on:
21596  * Ext JS Library 1.1.1
21597  * Copyright(c) 2006-2007, Ext JS, LLC.
21598  *
21599  * Originally Released Under LGPL - original licence link has changed is not relivant.
21600  *
21601  * Fork - LGPL
21602  * <script type="text/javascript">
21603  */
21604  
21605
21606 /**
21607  * @class Roo.form.TextField
21608  * @extends Roo.form.Field
21609  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21610  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21611  * @constructor
21612  * Creates a new TextField
21613  * @param {Object} config Configuration options
21614  */
21615 Roo.form.TextField = function(config){
21616     Roo.form.TextField.superclass.constructor.call(this, config);
21617     this.addEvents({
21618         /**
21619          * @event autosize
21620          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21621          * according to the default logic, but this event provides a hook for the developer to apply additional
21622          * logic at runtime to resize the field if needed.
21623              * @param {Roo.form.Field} this This text field
21624              * @param {Number} width The new field width
21625              */
21626         autosize : true
21627     });
21628 };
21629
21630 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21631     /**
21632      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21633      */
21634     grow : false,
21635     /**
21636      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21637      */
21638     growMin : 30,
21639     /**
21640      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21641      */
21642     growMax : 800,
21643     /**
21644      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21645      */
21646     vtype : null,
21647     /**
21648      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21649      */
21650     maskRe : null,
21651     /**
21652      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21653      */
21654     disableKeyFilter : false,
21655     /**
21656      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21657      */
21658     allowBlank : true,
21659     /**
21660      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21661      */
21662     minLength : 0,
21663     /**
21664      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21665      */
21666     maxLength : Number.MAX_VALUE,
21667     /**
21668      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21669      */
21670     minLengthText : "The minimum length for this field is {0}",
21671     /**
21672      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21673      */
21674     maxLengthText : "The maximum length for this field is {0}",
21675     /**
21676      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21677      */
21678     selectOnFocus : false,
21679     /**
21680      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21681      */
21682     blankText : "This field is required",
21683     /**
21684      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21685      * If available, this function will be called only after the basic validators all return true, and will be passed the
21686      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21687      */
21688     validator : null,
21689     /**
21690      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21691      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21692      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21693      */
21694     regex : null,
21695     /**
21696      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21697      */
21698     regexText : "",
21699     /**
21700      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21701      */
21702     emptyText : null,
21703     /**
21704      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21705      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21706      */
21707     emptyClass : 'x-form-empty-field',
21708
21709     // private
21710     initEvents : function(){
21711         Roo.form.TextField.superclass.initEvents.call(this);
21712         if(this.validationEvent == 'keyup'){
21713             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21714             this.el.on('keyup', this.filterValidation, this);
21715         }
21716         else if(this.validationEvent !== false){
21717             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21718         }
21719         if(this.selectOnFocus || this.emptyText){
21720             this.on("focus", this.preFocus, this);
21721             if(this.emptyText){
21722                 this.on('blur', this.postBlur, this);
21723                 this.applyEmptyText();
21724             }
21725         }
21726         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21727             this.el.on("keypress", this.filterKeys, this);
21728         }
21729         if(this.grow){
21730             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21731             this.el.on("click", this.autoSize,  this);
21732         }
21733     },
21734
21735     processValue : function(value){
21736         if(this.stripCharsRe){
21737             var newValue = value.replace(this.stripCharsRe, '');
21738             if(newValue !== value){
21739                 this.setRawValue(newValue);
21740                 return newValue;
21741             }
21742         }
21743         return value;
21744     },
21745
21746     filterValidation : function(e){
21747         if(!e.isNavKeyPress()){
21748             this.validationTask.delay(this.validationDelay);
21749         }
21750     },
21751
21752     // private
21753     onKeyUp : function(e){
21754         if(!e.isNavKeyPress()){
21755             this.autoSize();
21756         }
21757     },
21758
21759     /**
21760      * Resets the current field value to the originally-loaded value and clears any validation messages.
21761      * Also adds emptyText and emptyClass if the original value was blank.
21762      */
21763     reset : function(){
21764         Roo.form.TextField.superclass.reset.call(this);
21765         this.applyEmptyText();
21766     },
21767
21768     applyEmptyText : function(){
21769         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21770             this.setRawValue(this.emptyText);
21771             this.el.addClass(this.emptyClass);
21772         }
21773     },
21774
21775     // private
21776     preFocus : function(){
21777         if(this.emptyText){
21778             if(this.el.dom.value == this.emptyText){
21779                 this.setRawValue('');
21780             }
21781             this.el.removeClass(this.emptyClass);
21782         }
21783         if(this.selectOnFocus){
21784             this.el.dom.select();
21785         }
21786     },
21787
21788     // private
21789     postBlur : function(){
21790         this.applyEmptyText();
21791     },
21792
21793     // private
21794     filterKeys : function(e){
21795         var k = e.getKey();
21796         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21797             return;
21798         }
21799         var c = e.getCharCode(), cc = String.fromCharCode(c);
21800         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21801             return;
21802         }
21803         if(!this.maskRe.test(cc)){
21804             e.stopEvent();
21805         }
21806     },
21807
21808     setValue : function(v){
21809         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21810             this.el.removeClass(this.emptyClass);
21811         }
21812         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21813         this.applyEmptyText();
21814         this.autoSize();
21815     },
21816
21817     /**
21818      * Validates a value according to the field's validation rules and marks the field as invalid
21819      * if the validation fails
21820      * @param {Mixed} value The value to validate
21821      * @return {Boolean} True if the value is valid, else false
21822      */
21823     validateValue : function(value){
21824         if(value.length < 1 || value === this.emptyText){ // if it's blank
21825              if(this.allowBlank){
21826                 this.clearInvalid();
21827                 return true;
21828              }else{
21829                 this.markInvalid(this.blankText);
21830                 return false;
21831              }
21832         }
21833         if(value.length < this.minLength){
21834             this.markInvalid(String.format(this.minLengthText, this.minLength));
21835             return false;
21836         }
21837         if(value.length > this.maxLength){
21838             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21839             return false;
21840         }
21841         if(this.vtype){
21842             var vt = Roo.form.VTypes;
21843             if(!vt[this.vtype](value, this)){
21844                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21845                 return false;
21846             }
21847         }
21848         if(typeof this.validator == "function"){
21849             var msg = this.validator(value);
21850             if(msg !== true){
21851                 this.markInvalid(msg);
21852                 return false;
21853             }
21854         }
21855         if(this.regex && !this.regex.test(value)){
21856             this.markInvalid(this.regexText);
21857             return false;
21858         }
21859         return true;
21860     },
21861
21862     /**
21863      * Selects text in this field
21864      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21865      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21866      */
21867     selectText : function(start, end){
21868         var v = this.getRawValue();
21869         if(v.length > 0){
21870             start = start === undefined ? 0 : start;
21871             end = end === undefined ? v.length : end;
21872             var d = this.el.dom;
21873             if(d.setSelectionRange){
21874                 d.setSelectionRange(start, end);
21875             }else if(d.createTextRange){
21876                 var range = d.createTextRange();
21877                 range.moveStart("character", start);
21878                 range.moveEnd("character", v.length-end);
21879                 range.select();
21880             }
21881         }
21882     },
21883
21884     /**
21885      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21886      * This only takes effect if grow = true, and fires the autosize event.
21887      */
21888     autoSize : function(){
21889         if(!this.grow || !this.rendered){
21890             return;
21891         }
21892         if(!this.metrics){
21893             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21894         }
21895         var el = this.el;
21896         var v = el.dom.value;
21897         var d = document.createElement('div');
21898         d.appendChild(document.createTextNode(v));
21899         v = d.innerHTML;
21900         d = null;
21901         v += "&#160;";
21902         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21903         this.el.setWidth(w);
21904         this.fireEvent("autosize", this, w);
21905     }
21906 });/*
21907  * Based on:
21908  * Ext JS Library 1.1.1
21909  * Copyright(c) 2006-2007, Ext JS, LLC.
21910  *
21911  * Originally Released Under LGPL - original licence link has changed is not relivant.
21912  *
21913  * Fork - LGPL
21914  * <script type="text/javascript">
21915  */
21916  
21917 /**
21918  * @class Roo.form.Hidden
21919  * @extends Roo.form.TextField
21920  * Simple Hidden element used on forms 
21921  * 
21922  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21923  * 
21924  * @constructor
21925  * Creates a new Hidden form element.
21926  * @param {Object} config Configuration options
21927  */
21928
21929
21930
21931 // easy hidden field...
21932 Roo.form.Hidden = function(config){
21933     Roo.form.Hidden.superclass.constructor.call(this, config);
21934 };
21935   
21936 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21937     fieldLabel:      '',
21938     inputType:      'hidden',
21939     width:          50,
21940     allowBlank:     true,
21941     labelSeparator: '',
21942     hidden:         true,
21943     itemCls :       'x-form-item-display-none'
21944
21945
21946 });
21947
21948
21949 /*
21950  * Based on:
21951  * Ext JS Library 1.1.1
21952  * Copyright(c) 2006-2007, Ext JS, LLC.
21953  *
21954  * Originally Released Under LGPL - original licence link has changed is not relivant.
21955  *
21956  * Fork - LGPL
21957  * <script type="text/javascript">
21958  */
21959  
21960 /**
21961  * @class Roo.form.TriggerField
21962  * @extends Roo.form.TextField
21963  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21964  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21965  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21966  * for which you can provide a custom implementation.  For example:
21967  * <pre><code>
21968 var trigger = new Roo.form.TriggerField();
21969 trigger.onTriggerClick = myTriggerFn;
21970 trigger.applyTo('my-field');
21971 </code></pre>
21972  *
21973  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21974  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21975  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21976  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21977  * @constructor
21978  * Create a new TriggerField.
21979  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21980  * to the base TextField)
21981  */
21982 Roo.form.TriggerField = function(config){
21983     this.mimicing = false;
21984     Roo.form.TriggerField.superclass.constructor.call(this, config);
21985 };
21986
21987 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21988     /**
21989      * @cfg {String} triggerClass A CSS class to apply to the trigger
21990      */
21991     /**
21992      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21993      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21994      */
21995     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21996     /**
21997      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21998      */
21999     hideTrigger:false,
22000
22001     /** @cfg {Boolean} grow @hide */
22002     /** @cfg {Number} growMin @hide */
22003     /** @cfg {Number} growMax @hide */
22004
22005     /**
22006      * @hide 
22007      * @method
22008      */
22009     autoSize: Roo.emptyFn,
22010     // private
22011     monitorTab : true,
22012     // private
22013     deferHeight : true,
22014
22015     
22016     actionMode : 'wrap',
22017     // private
22018     onResize : function(w, h){
22019         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22020         if(typeof w == 'number'){
22021             var x = w - this.trigger.getWidth();
22022             this.el.setWidth(this.adjustWidth('input', x));
22023             this.trigger.setStyle('left', x+'px');
22024         }
22025     },
22026
22027     // private
22028     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22029
22030     // private
22031     getResizeEl : function(){
22032         return this.wrap;
22033     },
22034
22035     // private
22036     getPositionEl : function(){
22037         return this.wrap;
22038     },
22039
22040     // private
22041     alignErrorIcon : function(){
22042         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22043     },
22044
22045     // private
22046     onRender : function(ct, position){
22047         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22048         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22049         this.trigger = this.wrap.createChild(this.triggerConfig ||
22050                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22051         if(this.hideTrigger){
22052             this.trigger.setDisplayed(false);
22053         }
22054         this.initTrigger();
22055         if(!this.width){
22056             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22057         }
22058     },
22059
22060     // private
22061     initTrigger : function(){
22062         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22063         this.trigger.addClassOnOver('x-form-trigger-over');
22064         this.trigger.addClassOnClick('x-form-trigger-click');
22065     },
22066
22067     // private
22068     onDestroy : function(){
22069         if(this.trigger){
22070             this.trigger.removeAllListeners();
22071             this.trigger.remove();
22072         }
22073         if(this.wrap){
22074             this.wrap.remove();
22075         }
22076         Roo.form.TriggerField.superclass.onDestroy.call(this);
22077     },
22078
22079     // private
22080     onFocus : function(){
22081         Roo.form.TriggerField.superclass.onFocus.call(this);
22082         if(!this.mimicing){
22083             this.wrap.addClass('x-trigger-wrap-focus');
22084             this.mimicing = true;
22085             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22086             if(this.monitorTab){
22087                 this.el.on("keydown", this.checkTab, this);
22088             }
22089         }
22090     },
22091
22092     // private
22093     checkTab : function(e){
22094         if(e.getKey() == e.TAB){
22095             this.triggerBlur();
22096         }
22097     },
22098
22099     // private
22100     onBlur : function(){
22101         // do nothing
22102     },
22103
22104     // private
22105     mimicBlur : function(e, t){
22106         if(!this.wrap.contains(t) && this.validateBlur()){
22107             this.triggerBlur();
22108         }
22109     },
22110
22111     // private
22112     triggerBlur : function(){
22113         this.mimicing = false;
22114         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22115         if(this.monitorTab){
22116             this.el.un("keydown", this.checkTab, this);
22117         }
22118         this.wrap.removeClass('x-trigger-wrap-focus');
22119         Roo.form.TriggerField.superclass.onBlur.call(this);
22120     },
22121
22122     // private
22123     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22124     validateBlur : function(e, t){
22125         return true;
22126     },
22127
22128     // private
22129     onDisable : function(){
22130         Roo.form.TriggerField.superclass.onDisable.call(this);
22131         if(this.wrap){
22132             this.wrap.addClass('x-item-disabled');
22133         }
22134     },
22135
22136     // private
22137     onEnable : function(){
22138         Roo.form.TriggerField.superclass.onEnable.call(this);
22139         if(this.wrap){
22140             this.wrap.removeClass('x-item-disabled');
22141         }
22142     },
22143
22144     // private
22145     onShow : function(){
22146         var ae = this.getActionEl();
22147         
22148         if(ae){
22149             ae.dom.style.display = '';
22150             ae.dom.style.visibility = 'visible';
22151         }
22152     },
22153
22154     // private
22155     
22156     onHide : function(){
22157         var ae = this.getActionEl();
22158         ae.dom.style.display = 'none';
22159     },
22160
22161     /**
22162      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22163      * by an implementing function.
22164      * @method
22165      * @param {EventObject} e
22166      */
22167     onTriggerClick : Roo.emptyFn
22168 });
22169
22170 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22171 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22172 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22173 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22174     initComponent : function(){
22175         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22176
22177         this.triggerConfig = {
22178             tag:'span', cls:'x-form-twin-triggers', cn:[
22179             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22180             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22181         ]};
22182     },
22183
22184     getTrigger : function(index){
22185         return this.triggers[index];
22186     },
22187
22188     initTrigger : function(){
22189         var ts = this.trigger.select('.x-form-trigger', true);
22190         this.wrap.setStyle('overflow', 'hidden');
22191         var triggerField = this;
22192         ts.each(function(t, all, index){
22193             t.hide = function(){
22194                 var w = triggerField.wrap.getWidth();
22195                 this.dom.style.display = 'none';
22196                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22197             };
22198             t.show = function(){
22199                 var w = triggerField.wrap.getWidth();
22200                 this.dom.style.display = '';
22201                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22202             };
22203             var triggerIndex = 'Trigger'+(index+1);
22204
22205             if(this['hide'+triggerIndex]){
22206                 t.dom.style.display = 'none';
22207             }
22208             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22209             t.addClassOnOver('x-form-trigger-over');
22210             t.addClassOnClick('x-form-trigger-click');
22211         }, this);
22212         this.triggers = ts.elements;
22213     },
22214
22215     onTrigger1Click : Roo.emptyFn,
22216     onTrigger2Click : Roo.emptyFn
22217 });/*
22218  * Based on:
22219  * Ext JS Library 1.1.1
22220  * Copyright(c) 2006-2007, Ext JS, LLC.
22221  *
22222  * Originally Released Under LGPL - original licence link has changed is not relivant.
22223  *
22224  * Fork - LGPL
22225  * <script type="text/javascript">
22226  */
22227  
22228 /**
22229  * @class Roo.form.TextArea
22230  * @extends Roo.form.TextField
22231  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22232  * support for auto-sizing.
22233  * @constructor
22234  * Creates a new TextArea
22235  * @param {Object} config Configuration options
22236  */
22237 Roo.form.TextArea = function(config){
22238     Roo.form.TextArea.superclass.constructor.call(this, config);
22239     // these are provided exchanges for backwards compat
22240     // minHeight/maxHeight were replaced by growMin/growMax to be
22241     // compatible with TextField growing config values
22242     if(this.minHeight !== undefined){
22243         this.growMin = this.minHeight;
22244     }
22245     if(this.maxHeight !== undefined){
22246         this.growMax = this.maxHeight;
22247     }
22248 };
22249
22250 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22251     /**
22252      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22253      */
22254     growMin : 60,
22255     /**
22256      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22257      */
22258     growMax: 1000,
22259     /**
22260      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22261      * in the field (equivalent to setting overflow: hidden, defaults to false)
22262      */
22263     preventScrollbars: false,
22264     /**
22265      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22266      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22267      */
22268
22269     // private
22270     onRender : function(ct, position){
22271         if(!this.el){
22272             this.defaultAutoCreate = {
22273                 tag: "textarea",
22274                 style:"width:300px;height:60px;",
22275                 autocomplete: "off"
22276             };
22277         }
22278         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22279         if(this.grow){
22280             this.textSizeEl = Roo.DomHelper.append(document.body, {
22281                 tag: "pre", cls: "x-form-grow-sizer"
22282             });
22283             if(this.preventScrollbars){
22284                 this.el.setStyle("overflow", "hidden");
22285             }
22286             this.el.setHeight(this.growMin);
22287         }
22288     },
22289
22290     onDestroy : function(){
22291         if(this.textSizeEl){
22292             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22293         }
22294         Roo.form.TextArea.superclass.onDestroy.call(this);
22295     },
22296
22297     // private
22298     onKeyUp : function(e){
22299         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22300             this.autoSize();
22301         }
22302     },
22303
22304     /**
22305      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22306      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22307      */
22308     autoSize : function(){
22309         if(!this.grow || !this.textSizeEl){
22310             return;
22311         }
22312         var el = this.el;
22313         var v = el.dom.value;
22314         var ts = this.textSizeEl;
22315
22316         ts.innerHTML = '';
22317         ts.appendChild(document.createTextNode(v));
22318         v = ts.innerHTML;
22319
22320         Roo.fly(ts).setWidth(this.el.getWidth());
22321         if(v.length < 1){
22322             v = "&#160;&#160;";
22323         }else{
22324             if(Roo.isIE){
22325                 v = v.replace(/\n/g, '<p>&#160;</p>');
22326             }
22327             v += "&#160;\n&#160;";
22328         }
22329         ts.innerHTML = v;
22330         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22331         if(h != this.lastHeight){
22332             this.lastHeight = h;
22333             this.el.setHeight(h);
22334             this.fireEvent("autosize", this, h);
22335         }
22336     }
22337 });/*
22338  * Based on:
22339  * Ext JS Library 1.1.1
22340  * Copyright(c) 2006-2007, Ext JS, LLC.
22341  *
22342  * Originally Released Under LGPL - original licence link has changed is not relivant.
22343  *
22344  * Fork - LGPL
22345  * <script type="text/javascript">
22346  */
22347  
22348
22349 /**
22350  * @class Roo.form.NumberField
22351  * @extends Roo.form.TextField
22352  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22353  * @constructor
22354  * Creates a new NumberField
22355  * @param {Object} config Configuration options
22356  */
22357 Roo.form.NumberField = function(config){
22358     Roo.form.NumberField.superclass.constructor.call(this, config);
22359 };
22360
22361 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22362     /**
22363      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22364      */
22365     fieldClass: "x-form-field x-form-num-field",
22366     /**
22367      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22368      */
22369     allowDecimals : true,
22370     /**
22371      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22372      */
22373     decimalSeparator : ".",
22374     /**
22375      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22376      */
22377     decimalPrecision : 2,
22378     /**
22379      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22380      */
22381     allowNegative : true,
22382     /**
22383      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22384      */
22385     minValue : Number.NEGATIVE_INFINITY,
22386     /**
22387      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22388      */
22389     maxValue : Number.MAX_VALUE,
22390     /**
22391      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22392      */
22393     minText : "The minimum value for this field is {0}",
22394     /**
22395      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22396      */
22397     maxText : "The maximum value for this field is {0}",
22398     /**
22399      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22400      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22401      */
22402     nanText : "{0} is not a valid number",
22403
22404     // private
22405     initEvents : function(){
22406         Roo.form.NumberField.superclass.initEvents.call(this);
22407         var allowed = "0123456789";
22408         if(this.allowDecimals){
22409             allowed += this.decimalSeparator;
22410         }
22411         if(this.allowNegative){
22412             allowed += "-";
22413         }
22414         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22415         var keyPress = function(e){
22416             var k = e.getKey();
22417             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22418                 return;
22419             }
22420             var c = e.getCharCode();
22421             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22422                 e.stopEvent();
22423             }
22424         };
22425         this.el.on("keypress", keyPress, this);
22426     },
22427
22428     // private
22429     validateValue : function(value){
22430         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22431             return false;
22432         }
22433         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22434              return true;
22435         }
22436         var num = this.parseValue(value);
22437         if(isNaN(num)){
22438             this.markInvalid(String.format(this.nanText, value));
22439             return false;
22440         }
22441         if(num < this.minValue){
22442             this.markInvalid(String.format(this.minText, this.minValue));
22443             return false;
22444         }
22445         if(num > this.maxValue){
22446             this.markInvalid(String.format(this.maxText, this.maxValue));
22447             return false;
22448         }
22449         return true;
22450     },
22451
22452     getValue : function(){
22453         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22454     },
22455
22456     // private
22457     parseValue : function(value){
22458         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22459         return isNaN(value) ? '' : value;
22460     },
22461
22462     // private
22463     fixPrecision : function(value){
22464         var nan = isNaN(value);
22465         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22466             return nan ? '' : value;
22467         }
22468         return parseFloat(value).toFixed(this.decimalPrecision);
22469     },
22470
22471     setValue : function(v){
22472         v = this.fixPrecision(v);
22473         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22474     },
22475
22476     // private
22477     decimalPrecisionFcn : function(v){
22478         return Math.floor(v);
22479     },
22480
22481     beforeBlur : function(){
22482         var v = this.parseValue(this.getRawValue());
22483         if(v){
22484             this.setValue(v);
22485         }
22486     }
22487 });/*
22488  * Based on:
22489  * Ext JS Library 1.1.1
22490  * Copyright(c) 2006-2007, Ext JS, LLC.
22491  *
22492  * Originally Released Under LGPL - original licence link has changed is not relivant.
22493  *
22494  * Fork - LGPL
22495  * <script type="text/javascript">
22496  */
22497  
22498 /**
22499  * @class Roo.form.DateField
22500  * @extends Roo.form.TriggerField
22501  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22502 * @constructor
22503 * Create a new DateField
22504 * @param {Object} config
22505  */
22506 Roo.form.DateField = function(config){
22507     Roo.form.DateField.superclass.constructor.call(this, config);
22508     
22509       this.addEvents({
22510          
22511         /**
22512          * @event select
22513          * Fires when a date is selected
22514              * @param {Roo.form.DateField} combo This combo box
22515              * @param {Date} date The date selected
22516              */
22517         'select' : true
22518          
22519     });
22520     
22521     
22522     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22523     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22524     this.ddMatch = null;
22525     if(this.disabledDates){
22526         var dd = this.disabledDates;
22527         var re = "(?:";
22528         for(var i = 0; i < dd.length; i++){
22529             re += dd[i];
22530             if(i != dd.length-1) re += "|";
22531         }
22532         this.ddMatch = new RegExp(re + ")");
22533     }
22534 };
22535
22536 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22537     /**
22538      * @cfg {String} format
22539      * The default date format string which can be overriden for localization support.  The format must be
22540      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22541      */
22542     format : "m/d/y",
22543     /**
22544      * @cfg {String} altFormats
22545      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22546      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22547      */
22548     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22549     /**
22550      * @cfg {Array} disabledDays
22551      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22552      */
22553     disabledDays : null,
22554     /**
22555      * @cfg {String} disabledDaysText
22556      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22557      */
22558     disabledDaysText : "Disabled",
22559     /**
22560      * @cfg {Array} disabledDates
22561      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22562      * expression so they are very powerful. Some examples:
22563      * <ul>
22564      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22565      * <li>["03/08", "09/16"] would disable those days for every year</li>
22566      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22567      * <li>["03/../2006"] would disable every day in March 2006</li>
22568      * <li>["^03"] would disable every day in every March</li>
22569      * </ul>
22570      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22571      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22572      */
22573     disabledDates : null,
22574     /**
22575      * @cfg {String} disabledDatesText
22576      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22577      */
22578     disabledDatesText : "Disabled",
22579     /**
22580      * @cfg {Date/String} minValue
22581      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22582      * valid format (defaults to null).
22583      */
22584     minValue : null,
22585     /**
22586      * @cfg {Date/String} maxValue
22587      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22588      * valid format (defaults to null).
22589      */
22590     maxValue : null,
22591     /**
22592      * @cfg {String} minText
22593      * The error text to display when the date in the cell is before minValue (defaults to
22594      * 'The date in this field must be after {minValue}').
22595      */
22596     minText : "The date in this field must be equal to or after {0}",
22597     /**
22598      * @cfg {String} maxText
22599      * The error text to display when the date in the cell is after maxValue (defaults to
22600      * 'The date in this field must be before {maxValue}').
22601      */
22602     maxText : "The date in this field must be equal to or before {0}",
22603     /**
22604      * @cfg {String} invalidText
22605      * The error text to display when the date in the field is invalid (defaults to
22606      * '{value} is not a valid date - it must be in the format {format}').
22607      */
22608     invalidText : "{0} is not a valid date - it must be in the format {1}",
22609     /**
22610      * @cfg {String} triggerClass
22611      * An additional CSS class used to style the trigger button.  The trigger will always get the
22612      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22613      * which displays a calendar icon).
22614      */
22615     triggerClass : 'x-form-date-trigger',
22616     
22617
22618     /**
22619      * @cfg {bool} useIso
22620      * if enabled, then the date field will use a hidden field to store the 
22621      * real value as iso formated date. default (false)
22622      */ 
22623     useIso : false,
22624     /**
22625      * @cfg {String/Object} autoCreate
22626      * A DomHelper element spec, or true for a default element spec (defaults to
22627      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22628      */ 
22629     // private
22630     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22631     
22632     // private
22633     hiddenField: false,
22634     
22635     onRender : function(ct, position)
22636     {
22637         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22638         if (this.useIso) {
22639             this.el.dom.removeAttribute('name'); 
22640             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22641                     'before', true);
22642             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22643             // prevent input submission
22644             this.hiddenName = this.name;
22645         }
22646             
22647             
22648     },
22649     
22650     // private
22651     validateValue : function(value)
22652     {
22653         value = this.formatDate(value);
22654         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22655             return false;
22656         }
22657         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22658              return true;
22659         }
22660         var svalue = value;
22661         value = this.parseDate(value);
22662         if(!value){
22663             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22664             return false;
22665         }
22666         var time = value.getTime();
22667         if(this.minValue && time < this.minValue.getTime()){
22668             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22669             return false;
22670         }
22671         if(this.maxValue && time > this.maxValue.getTime()){
22672             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22673             return false;
22674         }
22675         if(this.disabledDays){
22676             var day = value.getDay();
22677             for(var i = 0; i < this.disabledDays.length; i++) {
22678                 if(day === this.disabledDays[i]){
22679                     this.markInvalid(this.disabledDaysText);
22680                     return false;
22681                 }
22682             }
22683         }
22684         var fvalue = this.formatDate(value);
22685         if(this.ddMatch && this.ddMatch.test(fvalue)){
22686             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22687             return false;
22688         }
22689         return true;
22690     },
22691
22692     // private
22693     // Provides logic to override the default TriggerField.validateBlur which just returns true
22694     validateBlur : function(){
22695         return !this.menu || !this.menu.isVisible();
22696     },
22697
22698     /**
22699      * Returns the current date value of the date field.
22700      * @return {Date} The date value
22701      */
22702     getValue : function(){
22703         
22704         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22705     },
22706
22707     /**
22708      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22709      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22710      * (the default format used is "m/d/y").
22711      * <br />Usage:
22712      * <pre><code>
22713 //All of these calls set the same date value (May 4, 2006)
22714
22715 //Pass a date object:
22716 var dt = new Date('5/4/06');
22717 dateField.setValue(dt);
22718
22719 //Pass a date string (default format):
22720 dateField.setValue('5/4/06');
22721
22722 //Pass a date string (custom format):
22723 dateField.format = 'Y-m-d';
22724 dateField.setValue('2006-5-4');
22725 </code></pre>
22726      * @param {String/Date} date The date or valid date string
22727      */
22728     setValue : function(date){
22729         if (this.hiddenField) {
22730             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22731         }
22732         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22733     },
22734
22735     // private
22736     parseDate : function(value){
22737         if(!value || value instanceof Date){
22738             return value;
22739         }
22740         var v = Date.parseDate(value, this.format);
22741         if(!v && this.altFormats){
22742             if(!this.altFormatsArray){
22743                 this.altFormatsArray = this.altFormats.split("|");
22744             }
22745             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22746                 v = Date.parseDate(value, this.altFormatsArray[i]);
22747             }
22748         }
22749         return v;
22750     },
22751
22752     // private
22753     formatDate : function(date, fmt){
22754         return (!date || !(date instanceof Date)) ?
22755                date : date.dateFormat(fmt || this.format);
22756     },
22757
22758     // private
22759     menuListeners : {
22760         select: function(m, d){
22761             this.setValue(d);
22762             this.fireEvent('select', this, d);
22763         },
22764         show : function(){ // retain focus styling
22765             this.onFocus();
22766         },
22767         hide : function(){
22768             this.focus.defer(10, this);
22769             var ml = this.menuListeners;
22770             this.menu.un("select", ml.select,  this);
22771             this.menu.un("show", ml.show,  this);
22772             this.menu.un("hide", ml.hide,  this);
22773         }
22774     },
22775
22776     // private
22777     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22778     onTriggerClick : function(){
22779         if(this.disabled){
22780             return;
22781         }
22782         if(this.menu == null){
22783             this.menu = new Roo.menu.DateMenu();
22784         }
22785         Roo.apply(this.menu.picker,  {
22786             showClear: this.allowBlank,
22787             minDate : this.minValue,
22788             maxDate : this.maxValue,
22789             disabledDatesRE : this.ddMatch,
22790             disabledDatesText : this.disabledDatesText,
22791             disabledDays : this.disabledDays,
22792             disabledDaysText : this.disabledDaysText,
22793             format : this.format,
22794             minText : String.format(this.minText, this.formatDate(this.minValue)),
22795             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22796         });
22797         this.menu.on(Roo.apply({}, this.menuListeners, {
22798             scope:this
22799         }));
22800         this.menu.picker.setValue(this.getValue() || new Date());
22801         this.menu.show(this.el, "tl-bl?");
22802     },
22803
22804     beforeBlur : function(){
22805         var v = this.parseDate(this.getRawValue());
22806         if(v){
22807             this.setValue(v);
22808         }
22809     }
22810
22811     /** @cfg {Boolean} grow @hide */
22812     /** @cfg {Number} growMin @hide */
22813     /** @cfg {Number} growMax @hide */
22814     /**
22815      * @hide
22816      * @method autoSize
22817      */
22818 });/*
22819  * Based on:
22820  * Ext JS Library 1.1.1
22821  * Copyright(c) 2006-2007, Ext JS, LLC.
22822  *
22823  * Originally Released Under LGPL - original licence link has changed is not relivant.
22824  *
22825  * Fork - LGPL
22826  * <script type="text/javascript">
22827  */
22828  
22829
22830 /**
22831  * @class Roo.form.ComboBox
22832  * @extends Roo.form.TriggerField
22833  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22834  * @constructor
22835  * Create a new ComboBox.
22836  * @param {Object} config Configuration options
22837  */
22838 Roo.form.ComboBox = function(config){
22839     Roo.form.ComboBox.superclass.constructor.call(this, config);
22840     this.addEvents({
22841         /**
22842          * @event expand
22843          * Fires when the dropdown list is expanded
22844              * @param {Roo.form.ComboBox} combo This combo box
22845              */
22846         'expand' : true,
22847         /**
22848          * @event collapse
22849          * Fires when the dropdown list is collapsed
22850              * @param {Roo.form.ComboBox} combo This combo box
22851              */
22852         'collapse' : true,
22853         /**
22854          * @event beforeselect
22855          * Fires before a list item is selected. Return false to cancel the selection.
22856              * @param {Roo.form.ComboBox} combo This combo box
22857              * @param {Roo.data.Record} record The data record returned from the underlying store
22858              * @param {Number} index The index of the selected item in the dropdown list
22859              */
22860         'beforeselect' : true,
22861         /**
22862          * @event select
22863          * Fires when a list item is selected
22864              * @param {Roo.form.ComboBox} combo This combo box
22865              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22866              * @param {Number} index The index of the selected item in the dropdown list
22867              */
22868         'select' : true,
22869         /**
22870          * @event beforequery
22871          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22872          * The event object passed has these properties:
22873              * @param {Roo.form.ComboBox} combo This combo box
22874              * @param {String} query The query
22875              * @param {Boolean} forceAll true to force "all" query
22876              * @param {Boolean} cancel true to cancel the query
22877              * @param {Object} e The query event object
22878              */
22879         'beforequery': true,
22880          /**
22881          * @event add
22882          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22883              * @param {Roo.form.ComboBox} combo This combo box
22884              */
22885         'add' : true,
22886         /**
22887          * @event edit
22888          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22889              * @param {Roo.form.ComboBox} combo This combo box
22890              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22891              */
22892         'edit' : true
22893         
22894         
22895     });
22896     if(this.transform){
22897         this.allowDomMove = false;
22898         var s = Roo.getDom(this.transform);
22899         if(!this.hiddenName){
22900             this.hiddenName = s.name;
22901         }
22902         if(!this.store){
22903             this.mode = 'local';
22904             var d = [], opts = s.options;
22905             for(var i = 0, len = opts.length;i < len; i++){
22906                 var o = opts[i];
22907                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22908                 if(o.selected) {
22909                     this.value = value;
22910                 }
22911                 d.push([value, o.text]);
22912             }
22913             this.store = new Roo.data.SimpleStore({
22914                 'id': 0,
22915                 fields: ['value', 'text'],
22916                 data : d
22917             });
22918             this.valueField = 'value';
22919             this.displayField = 'text';
22920         }
22921         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22922         if(!this.lazyRender){
22923             this.target = true;
22924             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22925             s.parentNode.removeChild(s); // remove it
22926             this.render(this.el.parentNode);
22927         }else{
22928             s.parentNode.removeChild(s); // remove it
22929         }
22930
22931     }
22932     if (this.store) {
22933         this.store = Roo.factory(this.store, Roo.data);
22934     }
22935     
22936     this.selectedIndex = -1;
22937     if(this.mode == 'local'){
22938         if(config.queryDelay === undefined){
22939             this.queryDelay = 10;
22940         }
22941         if(config.minChars === undefined){
22942             this.minChars = 0;
22943         }
22944     }
22945 };
22946
22947 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22948     /**
22949      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22950      */
22951     /**
22952      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22953      * rendering into an Roo.Editor, defaults to false)
22954      */
22955     /**
22956      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22957      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22958      */
22959     /**
22960      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22961      */
22962     /**
22963      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22964      * the dropdown list (defaults to undefined, with no header element)
22965      */
22966
22967      /**
22968      * @cfg {String/Roo.Template} tpl The template to use to render the output
22969      */
22970      
22971     // private
22972     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22973     /**
22974      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22975      */
22976     listWidth: undefined,
22977     /**
22978      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22979      * mode = 'remote' or 'text' if mode = 'local')
22980      */
22981     displayField: undefined,
22982     /**
22983      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22984      * mode = 'remote' or 'value' if mode = 'local'). 
22985      * Note: use of a valueField requires the user make a selection
22986      * in order for a value to be mapped.
22987      */
22988     valueField: undefined,
22989     
22990     
22991     /**
22992      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22993      * field's data value (defaults to the underlying DOM element's name)
22994      */
22995     hiddenName: undefined,
22996     /**
22997      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22998      */
22999     listClass: '',
23000     /**
23001      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23002      */
23003     selectedClass: 'x-combo-selected',
23004     /**
23005      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23006      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23007      * which displays a downward arrow icon).
23008      */
23009     triggerClass : 'x-form-arrow-trigger',
23010     /**
23011      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23012      */
23013     shadow:'sides',
23014     /**
23015      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23016      * anchor positions (defaults to 'tl-bl')
23017      */
23018     listAlign: 'tl-bl?',
23019     /**
23020      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23021      */
23022     maxHeight: 300,
23023     /**
23024      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23025      * query specified by the allQuery config option (defaults to 'query')
23026      */
23027     triggerAction: 'query',
23028     /**
23029      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23030      * (defaults to 4, does not apply if editable = false)
23031      */
23032     minChars : 4,
23033     /**
23034      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23035      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23036      */
23037     typeAhead: false,
23038     /**
23039      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23040      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23041      */
23042     queryDelay: 500,
23043     /**
23044      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23045      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23046      */
23047     pageSize: 0,
23048     /**
23049      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23050      * when editable = true (defaults to false)
23051      */
23052     selectOnFocus:false,
23053     /**
23054      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23055      */
23056     queryParam: 'query',
23057     /**
23058      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23059      * when mode = 'remote' (defaults to 'Loading...')
23060      */
23061     loadingText: 'Loading...',
23062     /**
23063      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23064      */
23065     resizable: false,
23066     /**
23067      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23068      */
23069     handleHeight : 8,
23070     /**
23071      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23072      * traditional select (defaults to true)
23073      */
23074     editable: true,
23075     /**
23076      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23077      */
23078     allQuery: '',
23079     /**
23080      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23081      */
23082     mode: 'remote',
23083     /**
23084      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23085      * listWidth has a higher value)
23086      */
23087     minListWidth : 70,
23088     /**
23089      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23090      * allow the user to set arbitrary text into the field (defaults to false)
23091      */
23092     forceSelection:false,
23093     /**
23094      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23095      * if typeAhead = true (defaults to 250)
23096      */
23097     typeAheadDelay : 250,
23098     /**
23099      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23100      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23101      */
23102     valueNotFoundText : undefined,
23103     /**
23104      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23105      */
23106     blockFocus : false,
23107     
23108     /**
23109      * @cfg {Boolean} disableClear Disable showing of clear button.
23110      */
23111     disableClear : false,
23112     /**
23113      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23114      */
23115     alwaysQuery : false,
23116     
23117     //private
23118     addicon : false,
23119     editicon: false,
23120     
23121     // element that contains real text value.. (when hidden is used..)
23122      
23123     // private
23124     onRender : function(ct, position){
23125         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23126         if(this.hiddenName){
23127             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23128                     'before', true);
23129             this.hiddenField.value =
23130                 this.hiddenValue !== undefined ? this.hiddenValue :
23131                 this.value !== undefined ? this.value : '';
23132
23133             // prevent input submission
23134             this.el.dom.removeAttribute('name');
23135              
23136              
23137         }
23138         if(Roo.isGecko){
23139             this.el.dom.setAttribute('autocomplete', 'off');
23140         }
23141
23142         var cls = 'x-combo-list';
23143
23144         this.list = new Roo.Layer({
23145             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23146         });
23147
23148         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23149         this.list.setWidth(lw);
23150         this.list.swallowEvent('mousewheel');
23151         this.assetHeight = 0;
23152
23153         if(this.title){
23154             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23155             this.assetHeight += this.header.getHeight();
23156         }
23157
23158         this.innerList = this.list.createChild({cls:cls+'-inner'});
23159         this.innerList.on('mouseover', this.onViewOver, this);
23160         this.innerList.on('mousemove', this.onViewMove, this);
23161         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23162         
23163         if(this.allowBlank && !this.pageSize && !this.disableClear){
23164             this.footer = this.list.createChild({cls:cls+'-ft'});
23165             this.pageTb = new Roo.Toolbar(this.footer);
23166            
23167         }
23168         if(this.pageSize){
23169             this.footer = this.list.createChild({cls:cls+'-ft'});
23170             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23171                     {pageSize: this.pageSize});
23172             
23173         }
23174         
23175         if (this.pageTb && this.allowBlank && !this.disableClear) {
23176             var _this = this;
23177             this.pageTb.add(new Roo.Toolbar.Fill(), {
23178                 cls: 'x-btn-icon x-btn-clear',
23179                 text: '&#160;',
23180                 handler: function()
23181                 {
23182                     _this.collapse();
23183                     _this.clearValue();
23184                     _this.onSelect(false, -1);
23185                 }
23186             });
23187         }
23188         if (this.footer) {
23189             this.assetHeight += this.footer.getHeight();
23190         }
23191         
23192
23193         if(!this.tpl){
23194             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23195         }
23196
23197         this.view = new Roo.View(this.innerList, this.tpl, {
23198             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23199         });
23200
23201         this.view.on('click', this.onViewClick, this);
23202
23203         this.store.on('beforeload', this.onBeforeLoad, this);
23204         this.store.on('load', this.onLoad, this);
23205         this.store.on('loadexception', this.onLoadException, this);
23206
23207         if(this.resizable){
23208             this.resizer = new Roo.Resizable(this.list,  {
23209                pinned:true, handles:'se'
23210             });
23211             this.resizer.on('resize', function(r, w, h){
23212                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23213                 this.listWidth = w;
23214                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23215                 this.restrictHeight();
23216             }, this);
23217             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23218         }
23219         if(!this.editable){
23220             this.editable = true;
23221             this.setEditable(false);
23222         }  
23223         
23224         
23225         if (typeof(this.events.add.listeners) != 'undefined') {
23226             
23227             this.addicon = this.wrap.createChild(
23228                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23229        
23230             this.addicon.on('click', function(e) {
23231                 this.fireEvent('add', this);
23232             }, this);
23233         }
23234         if (typeof(this.events.edit.listeners) != 'undefined') {
23235             
23236             this.editicon = this.wrap.createChild(
23237                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23238             if (this.addicon) {
23239                 this.editicon.setStyle('margin-left', '40px');
23240             }
23241             this.editicon.on('click', function(e) {
23242                 
23243                 // we fire even  if inothing is selected..
23244                 this.fireEvent('edit', this, this.lastData );
23245                 
23246             }, this);
23247         }
23248         
23249         
23250         
23251     },
23252
23253     // private
23254     initEvents : function(){
23255         Roo.form.ComboBox.superclass.initEvents.call(this);
23256
23257         this.keyNav = new Roo.KeyNav(this.el, {
23258             "up" : function(e){
23259                 this.inKeyMode = true;
23260                 this.selectPrev();
23261             },
23262
23263             "down" : function(e){
23264                 if(!this.isExpanded()){
23265                     this.onTriggerClick();
23266                 }else{
23267                     this.inKeyMode = true;
23268                     this.selectNext();
23269                 }
23270             },
23271
23272             "enter" : function(e){
23273                 this.onViewClick();
23274                 //return true;
23275             },
23276
23277             "esc" : function(e){
23278                 this.collapse();
23279             },
23280
23281             "tab" : function(e){
23282                 this.onViewClick(false);
23283                 this.fireEvent("specialkey", this, e);
23284                 return true;
23285             },
23286
23287             scope : this,
23288
23289             doRelay : function(foo, bar, hname){
23290                 if(hname == 'down' || this.scope.isExpanded()){
23291                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23292                 }
23293                 return true;
23294             },
23295
23296             forceKeyDown: true
23297         });
23298         this.queryDelay = Math.max(this.queryDelay || 10,
23299                 this.mode == 'local' ? 10 : 250);
23300         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23301         if(this.typeAhead){
23302             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23303         }
23304         if(this.editable !== false){
23305             this.el.on("keyup", this.onKeyUp, this);
23306         }
23307         if(this.forceSelection){
23308             this.on('blur', this.doForce, this);
23309         }
23310     },
23311
23312     onDestroy : function(){
23313         if(this.view){
23314             this.view.setStore(null);
23315             this.view.el.removeAllListeners();
23316             this.view.el.remove();
23317             this.view.purgeListeners();
23318         }
23319         if(this.list){
23320             this.list.destroy();
23321         }
23322         if(this.store){
23323             this.store.un('beforeload', this.onBeforeLoad, this);
23324             this.store.un('load', this.onLoad, this);
23325             this.store.un('loadexception', this.onLoadException, this);
23326         }
23327         Roo.form.ComboBox.superclass.onDestroy.call(this);
23328     },
23329
23330     // private
23331     fireKey : function(e){
23332         if(e.isNavKeyPress() && !this.list.isVisible()){
23333             this.fireEvent("specialkey", this, e);
23334         }
23335     },
23336
23337     // private
23338     onResize: function(w, h){
23339         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23340         
23341         if(typeof w != 'number'){
23342             // we do not handle it!?!?
23343             return;
23344         }
23345         var tw = this.trigger.getWidth();
23346         tw += this.addicon ? this.addicon.getWidth() : 0;
23347         tw += this.editicon ? this.editicon.getWidth() : 0;
23348         var x = w - tw;
23349         this.el.setWidth( this.adjustWidth('input', x));
23350             
23351         this.trigger.setStyle('left', x+'px');
23352         
23353         if(this.list && this.listWidth === undefined){
23354             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23355             this.list.setWidth(lw);
23356             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23357         }
23358         
23359     
23360         
23361     },
23362
23363     /**
23364      * Allow or prevent the user from directly editing the field text.  If false is passed,
23365      * the user will only be able to select from the items defined in the dropdown list.  This method
23366      * is the runtime equivalent of setting the 'editable' config option at config time.
23367      * @param {Boolean} value True to allow the user to directly edit the field text
23368      */
23369     setEditable : function(value){
23370         if(value == this.editable){
23371             return;
23372         }
23373         this.editable = value;
23374         if(!value){
23375             this.el.dom.setAttribute('readOnly', true);
23376             this.el.on('mousedown', this.onTriggerClick,  this);
23377             this.el.addClass('x-combo-noedit');
23378         }else{
23379             this.el.dom.setAttribute('readOnly', false);
23380             this.el.un('mousedown', this.onTriggerClick,  this);
23381             this.el.removeClass('x-combo-noedit');
23382         }
23383     },
23384
23385     // private
23386     onBeforeLoad : function(){
23387         if(!this.hasFocus){
23388             return;
23389         }
23390         this.innerList.update(this.loadingText ?
23391                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23392         this.restrictHeight();
23393         this.selectedIndex = -1;
23394     },
23395
23396     // private
23397     onLoad : function(){
23398         if(!this.hasFocus){
23399             return;
23400         }
23401         if(this.store.getCount() > 0){
23402             this.expand();
23403             this.restrictHeight();
23404             if(this.lastQuery == this.allQuery){
23405                 if(this.editable){
23406                     this.el.dom.select();
23407                 }
23408                 if(!this.selectByValue(this.value, true)){
23409                     this.select(0, true);
23410                 }
23411             }else{
23412                 this.selectNext();
23413                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23414                     this.taTask.delay(this.typeAheadDelay);
23415                 }
23416             }
23417         }else{
23418             this.onEmptyResults();
23419         }
23420         //this.el.focus();
23421     },
23422     // private
23423     onLoadException : function()
23424     {
23425         this.collapse();
23426         Roo.log(this.store.reader.jsonData);
23427         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23428             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23429         }
23430         
23431         
23432     },
23433     // private
23434     onTypeAhead : function(){
23435         if(this.store.getCount() > 0){
23436             var r = this.store.getAt(0);
23437             var newValue = r.data[this.displayField];
23438             var len = newValue.length;
23439             var selStart = this.getRawValue().length;
23440             if(selStart != len){
23441                 this.setRawValue(newValue);
23442                 this.selectText(selStart, newValue.length);
23443             }
23444         }
23445     },
23446
23447     // private
23448     onSelect : function(record, index){
23449         if(this.fireEvent('beforeselect', this, record, index) !== false){
23450             this.setFromData(index > -1 ? record.data : false);
23451             this.collapse();
23452             this.fireEvent('select', this, record, index);
23453         }
23454     },
23455
23456     /**
23457      * Returns the currently selected field value or empty string if no value is set.
23458      * @return {String} value The selected value
23459      */
23460     getValue : function(){
23461         if(this.valueField){
23462             return typeof this.value != 'undefined' ? this.value : '';
23463         }else{
23464             return Roo.form.ComboBox.superclass.getValue.call(this);
23465         }
23466     },
23467
23468     /**
23469      * Clears any text/value currently set in the field
23470      */
23471     clearValue : function(){
23472         if(this.hiddenField){
23473             this.hiddenField.value = '';
23474         }
23475         this.value = '';
23476         this.setRawValue('');
23477         this.lastSelectionText = '';
23478         this.applyEmptyText();
23479     },
23480
23481     /**
23482      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23483      * will be displayed in the field.  If the value does not match the data value of an existing item,
23484      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23485      * Otherwise the field will be blank (although the value will still be set).
23486      * @param {String} value The value to match
23487      */
23488     setValue : function(v){
23489         var text = v;
23490         if(this.valueField){
23491             var r = this.findRecord(this.valueField, v);
23492             if(r){
23493                 text = r.data[this.displayField];
23494             }else if(this.valueNotFoundText !== undefined){
23495                 text = this.valueNotFoundText;
23496             }
23497         }
23498         this.lastSelectionText = text;
23499         if(this.hiddenField){
23500             this.hiddenField.value = v;
23501         }
23502         Roo.form.ComboBox.superclass.setValue.call(this, text);
23503         this.value = v;
23504     },
23505     /**
23506      * @property {Object} the last set data for the element
23507      */
23508     
23509     lastData : false,
23510     /**
23511      * Sets the value of the field based on a object which is related to the record format for the store.
23512      * @param {Object} value the value to set as. or false on reset?
23513      */
23514     setFromData : function(o){
23515         var dv = ''; // display value
23516         var vv = ''; // value value..
23517         this.lastData = o;
23518         if (this.displayField) {
23519             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23520         } else {
23521             // this is an error condition!!!
23522             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23523         }
23524         
23525         if(this.valueField){
23526             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23527         }
23528         if(this.hiddenField){
23529             this.hiddenField.value = vv;
23530             
23531             this.lastSelectionText = dv;
23532             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23533             this.value = vv;
23534             return;
23535         }
23536         // no hidden field.. - we store the value in 'value', but still display
23537         // display field!!!!
23538         this.lastSelectionText = dv;
23539         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23540         this.value = vv;
23541         
23542         
23543     },
23544     // private
23545     reset : function(){
23546         // overridden so that last data is reset..
23547         this.setValue(this.originalValue);
23548         this.clearInvalid();
23549         this.lastData = false;
23550     },
23551     // private
23552     findRecord : function(prop, value){
23553         var record;
23554         if(this.store.getCount() > 0){
23555             this.store.each(function(r){
23556                 if(r.data[prop] == value){
23557                     record = r;
23558                     return false;
23559                 }
23560                 return true;
23561             });
23562         }
23563         return record;
23564     },
23565     
23566     getName: function()
23567     {
23568         // returns hidden if it's set..
23569         if (!this.rendered) {return ''};
23570         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23571         
23572     },
23573     // private
23574     onViewMove : function(e, t){
23575         this.inKeyMode = false;
23576     },
23577
23578     // private
23579     onViewOver : function(e, t){
23580         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23581             return;
23582         }
23583         var item = this.view.findItemFromChild(t);
23584         if(item){
23585             var index = this.view.indexOf(item);
23586             this.select(index, false);
23587         }
23588     },
23589
23590     // private
23591     onViewClick : function(doFocus)
23592     {
23593         var index = this.view.getSelectedIndexes()[0];
23594         var r = this.store.getAt(index);
23595         if(r){
23596             this.onSelect(r, index);
23597         }
23598         if(doFocus !== false && !this.blockFocus){
23599             this.el.focus();
23600         }
23601     },
23602
23603     // private
23604     restrictHeight : function(){
23605         this.innerList.dom.style.height = '';
23606         var inner = this.innerList.dom;
23607         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23608         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23609         this.list.beginUpdate();
23610         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23611         this.list.alignTo(this.el, this.listAlign);
23612         this.list.endUpdate();
23613     },
23614
23615     // private
23616     onEmptyResults : function(){
23617         this.collapse();
23618     },
23619
23620     /**
23621      * Returns true if the dropdown list is expanded, else false.
23622      */
23623     isExpanded : function(){
23624         return this.list.isVisible();
23625     },
23626
23627     /**
23628      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23629      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23630      * @param {String} value The data value of the item to select
23631      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23632      * selected item if it is not currently in view (defaults to true)
23633      * @return {Boolean} True if the value matched an item in the list, else false
23634      */
23635     selectByValue : function(v, scrollIntoView){
23636         if(v !== undefined && v !== null){
23637             var r = this.findRecord(this.valueField || this.displayField, v);
23638             if(r){
23639                 this.select(this.store.indexOf(r), scrollIntoView);
23640                 return true;
23641             }
23642         }
23643         return false;
23644     },
23645
23646     /**
23647      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23648      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23649      * @param {Number} index The zero-based index of the list item to select
23650      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23651      * selected item if it is not currently in view (defaults to true)
23652      */
23653     select : function(index, scrollIntoView){
23654         this.selectedIndex = index;
23655         this.view.select(index);
23656         if(scrollIntoView !== false){
23657             var el = this.view.getNode(index);
23658             if(el){
23659                 this.innerList.scrollChildIntoView(el, false);
23660             }
23661         }
23662     },
23663
23664     // private
23665     selectNext : function(){
23666         var ct = this.store.getCount();
23667         if(ct > 0){
23668             if(this.selectedIndex == -1){
23669                 this.select(0);
23670             }else if(this.selectedIndex < ct-1){
23671                 this.select(this.selectedIndex+1);
23672             }
23673         }
23674     },
23675
23676     // private
23677     selectPrev : function(){
23678         var ct = this.store.getCount();
23679         if(ct > 0){
23680             if(this.selectedIndex == -1){
23681                 this.select(0);
23682             }else if(this.selectedIndex != 0){
23683                 this.select(this.selectedIndex-1);
23684             }
23685         }
23686     },
23687
23688     // private
23689     onKeyUp : function(e){
23690         if(this.editable !== false && !e.isSpecialKey()){
23691             this.lastKey = e.getKey();
23692             this.dqTask.delay(this.queryDelay);
23693         }
23694     },
23695
23696     // private
23697     validateBlur : function(){
23698         return !this.list || !this.list.isVisible();   
23699     },
23700
23701     // private
23702     initQuery : function(){
23703         this.doQuery(this.getRawValue());
23704     },
23705
23706     // private
23707     doForce : function(){
23708         if(this.el.dom.value.length > 0){
23709             this.el.dom.value =
23710                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23711             this.applyEmptyText();
23712         }
23713     },
23714
23715     /**
23716      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23717      * query allowing the query action to be canceled if needed.
23718      * @param {String} query The SQL query to execute
23719      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23720      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23721      * saved in the current store (defaults to false)
23722      */
23723     doQuery : function(q, forceAll){
23724         if(q === undefined || q === null){
23725             q = '';
23726         }
23727         var qe = {
23728             query: q,
23729             forceAll: forceAll,
23730             combo: this,
23731             cancel:false
23732         };
23733         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23734             return false;
23735         }
23736         q = qe.query;
23737         forceAll = qe.forceAll;
23738         if(forceAll === true || (q.length >= this.minChars)){
23739             if(this.lastQuery != q || this.alwaysQuery){
23740                 this.lastQuery = q;
23741                 if(this.mode == 'local'){
23742                     this.selectedIndex = -1;
23743                     if(forceAll){
23744                         this.store.clearFilter();
23745                     }else{
23746                         this.store.filter(this.displayField, q);
23747                     }
23748                     this.onLoad();
23749                 }else{
23750                     this.store.baseParams[this.queryParam] = q;
23751                     this.store.load({
23752                         params: this.getParams(q)
23753                     });
23754                     this.expand();
23755                 }
23756             }else{
23757                 this.selectedIndex = -1;
23758                 this.onLoad();   
23759             }
23760         }
23761     },
23762
23763     // private
23764     getParams : function(q){
23765         var p = {};
23766         //p[this.queryParam] = q;
23767         if(this.pageSize){
23768             p.start = 0;
23769             p.limit = this.pageSize;
23770         }
23771         return p;
23772     },
23773
23774     /**
23775      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23776      */
23777     collapse : function(){
23778         if(!this.isExpanded()){
23779             return;
23780         }
23781         this.list.hide();
23782         Roo.get(document).un('mousedown', this.collapseIf, this);
23783         Roo.get(document).un('mousewheel', this.collapseIf, this);
23784         if (!this.editable) {
23785             Roo.get(document).un('keydown', this.listKeyPress, this);
23786         }
23787         this.fireEvent('collapse', this);
23788     },
23789
23790     // private
23791     collapseIf : function(e){
23792         if(!e.within(this.wrap) && !e.within(this.list)){
23793             this.collapse();
23794         }
23795     },
23796
23797     /**
23798      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23799      */
23800     expand : function(){
23801         if(this.isExpanded() || !this.hasFocus){
23802             return;
23803         }
23804         this.list.alignTo(this.el, this.listAlign);
23805         this.list.show();
23806         Roo.get(document).on('mousedown', this.collapseIf, this);
23807         Roo.get(document).on('mousewheel', this.collapseIf, this);
23808         if (!this.editable) {
23809             Roo.get(document).on('keydown', this.listKeyPress, this);
23810         }
23811         
23812         this.fireEvent('expand', this);
23813     },
23814
23815     // private
23816     // Implements the default empty TriggerField.onTriggerClick function
23817     onTriggerClick : function(){
23818         if(this.disabled){
23819             return;
23820         }
23821         if(this.isExpanded()){
23822             this.collapse();
23823             if (!this.blockFocus) {
23824                 this.el.focus();
23825             }
23826             
23827         }else {
23828             this.hasFocus = true;
23829             if(this.triggerAction == 'all') {
23830                 this.doQuery(this.allQuery, true);
23831             } else {
23832                 this.doQuery(this.getRawValue());
23833             }
23834             if (!this.blockFocus) {
23835                 this.el.focus();
23836             }
23837         }
23838     },
23839     listKeyPress : function(e)
23840     {
23841         //Roo.log('listkeypress');
23842         // scroll to first matching element based on key pres..
23843         if (e.isSpecialKey()) {
23844             return false;
23845         }
23846         var k = String.fromCharCode(e.getKey()).toUpperCase();
23847         //Roo.log(k);
23848         var match  = false;
23849         var csel = this.view.getSelectedNodes();
23850         var cselitem = false;
23851         if (csel.length) {
23852             var ix = this.view.indexOf(csel[0]);
23853             cselitem  = this.store.getAt(ix);
23854             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23855                 cselitem = false;
23856             }
23857             
23858         }
23859         
23860         this.store.each(function(v) { 
23861             if (cselitem) {
23862                 // start at existing selection.
23863                 if (cselitem.id == v.id) {
23864                     cselitem = false;
23865                 }
23866                 return;
23867             }
23868                 
23869             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23870                 match = this.store.indexOf(v);
23871                 return false;
23872             }
23873         }, this);
23874         
23875         if (match === false) {
23876             return true; // no more action?
23877         }
23878         // scroll to?
23879         this.view.select(match);
23880         var sn = Roo.get(this.view.getSelectedNodes()[0])
23881         sn.scrollIntoView(sn.dom.parentNode, false);
23882     }
23883
23884     /** 
23885     * @cfg {Boolean} grow 
23886     * @hide 
23887     */
23888     /** 
23889     * @cfg {Number} growMin 
23890     * @hide 
23891     */
23892     /** 
23893     * @cfg {Number} growMax 
23894     * @hide 
23895     */
23896     /**
23897      * @hide
23898      * @method autoSize
23899      */
23900 });/*
23901  * Based on:
23902  * Ext JS Library 1.1.1
23903  * Copyright(c) 2006-2007, Ext JS, LLC.
23904  *
23905  * Originally Released Under LGPL - original licence link has changed is not relivant.
23906  *
23907  * Fork - LGPL
23908  * <script type="text/javascript">
23909  */
23910 /**
23911  * @class Roo.form.Checkbox
23912  * @extends Roo.form.Field
23913  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23914  * @constructor
23915  * Creates a new Checkbox
23916  * @param {Object} config Configuration options
23917  */
23918 Roo.form.Checkbox = function(config){
23919     Roo.form.Checkbox.superclass.constructor.call(this, config);
23920     this.addEvents({
23921         /**
23922          * @event check
23923          * Fires when the checkbox is checked or unchecked.
23924              * @param {Roo.form.Checkbox} this This checkbox
23925              * @param {Boolean} checked The new checked value
23926              */
23927         check : true
23928     });
23929 };
23930
23931 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23932     /**
23933      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23934      */
23935     focusClass : undefined,
23936     /**
23937      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23938      */
23939     fieldClass: "x-form-field",
23940     /**
23941      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23942      */
23943     checked: false,
23944     /**
23945      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23946      * {tag: "input", type: "checkbox", autocomplete: "off"})
23947      */
23948     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23949     /**
23950      * @cfg {String} boxLabel The text that appears beside the checkbox
23951      */
23952     boxLabel : "",
23953     /**
23954      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23955      */  
23956     inputValue : '1',
23957     /**
23958      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23959      */
23960      valueOff: '0', // value when not checked..
23961
23962     actionMode : 'viewEl', 
23963     //
23964     // private
23965     itemCls : 'x-menu-check-item x-form-item',
23966     groupClass : 'x-menu-group-item',
23967     inputType : 'hidden',
23968     
23969     
23970     inSetChecked: false, // check that we are not calling self...
23971     
23972     inputElement: false, // real input element?
23973     basedOn: false, // ????
23974     
23975     isFormField: true, // not sure where this is needed!!!!
23976
23977     onResize : function(){
23978         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23979         if(!this.boxLabel){
23980             this.el.alignTo(this.wrap, 'c-c');
23981         }
23982     },
23983
23984     initEvents : function(){
23985         Roo.form.Checkbox.superclass.initEvents.call(this);
23986         this.el.on("click", this.onClick,  this);
23987         this.el.on("change", this.onClick,  this);
23988     },
23989
23990
23991     getResizeEl : function(){
23992         return this.wrap;
23993     },
23994
23995     getPositionEl : function(){
23996         return this.wrap;
23997     },
23998
23999     // private
24000     onRender : function(ct, position){
24001         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24002         /*
24003         if(this.inputValue !== undefined){
24004             this.el.dom.value = this.inputValue;
24005         }
24006         */
24007         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24008         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24009         var viewEl = this.wrap.createChild({ 
24010             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24011         this.viewEl = viewEl;   
24012         this.wrap.on('click', this.onClick,  this); 
24013         
24014         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24015         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24016         
24017         
24018         
24019         if(this.boxLabel){
24020             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24021         //    viewEl.on('click', this.onClick,  this); 
24022         }
24023         //if(this.checked){
24024             this.setChecked(this.checked);
24025         //}else{
24026             //this.checked = this.el.dom;
24027         //}
24028
24029     },
24030
24031     // private
24032     initValue : Roo.emptyFn,
24033
24034     /**
24035      * Returns the checked state of the checkbox.
24036      * @return {Boolean} True if checked, else false
24037      */
24038     getValue : function(){
24039         if(this.el){
24040             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24041         }
24042         return this.valueOff;
24043         
24044     },
24045
24046         // private
24047     onClick : function(){ 
24048         this.setChecked(!this.checked);
24049
24050         //if(this.el.dom.checked != this.checked){
24051         //    this.setValue(this.el.dom.checked);
24052        // }
24053     },
24054
24055     /**
24056      * Sets the checked state of the checkbox.
24057      * On is always based on a string comparison between inputValue and the param.
24058      * @param {Boolean/String} value - the value to set 
24059      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24060      */
24061     setValue : function(v,suppressEvent){
24062         
24063         
24064         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24065         //if(this.el && this.el.dom){
24066         //    this.el.dom.checked = this.checked;
24067         //    this.el.dom.defaultChecked = this.checked;
24068         //}
24069         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24070         //this.fireEvent("check", this, this.checked);
24071     },
24072     // private..
24073     setChecked : function(state,suppressEvent)
24074     {
24075         if (this.inSetChecked) {
24076             this.checked = state;
24077             return;
24078         }
24079         
24080     
24081         if(this.wrap){
24082             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24083         }
24084         this.checked = state;
24085         if(suppressEvent !== true){
24086             this.fireEvent('check', this, state);
24087         }
24088         this.inSetChecked = true;
24089         this.el.dom.value = state ? this.inputValue : this.valueOff;
24090         this.inSetChecked = false;
24091         
24092     },
24093     // handle setting of hidden value by some other method!!?!?
24094     setFromHidden: function()
24095     {
24096         if(!this.el){
24097             return;
24098         }
24099         //console.log("SET FROM HIDDEN");
24100         //alert('setFrom hidden');
24101         this.setValue(this.el.dom.value);
24102     },
24103     
24104     onDestroy : function()
24105     {
24106         if(this.viewEl){
24107             Roo.get(this.viewEl).remove();
24108         }
24109          
24110         Roo.form.Checkbox.superclass.onDestroy.call(this);
24111     }
24112
24113 });/*
24114  * Based on:
24115  * Ext JS Library 1.1.1
24116  * Copyright(c) 2006-2007, Ext JS, LLC.
24117  *
24118  * Originally Released Under LGPL - original licence link has changed is not relivant.
24119  *
24120  * Fork - LGPL
24121  * <script type="text/javascript">
24122  */
24123  
24124 /**
24125  * @class Roo.form.Radio
24126  * @extends Roo.form.Checkbox
24127  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24128  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24129  * @constructor
24130  * Creates a new Radio
24131  * @param {Object} config Configuration options
24132  */
24133 Roo.form.Radio = function(){
24134     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24135 };
24136 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24137     inputType: 'radio',
24138
24139     /**
24140      * If this radio is part of a group, it will return the selected value
24141      * @return {String}
24142      */
24143     getGroupValue : function(){
24144         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24145     }
24146 });//<script type="text/javascript">
24147
24148 /*
24149  * Ext JS Library 1.1.1
24150  * Copyright(c) 2006-2007, Ext JS, LLC.
24151  * licensing@extjs.com
24152  * 
24153  * http://www.extjs.com/license
24154  */
24155  
24156  /*
24157   * 
24158   * Known bugs:
24159   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24160   * - IE ? - no idea how much works there.
24161   * 
24162   * 
24163   * 
24164   */
24165  
24166
24167 /**
24168  * @class Ext.form.HtmlEditor
24169  * @extends Ext.form.Field
24170  * Provides a lightweight HTML Editor component.
24171  *
24172  * This has been tested on Fireforx / Chrome.. IE may not be so great..
24173  * 
24174  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24175  * supported by this editor.</b><br/><br/>
24176  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24177  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24178  */
24179 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24180       /**
24181      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24182      */
24183     toolbars : false,
24184     /**
24185      * @cfg {String} createLinkText The default text for the create link prompt
24186      */
24187     createLinkText : 'Please enter the URL for the link:',
24188     /**
24189      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24190      */
24191     defaultLinkValue : 'http:/'+'/',
24192    
24193      /**
24194      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24195      *                        Roo.resizable.
24196      */
24197     resizable : false,
24198      /**
24199      * @cfg {Number} height (in pixels)
24200      */   
24201     height: 300,
24202    /**
24203      * @cfg {Number} width (in pixels)
24204      */   
24205     width: 500,
24206     
24207     /**
24208      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24209      * 
24210      */
24211     stylesheets: false,
24212     
24213     // id of frame..
24214     frameId: false,
24215     
24216     // private properties
24217     validationEvent : false,
24218     deferHeight: true,
24219     initialized : false,
24220     activated : false,
24221     sourceEditMode : false,
24222     onFocus : Roo.emptyFn,
24223     iframePad:3,
24224     hideMode:'offsets',
24225     
24226     defaultAutoCreate : { // modified by initCompnoent..
24227         tag: "textarea",
24228         style:"width:500px;height:300px;",
24229         autocomplete: "off"
24230     },
24231
24232     // private
24233     initComponent : function(){
24234         this.addEvents({
24235             /**
24236              * @event initialize
24237              * Fires when the editor is fully initialized (including the iframe)
24238              * @param {HtmlEditor} this
24239              */
24240             initialize: true,
24241             /**
24242              * @event activate
24243              * Fires when the editor is first receives the focus. Any insertion must wait
24244              * until after this event.
24245              * @param {HtmlEditor} this
24246              */
24247             activate: true,
24248              /**
24249              * @event beforesync
24250              * Fires before the textarea is updated with content from the editor iframe. Return false
24251              * to cancel the sync.
24252              * @param {HtmlEditor} this
24253              * @param {String} html
24254              */
24255             beforesync: true,
24256              /**
24257              * @event beforepush
24258              * Fires before the iframe editor is updated with content from the textarea. Return false
24259              * to cancel the push.
24260              * @param {HtmlEditor} this
24261              * @param {String} html
24262              */
24263             beforepush: true,
24264              /**
24265              * @event sync
24266              * Fires when the textarea is updated with content from the editor iframe.
24267              * @param {HtmlEditor} this
24268              * @param {String} html
24269              */
24270             sync: true,
24271              /**
24272              * @event push
24273              * Fires when the iframe editor is updated with content from the textarea.
24274              * @param {HtmlEditor} this
24275              * @param {String} html
24276              */
24277             push: true,
24278              /**
24279              * @event editmodechange
24280              * Fires when the editor switches edit modes
24281              * @param {HtmlEditor} this
24282              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24283              */
24284             editmodechange: true,
24285             /**
24286              * @event editorevent
24287              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24288              * @param {HtmlEditor} this
24289              */
24290             editorevent: true
24291         });
24292         this.defaultAutoCreate =  {
24293             tag: "textarea",
24294             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24295             autocomplete: "off"
24296         };
24297     },
24298
24299     /**
24300      * Protected method that will not generally be called directly. It
24301      * is called when the editor creates its toolbar. Override this method if you need to
24302      * add custom toolbar buttons.
24303      * @param {HtmlEditor} editor
24304      */
24305     createToolbar : function(editor){
24306         if (!editor.toolbars || !editor.toolbars.length) {
24307             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24308         }
24309         
24310         for (var i =0 ; i < editor.toolbars.length;i++) {
24311             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24312             editor.toolbars[i].init(editor);
24313         }
24314          
24315         
24316     },
24317
24318     /**
24319      * Protected method that will not generally be called directly. It
24320      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24321      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24322      */
24323     getDocMarkup : function(){
24324         // body styles..
24325         var st = '';
24326         if (this.stylesheets === false) {
24327             
24328             Roo.get(document.head).select('style').each(function(node) {
24329                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24330             });
24331             
24332             Roo.get(document.head).select('link').each(function(node) { 
24333                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24334             });
24335             
24336         } else if (!this.stylesheets.length) {
24337                 // simple..
24338                 st = '<style type="text/css">' +
24339                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24340                    '</style>';
24341         } else {
24342             Roo.each(this.stylesheets, function(s) {
24343                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24344             });
24345             
24346         }
24347         
24348         st +=  '<style type="text/css">' +
24349             'IMG { cursor: pointer } ' +
24350         '</style>';
24351
24352         
24353         return '<html><head>' + st  +
24354             //<style type="text/css">' +
24355             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24356             //'</style>' +
24357             ' </head><body></body></html>';
24358     },
24359
24360     // private
24361     onRender : function(ct, position)
24362     {
24363         var _t = this;
24364         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24365         this.el.dom.style.border = '0 none';
24366         this.el.dom.setAttribute('tabIndex', -1);
24367         this.el.addClass('x-hidden');
24368         if(Roo.isIE){ // fix IE 1px bogus margin
24369             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24370         }
24371         this.wrap = this.el.wrap({
24372             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24373         });
24374         
24375         if (this.resizable) {
24376             this.resizeEl = new Roo.Resizable(this.wrap, {
24377                 pinned : true,
24378                 wrap: true,
24379                 dynamic : true,
24380                 minHeight : this.height,
24381                 height: this.height,
24382                 handles : this.resizable,
24383                 width: this.width,
24384                 listeners : {
24385                     resize : function(r, w, h) {
24386                         _t.onResize(w,h); // -something
24387                     }
24388                 }
24389             });
24390             
24391         }
24392
24393         this.frameId = Roo.id();
24394         
24395         this.createToolbar(this);
24396         
24397       
24398         
24399         var iframe = this.wrap.createChild({
24400             tag: 'iframe',
24401             id: this.frameId,
24402             name: this.frameId,
24403             frameBorder : 'no',
24404             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24405         }, this.el
24406         );
24407         
24408        // console.log(iframe);
24409         //this.wrap.dom.appendChild(iframe);
24410
24411         this.iframe = iframe.dom;
24412
24413          this.assignDocWin();
24414         
24415         this.doc.designMode = 'on';
24416        
24417         this.doc.open();
24418         this.doc.write(this.getDocMarkup());
24419         this.doc.close();
24420
24421         
24422         var task = { // must defer to wait for browser to be ready
24423             run : function(){
24424                 //console.log("run task?" + this.doc.readyState);
24425                 this.assignDocWin();
24426                 if(this.doc.body || this.doc.readyState == 'complete'){
24427                     try {
24428                         this.doc.designMode="on";
24429                     } catch (e) {
24430                         return;
24431                     }
24432                     Roo.TaskMgr.stop(task);
24433                     this.initEditor.defer(10, this);
24434                 }
24435             },
24436             interval : 10,
24437             duration:10000,
24438             scope: this
24439         };
24440         Roo.TaskMgr.start(task);
24441
24442         if(!this.width){
24443             this.setSize(this.wrap.getSize());
24444         }
24445         if (this.resizeEl) {
24446             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24447             // should trigger onReize..
24448         }
24449     },
24450
24451     // private
24452     onResize : function(w, h)
24453     {
24454         //Roo.log('resize: ' +w + ',' + h );
24455         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24456         if(this.el && this.iframe){
24457             if(typeof w == 'number'){
24458                 var aw = w - this.wrap.getFrameWidth('lr');
24459                 this.el.setWidth(this.adjustWidth('textarea', aw));
24460                 this.iframe.style.width = aw + 'px';
24461             }
24462             if(typeof h == 'number'){
24463                 var tbh = 0;
24464                 for (var i =0; i < this.toolbars.length;i++) {
24465                     // fixme - ask toolbars for heights?
24466                     tbh += this.toolbars[i].tb.el.getHeight();
24467                     if (this.toolbars[i].footer) {
24468                         tbh += this.toolbars[i].footer.el.getHeight();
24469                     }
24470                 }
24471                 
24472                 
24473                 
24474                 
24475                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24476                 ah -= 5; // knock a few pixes off for look..
24477                 this.el.setHeight(this.adjustWidth('textarea', ah));
24478                 this.iframe.style.height = ah + 'px';
24479                 if(this.doc){
24480                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24481                 }
24482             }
24483         }
24484     },
24485
24486     /**
24487      * Toggles the editor between standard and source edit mode.
24488      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24489      */
24490     toggleSourceEdit : function(sourceEditMode){
24491         
24492         this.sourceEditMode = sourceEditMode === true;
24493         
24494         if(this.sourceEditMode){
24495           
24496             this.syncValue();
24497             this.iframe.className = 'x-hidden';
24498             this.el.removeClass('x-hidden');
24499             this.el.dom.removeAttribute('tabIndex');
24500             this.el.focus();
24501         }else{
24502              
24503             this.pushValue();
24504             this.iframe.className = '';
24505             this.el.addClass('x-hidden');
24506             this.el.dom.setAttribute('tabIndex', -1);
24507             this.deferFocus();
24508         }
24509         this.setSize(this.wrap.getSize());
24510         this.fireEvent('editmodechange', this, this.sourceEditMode);
24511     },
24512
24513     // private used internally
24514     createLink : function(){
24515         var url = prompt(this.createLinkText, this.defaultLinkValue);
24516         if(url && url != 'http:/'+'/'){
24517             this.relayCmd('createlink', url);
24518         }
24519     },
24520
24521     // private (for BoxComponent)
24522     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24523
24524     // private (for BoxComponent)
24525     getResizeEl : function(){
24526         return this.wrap;
24527     },
24528
24529     // private (for BoxComponent)
24530     getPositionEl : function(){
24531         return this.wrap;
24532     },
24533
24534     // private
24535     initEvents : function(){
24536         this.originalValue = this.getValue();
24537     },
24538
24539     /**
24540      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24541      * @method
24542      */
24543     markInvalid : Roo.emptyFn,
24544     /**
24545      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24546      * @method
24547      */
24548     clearInvalid : Roo.emptyFn,
24549
24550     setValue : function(v){
24551         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24552         this.pushValue();
24553     },
24554
24555     /**
24556      * Protected method that will not generally be called directly. If you need/want
24557      * custom HTML cleanup, this is the method you should override.
24558      * @param {String} html The HTML to be cleaned
24559      * return {String} The cleaned HTML
24560      */
24561     cleanHtml : function(html){
24562         html = String(html);
24563         if(html.length > 5){
24564             if(Roo.isSafari){ // strip safari nonsense
24565                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24566             }
24567         }
24568         if(html == '&nbsp;'){
24569             html = '';
24570         }
24571         return html;
24572     },
24573
24574     /**
24575      * Protected method that will not generally be called directly. Syncs the contents
24576      * of the editor iframe with the textarea.
24577      */
24578     syncValue : function(){
24579         if(this.initialized){
24580             var bd = (this.doc.body || this.doc.documentElement);
24581             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24582             var html = bd.innerHTML;
24583             if(Roo.isSafari){
24584                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24585                 var m = bs.match(/text-align:(.*?);/i);
24586                 if(m && m[1]){
24587                     html = '<div style="'+m[0]+'">' + html + '</div>';
24588                 }
24589             }
24590             html = this.cleanHtml(html);
24591             // fix up the special chars..
24592             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24593                 return "&#"+b.charCodeAt()+";" 
24594             });
24595             if(this.fireEvent('beforesync', this, html) !== false){
24596                 this.el.dom.value = html;
24597                 this.fireEvent('sync', this, html);
24598             }
24599         }
24600     },
24601
24602     /**
24603      * Protected method that will not generally be called directly. Pushes the value of the textarea
24604      * into the iframe editor.
24605      */
24606     pushValue : function(){
24607         if(this.initialized){
24608             var v = this.el.dom.value;
24609             if(v.length < 1){
24610                 v = '&#160;';
24611             }
24612             
24613             if(this.fireEvent('beforepush', this, v) !== false){
24614                 var d = (this.doc.body || this.doc.documentElement);
24615                 d.innerHTML = v;
24616                 this.cleanUpPaste();
24617                 this.el.dom.value = d.innerHTML;
24618                 this.fireEvent('push', this, v);
24619             }
24620         }
24621     },
24622
24623     // private
24624     deferFocus : function(){
24625         this.focus.defer(10, this);
24626     },
24627
24628     // doc'ed in Field
24629     focus : function(){
24630         if(this.win && !this.sourceEditMode){
24631             this.win.focus();
24632         }else{
24633             this.el.focus();
24634         }
24635     },
24636     
24637     assignDocWin: function()
24638     {
24639         var iframe = this.iframe;
24640         
24641          if(Roo.isIE){
24642             this.doc = iframe.contentWindow.document;
24643             this.win = iframe.contentWindow;
24644         } else {
24645             if (!Roo.get(this.frameId)) {
24646                 return;
24647             }
24648             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24649             this.win = Roo.get(this.frameId).dom.contentWindow;
24650         }
24651     },
24652     
24653     // private
24654     initEditor : function(){
24655         //console.log("INIT EDITOR");
24656         this.assignDocWin();
24657         
24658         
24659         
24660         this.doc.designMode="on";
24661         this.doc.open();
24662         this.doc.write(this.getDocMarkup());
24663         this.doc.close();
24664         
24665         var dbody = (this.doc.body || this.doc.documentElement);
24666         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24667         // this copies styles from the containing element into thsi one..
24668         // not sure why we need all of this..
24669         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24670         ss['background-attachment'] = 'fixed'; // w3c
24671         dbody.bgProperties = 'fixed'; // ie
24672         Roo.DomHelper.applyStyles(dbody, ss);
24673         Roo.EventManager.on(this.doc, {
24674             //'mousedown': this.onEditorEvent,
24675             'mouseup': this.onEditorEvent,
24676             'dblclick': this.onEditorEvent,
24677             'click': this.onEditorEvent,
24678             'keyup': this.onEditorEvent,
24679             buffer:100,
24680             scope: this
24681         });
24682         if(Roo.isGecko){
24683             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24684         }
24685         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24686             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24687         }
24688         this.initialized = true;
24689
24690         this.fireEvent('initialize', this);
24691         this.pushValue();
24692     },
24693
24694     // private
24695     onDestroy : function(){
24696         
24697         
24698         
24699         if(this.rendered){
24700             
24701             for (var i =0; i < this.toolbars.length;i++) {
24702                 // fixme - ask toolbars for heights?
24703                 this.toolbars[i].onDestroy();
24704             }
24705             
24706             this.wrap.dom.innerHTML = '';
24707             this.wrap.remove();
24708         }
24709     },
24710
24711     // private
24712     onFirstFocus : function(){
24713         
24714         this.assignDocWin();
24715         
24716         
24717         this.activated = true;
24718         for (var i =0; i < this.toolbars.length;i++) {
24719             this.toolbars[i].onFirstFocus();
24720         }
24721        
24722         if(Roo.isGecko){ // prevent silly gecko errors
24723             this.win.focus();
24724             var s = this.win.getSelection();
24725             if(!s.focusNode || s.focusNode.nodeType != 3){
24726                 var r = s.getRangeAt(0);
24727                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24728                 r.collapse(true);
24729                 this.deferFocus();
24730             }
24731             try{
24732                 this.execCmd('useCSS', true);
24733                 this.execCmd('styleWithCSS', false);
24734             }catch(e){}
24735         }
24736         this.fireEvent('activate', this);
24737     },
24738
24739     // private
24740     adjustFont: function(btn){
24741         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24742         //if(Roo.isSafari){ // safari
24743         //    adjust *= 2;
24744        // }
24745         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24746         if(Roo.isSafari){ // safari
24747             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24748             v =  (v < 10) ? 10 : v;
24749             v =  (v > 48) ? 48 : v;
24750             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24751             
24752         }
24753         
24754         
24755         v = Math.max(1, v+adjust);
24756         
24757         this.execCmd('FontSize', v  );
24758     },
24759
24760     onEditorEvent : function(e){
24761         this.fireEvent('editorevent', this, e);
24762       //  this.updateToolbar();
24763         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24764     },
24765
24766     insertTag : function(tg)
24767     {
24768         // could be a bit smarter... -> wrap the current selected tRoo..
24769         
24770         this.execCmd("formatblock",   tg);
24771         
24772     },
24773     
24774     insertText : function(txt)
24775     {
24776         
24777         
24778         range = this.createRange();
24779         range.deleteContents();
24780                //alert(Sender.getAttribute('label'));
24781                
24782         range.insertNode(this.doc.createTextNode(txt));
24783     } ,
24784     
24785     // private
24786     relayBtnCmd : function(btn){
24787         this.relayCmd(btn.cmd);
24788     },
24789
24790     /**
24791      * Executes a Midas editor command on the editor document and performs necessary focus and
24792      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24793      * @param {String} cmd The Midas command
24794      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24795      */
24796     relayCmd : function(cmd, value){
24797         this.win.focus();
24798         this.execCmd(cmd, value);
24799         this.fireEvent('editorevent', this);
24800         //this.updateToolbar();
24801         this.deferFocus();
24802     },
24803
24804     /**
24805      * Executes a Midas editor command directly on the editor document.
24806      * For visual commands, you should use {@link #relayCmd} instead.
24807      * <b>This should only be called after the editor is initialized.</b>
24808      * @param {String} cmd The Midas command
24809      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24810      */
24811     execCmd : function(cmd, value){
24812         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24813         this.syncValue();
24814     },
24815  
24816  
24817    
24818     /**
24819      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24820      * to insert tRoo.
24821      * @param {String} text | dom node.. 
24822      */
24823     insertAtCursor : function(text)
24824     {
24825         
24826         
24827         
24828         if(!this.activated){
24829             return;
24830         }
24831         /*
24832         if(Roo.isIE){
24833             this.win.focus();
24834             var r = this.doc.selection.createRange();
24835             if(r){
24836                 r.collapse(true);
24837                 r.pasteHTML(text);
24838                 this.syncValue();
24839                 this.deferFocus();
24840             
24841             }
24842             return;
24843         }
24844         */
24845         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24846             this.win.focus();
24847             
24848             
24849             // from jquery ui (MIT licenced)
24850             var range, node;
24851             var win = this.win;
24852             
24853             if (win.getSelection && win.getSelection().getRangeAt) {
24854                 range = win.getSelection().getRangeAt(0);
24855                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24856                 range.insertNode(node);
24857             } else if (win.document.selection && win.document.selection.createRange) {
24858                 // no firefox support
24859                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24860                 win.document.selection.createRange().pasteHTML(txt);
24861             } else {
24862                 // no firefox support
24863                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24864                 this.execCmd('InsertHTML', txt);
24865             } 
24866             
24867             this.syncValue();
24868             
24869             this.deferFocus();
24870         }
24871     },
24872  // private
24873     mozKeyPress : function(e){
24874         if(e.ctrlKey){
24875             var c = e.getCharCode(), cmd;
24876           
24877             if(c > 0){
24878                 c = String.fromCharCode(c).toLowerCase();
24879                 switch(c){
24880                     case 'b':
24881                         cmd = 'bold';
24882                         break;
24883                     case 'i':
24884                         cmd = 'italic';
24885                         break;
24886                     
24887                     case 'u':
24888                         cmd = 'underline';
24889                         break;
24890                     
24891                     case 'v':
24892                         this.cleanUpPaste.defer(100, this);
24893                         return;
24894                         
24895                 }
24896                 if(cmd){
24897                     this.win.focus();
24898                     this.execCmd(cmd);
24899                     this.deferFocus();
24900                     e.preventDefault();
24901                 }
24902                 
24903             }
24904         }
24905     },
24906
24907     // private
24908     fixKeys : function(){ // load time branching for fastest keydown performance
24909         if(Roo.isIE){
24910             return function(e){
24911                 var k = e.getKey(), r;
24912                 if(k == e.TAB){
24913                     e.stopEvent();
24914                     r = this.doc.selection.createRange();
24915                     if(r){
24916                         r.collapse(true);
24917                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24918                         this.deferFocus();
24919                     }
24920                     return;
24921                 }
24922                 
24923                 if(k == e.ENTER){
24924                     r = this.doc.selection.createRange();
24925                     if(r){
24926                         var target = r.parentElement();
24927                         if(!target || target.tagName.toLowerCase() != 'li'){
24928                             e.stopEvent();
24929                             r.pasteHTML('<br />');
24930                             r.collapse(false);
24931                             r.select();
24932                         }
24933                     }
24934                 }
24935                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24936                     this.cleanUpPaste.defer(100, this);
24937                     return;
24938                 }
24939                 
24940                 
24941             };
24942         }else if(Roo.isOpera){
24943             return function(e){
24944                 var k = e.getKey();
24945                 if(k == e.TAB){
24946                     e.stopEvent();
24947                     this.win.focus();
24948                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24949                     this.deferFocus();
24950                 }
24951                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24952                     this.cleanUpPaste.defer(100, this);
24953                     return;
24954                 }
24955                 
24956             };
24957         }else if(Roo.isSafari){
24958             return function(e){
24959                 var k = e.getKey();
24960                 
24961                 if(k == e.TAB){
24962                     e.stopEvent();
24963                     this.execCmd('InsertText','\t');
24964                     this.deferFocus();
24965                     return;
24966                 }
24967                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24968                     this.cleanUpPaste.defer(100, this);
24969                     return;
24970                 }
24971                 
24972              };
24973         }
24974     }(),
24975     
24976     getAllAncestors: function()
24977     {
24978         var p = this.getSelectedNode();
24979         var a = [];
24980         if (!p) {
24981             a.push(p); // push blank onto stack..
24982             p = this.getParentElement();
24983         }
24984         
24985         
24986         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24987             a.push(p);
24988             p = p.parentNode;
24989         }
24990         a.push(this.doc.body);
24991         return a;
24992     },
24993     lastSel : false,
24994     lastSelNode : false,
24995     
24996     
24997     getSelection : function() 
24998     {
24999         this.assignDocWin();
25000         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25001     },
25002     
25003     getSelectedNode: function() 
25004     {
25005         // this may only work on Gecko!!!
25006         
25007         // should we cache this!!!!
25008         
25009         
25010         
25011          
25012         var range = this.createRange(this.getSelection()).cloneRange();
25013         
25014         if (Roo.isIE) {
25015             var parent = range.parentElement();
25016             while (true) {
25017                 var testRange = range.duplicate();
25018                 testRange.moveToElementText(parent);
25019                 if (testRange.inRange(range)) {
25020                     break;
25021                 }
25022                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25023                     break;
25024                 }
25025                 parent = parent.parentElement;
25026             }
25027             return parent;
25028         }
25029         
25030         // is ancestor a text element.
25031         var ac =  range.commonAncestorContainer;
25032         if (ac.nodeType == 3) {
25033             ac = ac.parentNode;
25034         }
25035         
25036         var ar = ac.childNodes;
25037          
25038         var nodes = [];
25039         var other_nodes = [];
25040         var has_other_nodes = false;
25041         for (var i=0;i<ar.length;i++) {
25042             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25043                 continue;
25044             }
25045             // fullly contained node.
25046             
25047             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25048                 nodes.push(ar[i]);
25049                 continue;
25050             }
25051             
25052             // probably selected..
25053             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25054                 other_nodes.push(ar[i]);
25055                 continue;
25056             }
25057             // outer..
25058             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25059                 continue;
25060             }
25061             
25062             
25063             has_other_nodes = true;
25064         }
25065         if (!nodes.length && other_nodes.length) {
25066             nodes= other_nodes;
25067         }
25068         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25069             return false;
25070         }
25071         
25072         return nodes[0];
25073     },
25074     createRange: function(sel)
25075     {
25076         // this has strange effects when using with 
25077         // top toolbar - not sure if it's a great idea.
25078         //this.editor.contentWindow.focus();
25079         if (typeof sel != "undefined") {
25080             try {
25081                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25082             } catch(e) {
25083                 return this.doc.createRange();
25084             }
25085         } else {
25086             return this.doc.createRange();
25087         }
25088     },
25089     getParentElement: function()
25090     {
25091         
25092         this.assignDocWin();
25093         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25094         
25095         var range = this.createRange(sel);
25096          
25097         try {
25098             var p = range.commonAncestorContainer;
25099             while (p.nodeType == 3) { // text node
25100                 p = p.parentNode;
25101             }
25102             return p;
25103         } catch (e) {
25104             return null;
25105         }
25106     
25107     },
25108     /***
25109      *
25110      * Range intersection.. the hard stuff...
25111      *  '-1' = before
25112      *  '0' = hits..
25113      *  '1' = after.
25114      *         [ -- selected range --- ]
25115      *   [fail]                        [fail]
25116      *
25117      *    basically..
25118      *      if end is before start or  hits it. fail.
25119      *      if start is after end or hits it fail.
25120      *
25121      *   if either hits (but other is outside. - then it's not 
25122      *   
25123      *    
25124      **/
25125     
25126     
25127     // @see http://www.thismuchiknow.co.uk/?p=64.
25128     rangeIntersectsNode : function(range, node)
25129     {
25130         var nodeRange = node.ownerDocument.createRange();
25131         try {
25132             nodeRange.selectNode(node);
25133         } catch (e) {
25134             nodeRange.selectNodeContents(node);
25135         }
25136     
25137         var rangeStartRange = range.cloneRange();
25138         rangeStartRange.collapse(true);
25139     
25140         var rangeEndRange = range.cloneRange();
25141         rangeEndRange.collapse(false);
25142     
25143         var nodeStartRange = nodeRange.cloneRange();
25144         nodeStartRange.collapse(true);
25145     
25146         var nodeEndRange = nodeRange.cloneRange();
25147         nodeEndRange.collapse(false);
25148     
25149         return rangeStartRange.compareBoundaryPoints(
25150                  Range.START_TO_START, nodeEndRange) == -1 &&
25151                rangeEndRange.compareBoundaryPoints(
25152                  Range.START_TO_START, nodeStartRange) == 1;
25153         
25154          
25155     },
25156     rangeCompareNode : function(range, node)
25157     {
25158         var nodeRange = node.ownerDocument.createRange();
25159         try {
25160             nodeRange.selectNode(node);
25161         } catch (e) {
25162             nodeRange.selectNodeContents(node);
25163         }
25164         
25165         
25166         range.collapse(true);
25167     
25168         nodeRange.collapse(true);
25169      
25170         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25171         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25172          
25173         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25174         
25175         var nodeIsBefore   =  ss == 1;
25176         var nodeIsAfter    = ee == -1;
25177         
25178         if (nodeIsBefore && nodeIsAfter)
25179             return 0; // outer
25180         if (!nodeIsBefore && nodeIsAfter)
25181             return 1; //right trailed.
25182         
25183         if (nodeIsBefore && !nodeIsAfter)
25184             return 2;  // left trailed.
25185         // fully contined.
25186         return 3;
25187     },
25188
25189     // private? - in a new class?
25190     cleanUpPaste :  function()
25191     {
25192         // cleans up the whole document..
25193          Roo.log('cleanuppaste');
25194         this.cleanUpChildren(this.doc.body);
25195         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25196         if (clean != this.doc.body.innerHTML) {
25197             this.doc.body.innerHTML = clean;
25198         }
25199         
25200     },
25201     
25202     cleanWordChars : function(input) {
25203         var he = Roo.form.HtmlEditor;
25204     
25205         var output = input;
25206         Roo.each(he.swapCodes, function(sw) { 
25207         
25208             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25209             output = output.replace(swapper, sw[1]);
25210         });
25211         return output;
25212     },
25213     
25214     
25215     cleanUpChildren : function (n)
25216     {
25217         if (!n.childNodes.length) {
25218             return;
25219         }
25220         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25221            this.cleanUpChild(n.childNodes[i]);
25222         }
25223     },
25224     
25225     
25226         
25227     
25228     cleanUpChild : function (node)
25229     {
25230         //console.log(node);
25231         if (node.nodeName == "#text") {
25232             // clean up silly Windows -- stuff?
25233             return; 
25234         }
25235         if (node.nodeName == "#comment") {
25236             node.parentNode.removeChild(node);
25237             // clean up silly Windows -- stuff?
25238             return; 
25239         }
25240         
25241         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25242             // remove node.
25243             node.parentNode.removeChild(node);
25244             return;
25245             
25246         }
25247         
25248         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25249         
25250         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25251         
25252         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25253             remove_keep_children = true;
25254         }
25255         
25256         if (remove_keep_children) {
25257             this.cleanUpChildren(node);
25258             // inserts everything just before this node...
25259             while (node.childNodes.length) {
25260                 var cn = node.childNodes[0];
25261                 node.removeChild(cn);
25262                 node.parentNode.insertBefore(cn, node);
25263             }
25264             node.parentNode.removeChild(node);
25265             return;
25266         }
25267         
25268         if (!node.attributes || !node.attributes.length) {
25269             this.cleanUpChildren(node);
25270             return;
25271         }
25272         
25273         function cleanAttr(n,v)
25274         {
25275             
25276             if (v.match(/^\./) || v.match(/^\//)) {
25277                 return;
25278             }
25279             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25280                 return;
25281             }
25282             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25283             node.removeAttribute(n);
25284             
25285         }
25286         
25287         function cleanStyle(n,v)
25288         {
25289             if (v.match(/expression/)) { //XSS?? should we even bother..
25290                 node.removeAttribute(n);
25291                 return;
25292             }
25293             
25294             
25295             var parts = v.split(/;/);
25296             Roo.each(parts, function(p) {
25297                 p = p.replace(/\s+/g,'');
25298                 if (!p.length) {
25299                     return true;
25300                 }
25301                 var l = p.split(':').shift().replace(/\s+/g,'');
25302                 
25303                 // only allow 'c whitelisted system attributes'
25304                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25305                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25306                     node.removeAttribute(n);
25307                     return false;
25308                 }
25309                 return true;
25310             });
25311             
25312             
25313         }
25314         
25315         
25316         for (var i = node.attributes.length-1; i > -1 ; i--) {
25317             var a = node.attributes[i];
25318             //console.log(a);
25319             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25320                 node.removeAttribute(a.name);
25321                 return;
25322             }
25323             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25324                 cleanAttr(a.name,a.value); // fixme..
25325                 return;
25326             }
25327             if (a.name == 'style') {
25328                 cleanStyle(a.name,a.value);
25329             }
25330             /// clean up MS crap..
25331             // tecnically this should be a list of valid class'es..
25332             
25333             
25334             if (a.name == 'class') {
25335                 if (a.value.match(/^Mso/)) {
25336                     node.className = '';
25337                 }
25338                 
25339                 if (a.value.match(/body/)) {
25340                     node.className = '';
25341                 }
25342             }
25343             
25344             // style cleanup!?
25345             // class cleanup?
25346             
25347         }
25348         
25349         
25350         this.cleanUpChildren(node);
25351         
25352         
25353     }
25354     
25355     
25356     // hide stuff that is not compatible
25357     /**
25358      * @event blur
25359      * @hide
25360      */
25361     /**
25362      * @event change
25363      * @hide
25364      */
25365     /**
25366      * @event focus
25367      * @hide
25368      */
25369     /**
25370      * @event specialkey
25371      * @hide
25372      */
25373     /**
25374      * @cfg {String} fieldClass @hide
25375      */
25376     /**
25377      * @cfg {String} focusClass @hide
25378      */
25379     /**
25380      * @cfg {String} autoCreate @hide
25381      */
25382     /**
25383      * @cfg {String} inputType @hide
25384      */
25385     /**
25386      * @cfg {String} invalidClass @hide
25387      */
25388     /**
25389      * @cfg {String} invalidText @hide
25390      */
25391     /**
25392      * @cfg {String} msgFx @hide
25393      */
25394     /**
25395      * @cfg {String} validateOnBlur @hide
25396      */
25397 });
25398
25399 Roo.form.HtmlEditor.white = [
25400         'area', 'br', 'img', 'input', 'hr', 'wbr',
25401         
25402        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25403        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25404        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25405        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25406        'table',   'ul',         'xmp', 
25407        
25408        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25409       'thead',   'tr', 
25410      
25411       'dir', 'menu', 'ol', 'ul', 'dl',
25412        
25413       'embed',  'object'
25414 ];
25415
25416
25417 Roo.form.HtmlEditor.black = [
25418     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25419         'applet', // 
25420         'base',   'basefont', 'bgsound', 'blink',  'body', 
25421         'frame',  'frameset', 'head',    'html',   'ilayer', 
25422         'iframe', 'layer',  'link',     'meta',    'object',   
25423         'script', 'style' ,'title',  'xml' // clean later..
25424 ];
25425 Roo.form.HtmlEditor.clean = [
25426     'script', 'style', 'title', 'xml'
25427 ];
25428 Roo.form.HtmlEditor.remove = [
25429     'font'
25430 ];
25431 // attributes..
25432
25433 Roo.form.HtmlEditor.ablack = [
25434     'on'
25435 ];
25436     
25437 Roo.form.HtmlEditor.aclean = [ 
25438     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25439 ];
25440
25441 // protocols..
25442 Roo.form.HtmlEditor.pwhite= [
25443         'http',  'https',  'mailto'
25444 ];
25445
25446 // white listed style attributes.
25447 Roo.form.HtmlEditor.cwhite= [
25448         'text-align',
25449         'font-size'
25450 ];
25451
25452
25453 Roo.form.HtmlEditor.swapCodes   =[ 
25454     [    8211, "--" ], 
25455     [    8212, "--" ], 
25456     [    8216,  "'" ],  
25457     [    8217, "'" ],  
25458     [    8220, '"' ],  
25459     [    8221, '"' ],  
25460     [    8226, "*" ],  
25461     [    8230, "..." ]
25462 ]; 
25463
25464     // <script type="text/javascript">
25465 /*
25466  * Based on
25467  * Ext JS Library 1.1.1
25468  * Copyright(c) 2006-2007, Ext JS, LLC.
25469  *  
25470  
25471  */
25472
25473 /**
25474  * @class Roo.form.HtmlEditorToolbar1
25475  * Basic Toolbar
25476  * 
25477  * Usage:
25478  *
25479  new Roo.form.HtmlEditor({
25480     ....
25481     toolbars : [
25482         new Roo.form.HtmlEditorToolbar1({
25483             disable : { fonts: 1 , format: 1, ..., ... , ...],
25484             btns : [ .... ]
25485         })
25486     }
25487      
25488  * 
25489  * @cfg {Object} disable List of elements to disable..
25490  * @cfg {Array} btns List of additional buttons.
25491  * 
25492  * 
25493  * NEEDS Extra CSS? 
25494  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25495  */
25496  
25497 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25498 {
25499     
25500     Roo.apply(this, config);
25501     
25502     // default disabled, based on 'good practice'..
25503     this.disable = this.disable || {};
25504     Roo.applyIf(this.disable, {
25505         fontSize : true,
25506         colors : true,
25507         specialElements : true
25508     });
25509     
25510     
25511     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25512     // dont call parent... till later.
25513 }
25514
25515 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25516     
25517     tb: false,
25518     
25519     rendered: false,
25520     
25521     editor : false,
25522     /**
25523      * @cfg {Object} disable  List of toolbar elements to disable
25524          
25525      */
25526     disable : false,
25527       /**
25528      * @cfg {Array} fontFamilies An array of available font families
25529      */
25530     fontFamilies : [
25531         'Arial',
25532         'Courier New',
25533         'Tahoma',
25534         'Times New Roman',
25535         'Verdana'
25536     ],
25537     
25538     specialChars : [
25539            "&#169;",
25540           "&#174;",     
25541           "&#8482;",    
25542           "&#163;" ,    
25543          // "&#8212;",    
25544           "&#8230;",    
25545           "&#247;" ,    
25546         //  "&#225;" ,     ?? a acute?
25547            "&#8364;"    , //Euro
25548        //   "&#8220;"    ,
25549         //  "&#8221;"    ,
25550         //  "&#8226;"    ,
25551           "&#176;"  //   , // degrees
25552
25553          // "&#233;"     , // e ecute
25554          // "&#250;"     , // u ecute?
25555     ],
25556     
25557     specialElements : [
25558         {
25559             text: "Insert Table",
25560             xtype: 'MenuItem',
25561             xns : Roo.Menu,
25562             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
25563                 
25564         },
25565         {    
25566             text: "Insert Image",
25567             xtype: 'MenuItem',
25568             xns : Roo.Menu,
25569             ihtml : '<img src="about:blank"/>'
25570             
25571         }
25572         
25573          
25574     ],
25575     
25576     
25577     inputElements : [ 
25578             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25579             "input:submit", "input:button", "select", "textarea", "label" ],
25580     formats : [
25581         ["p"] ,  
25582         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25583         ["pre"],[ "code"], 
25584         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25585     ],
25586      /**
25587      * @cfg {String} defaultFont default font to use.
25588      */
25589     defaultFont: 'tahoma',
25590    
25591     fontSelect : false,
25592     
25593     
25594     formatCombo : false,
25595     
25596     init : function(editor)
25597     {
25598         this.editor = editor;
25599         
25600         
25601         var fid = editor.frameId;
25602         var etb = this;
25603         function btn(id, toggle, handler){
25604             var xid = fid + '-'+ id ;
25605             return {
25606                 id : xid,
25607                 cmd : id,
25608                 cls : 'x-btn-icon x-edit-'+id,
25609                 enableToggle:toggle !== false,
25610                 scope: editor, // was editor...
25611                 handler:handler||editor.relayBtnCmd,
25612                 clickEvent:'mousedown',
25613                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25614                 tabIndex:-1
25615             };
25616         }
25617         
25618         
25619         
25620         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25621         this.tb = tb;
25622          // stop form submits
25623         tb.el.on('click', function(e){
25624             e.preventDefault(); // what does this do?
25625         });
25626
25627         if(!this.disable.font && !Roo.isSafari){
25628             /* why no safari for fonts
25629             editor.fontSelect = tb.el.createChild({
25630                 tag:'select',
25631                 tabIndex: -1,
25632                 cls:'x-font-select',
25633                 html: editor.createFontOptions()
25634             });
25635             editor.fontSelect.on('change', function(){
25636                 var font = editor.fontSelect.dom.value;
25637                 editor.relayCmd('fontname', font);
25638                 editor.deferFocus();
25639             }, editor);
25640             tb.add(
25641                 editor.fontSelect.dom,
25642                 '-'
25643             );
25644             */
25645         };
25646         if(!this.disable.formats){
25647             this.formatCombo = new Roo.form.ComboBox({
25648                 store: new Roo.data.SimpleStore({
25649                     id : 'tag',
25650                     fields: ['tag'],
25651                     data : this.formats // from states.js
25652                 }),
25653                 blockFocus : true,
25654                 //autoCreate : {tag: "div",  size: "20"},
25655                 displayField:'tag',
25656                 typeAhead: false,
25657                 mode: 'local',
25658                 editable : false,
25659                 triggerAction: 'all',
25660                 emptyText:'Add tag',
25661                 selectOnFocus:true,
25662                 width:135,
25663                 listeners : {
25664                     'select': function(c, r, i) {
25665                         editor.insertTag(r.get('tag'));
25666                         editor.focus();
25667                     }
25668                 }
25669
25670             });
25671             tb.addField(this.formatCombo);
25672             
25673         }
25674         
25675         if(!this.disable.format){
25676             tb.add(
25677                 btn('bold'),
25678                 btn('italic'),
25679                 btn('underline')
25680             );
25681         };
25682         if(!this.disable.fontSize){
25683             tb.add(
25684                 '-',
25685                 
25686                 
25687                 btn('increasefontsize', false, editor.adjustFont),
25688                 btn('decreasefontsize', false, editor.adjustFont)
25689             );
25690         };
25691         
25692         
25693         if(!this.disable.colors){
25694             tb.add(
25695                 '-', {
25696                     id:editor.frameId +'-forecolor',
25697                     cls:'x-btn-icon x-edit-forecolor',
25698                     clickEvent:'mousedown',
25699                     tooltip: this.buttonTips['forecolor'] || undefined,
25700                     tabIndex:-1,
25701                     menu : new Roo.menu.ColorMenu({
25702                         allowReselect: true,
25703                         focus: Roo.emptyFn,
25704                         value:'000000',
25705                         plain:true,
25706                         selectHandler: function(cp, color){
25707                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25708                             editor.deferFocus();
25709                         },
25710                         scope: editor,
25711                         clickEvent:'mousedown'
25712                     })
25713                 }, {
25714                     id:editor.frameId +'backcolor',
25715                     cls:'x-btn-icon x-edit-backcolor',
25716                     clickEvent:'mousedown',
25717                     tooltip: this.buttonTips['backcolor'] || undefined,
25718                     tabIndex:-1,
25719                     menu : new Roo.menu.ColorMenu({
25720                         focus: Roo.emptyFn,
25721                         value:'FFFFFF',
25722                         plain:true,
25723                         allowReselect: true,
25724                         selectHandler: function(cp, color){
25725                             if(Roo.isGecko){
25726                                 editor.execCmd('useCSS', false);
25727                                 editor.execCmd('hilitecolor', color);
25728                                 editor.execCmd('useCSS', true);
25729                                 editor.deferFocus();
25730                             }else{
25731                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25732                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25733                                 editor.deferFocus();
25734                             }
25735                         },
25736                         scope:editor,
25737                         clickEvent:'mousedown'
25738                     })
25739                 }
25740             );
25741         };
25742         // now add all the items...
25743         
25744
25745         if(!this.disable.alignments){
25746             tb.add(
25747                 '-',
25748                 btn('justifyleft'),
25749                 btn('justifycenter'),
25750                 btn('justifyright')
25751             );
25752         };
25753
25754         //if(!Roo.isSafari){
25755             if(!this.disable.links){
25756                 tb.add(
25757                     '-',
25758                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25759                 );
25760             };
25761
25762             if(!this.disable.lists){
25763                 tb.add(
25764                     '-',
25765                     btn('insertorderedlist'),
25766                     btn('insertunorderedlist')
25767                 );
25768             }
25769             if(!this.disable.sourceEdit){
25770                 tb.add(
25771                     '-',
25772                     btn('sourceedit', true, function(btn){
25773                         this.toggleSourceEdit(btn.pressed);
25774                     })
25775                 );
25776             }
25777         //}
25778         
25779         var smenu = { };
25780         // special menu.. - needs to be tidied up..
25781         if (!this.disable.special) {
25782             smenu = {
25783                 text: "&#169;",
25784                 cls: 'x-edit-none',
25785                 
25786                 menu : {
25787                     items : []
25788                 }
25789             };
25790             for (var i =0; i < this.specialChars.length; i++) {
25791                 smenu.menu.items.push({
25792                     
25793                     html: this.specialChars[i],
25794                     handler: function(a,b) {
25795                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25796                         //editor.insertAtCursor(a.html);
25797                         
25798                     },
25799                     tabIndex:-1
25800                 });
25801             }
25802             
25803             
25804             tb.add(smenu);
25805             
25806             
25807         }
25808          
25809         if (!this.disable.specialElements) {
25810             var semenu = {
25811                 text: "Other;",
25812                 cls: 'x-edit-none',
25813                 menu : {
25814                     items : []
25815                 }
25816             };
25817             for (var i =0; i < this.specialElements.length; i++) {
25818                 semenu.menu.items.push(
25819                     Roo.apply({ 
25820                         handler: function(a,b) {
25821                             editor.insertAtCursor(this.ihtml);
25822                         }
25823                     }, this.specialElements[i])
25824                 );
25825                     
25826             }
25827             
25828             tb.add(semenu);
25829             
25830             
25831         }
25832          
25833         
25834         if (this.btns) {
25835             for(var i =0; i< this.btns.length;i++) {
25836                 var b = this.btns[i];
25837                 b.cls =  'x-edit-none';
25838                 b.scope = editor;
25839                 tb.add(b);
25840             }
25841         
25842         }
25843         
25844         
25845         
25846         // disable everything...
25847         
25848         this.tb.items.each(function(item){
25849            if(item.id != editor.frameId+ '-sourceedit'){
25850                 item.disable();
25851             }
25852         });
25853         this.rendered = true;
25854         
25855         // the all the btns;
25856         editor.on('editorevent', this.updateToolbar, this);
25857         // other toolbars need to implement this..
25858         //editor.on('editmodechange', this.updateToolbar, this);
25859     },
25860     
25861     
25862     
25863     /**
25864      * Protected method that will not generally be called directly. It triggers
25865      * a toolbar update by reading the markup state of the current selection in the editor.
25866      */
25867     updateToolbar: function(){
25868
25869         if(!this.editor.activated){
25870             this.editor.onFirstFocus();
25871             return;
25872         }
25873
25874         var btns = this.tb.items.map, 
25875             doc = this.editor.doc,
25876             frameId = this.editor.frameId;
25877
25878         if(!this.disable.font && !Roo.isSafari){
25879             /*
25880             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25881             if(name != this.fontSelect.dom.value){
25882                 this.fontSelect.dom.value = name;
25883             }
25884             */
25885         }
25886         if(!this.disable.format){
25887             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25888             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25889             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25890         }
25891         if(!this.disable.alignments){
25892             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25893             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25894             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25895         }
25896         if(!Roo.isSafari && !this.disable.lists){
25897             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25898             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25899         }
25900         
25901         var ans = this.editor.getAllAncestors();
25902         if (this.formatCombo) {
25903             
25904             
25905             var store = this.formatCombo.store;
25906             this.formatCombo.setValue("");
25907             for (var i =0; i < ans.length;i++) {
25908                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25909                     // select it..
25910                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25911                     break;
25912                 }
25913             }
25914         }
25915         
25916         
25917         
25918         // hides menus... - so this cant be on a menu...
25919         Roo.menu.MenuMgr.hideAll();
25920
25921         //this.editorsyncValue();
25922     },
25923    
25924     
25925     createFontOptions : function(){
25926         var buf = [], fs = this.fontFamilies, ff, lc;
25927         for(var i = 0, len = fs.length; i< len; i++){
25928             ff = fs[i];
25929             lc = ff.toLowerCase();
25930             buf.push(
25931                 '<option value="',lc,'" style="font-family:',ff,';"',
25932                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25933                     ff,
25934                 '</option>'
25935             );
25936         }
25937         return buf.join('');
25938     },
25939     
25940     toggleSourceEdit : function(sourceEditMode){
25941         if(sourceEditMode === undefined){
25942             sourceEditMode = !this.sourceEditMode;
25943         }
25944         this.sourceEditMode = sourceEditMode === true;
25945         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25946         // just toggle the button?
25947         if(btn.pressed !== this.editor.sourceEditMode){
25948             btn.toggle(this.editor.sourceEditMode);
25949             return;
25950         }
25951         
25952         if(this.sourceEditMode){
25953             this.tb.items.each(function(item){
25954                 if(item.cmd != 'sourceedit'){
25955                     item.disable();
25956                 }
25957             });
25958           
25959         }else{
25960             if(this.initialized){
25961                 this.tb.items.each(function(item){
25962                     item.enable();
25963                 });
25964             }
25965             
25966         }
25967         // tell the editor that it's been pressed..
25968         this.editor.toggleSourceEdit(sourceEditMode);
25969        
25970     },
25971      /**
25972      * Object collection of toolbar tooltips for the buttons in the editor. The key
25973      * is the command id associated with that button and the value is a valid QuickTips object.
25974      * For example:
25975 <pre><code>
25976 {
25977     bold : {
25978         title: 'Bold (Ctrl+B)',
25979         text: 'Make the selected text bold.',
25980         cls: 'x-html-editor-tip'
25981     },
25982     italic : {
25983         title: 'Italic (Ctrl+I)',
25984         text: 'Make the selected text italic.',
25985         cls: 'x-html-editor-tip'
25986     },
25987     ...
25988 </code></pre>
25989     * @type Object
25990      */
25991     buttonTips : {
25992         bold : {
25993             title: 'Bold (Ctrl+B)',
25994             text: 'Make the selected text bold.',
25995             cls: 'x-html-editor-tip'
25996         },
25997         italic : {
25998             title: 'Italic (Ctrl+I)',
25999             text: 'Make the selected text italic.',
26000             cls: 'x-html-editor-tip'
26001         },
26002         underline : {
26003             title: 'Underline (Ctrl+U)',
26004             text: 'Underline the selected text.',
26005             cls: 'x-html-editor-tip'
26006         },
26007         increasefontsize : {
26008             title: 'Grow Text',
26009             text: 'Increase the font size.',
26010             cls: 'x-html-editor-tip'
26011         },
26012         decreasefontsize : {
26013             title: 'Shrink Text',
26014             text: 'Decrease the font size.',
26015             cls: 'x-html-editor-tip'
26016         },
26017         backcolor : {
26018             title: 'Text Highlight Color',
26019             text: 'Change the background color of the selected text.',
26020             cls: 'x-html-editor-tip'
26021         },
26022         forecolor : {
26023             title: 'Font Color',
26024             text: 'Change the color of the selected text.',
26025             cls: 'x-html-editor-tip'
26026         },
26027         justifyleft : {
26028             title: 'Align Text Left',
26029             text: 'Align text to the left.',
26030             cls: 'x-html-editor-tip'
26031         },
26032         justifycenter : {
26033             title: 'Center Text',
26034             text: 'Center text in the editor.',
26035             cls: 'x-html-editor-tip'
26036         },
26037         justifyright : {
26038             title: 'Align Text Right',
26039             text: 'Align text to the right.',
26040             cls: 'x-html-editor-tip'
26041         },
26042         insertunorderedlist : {
26043             title: 'Bullet List',
26044             text: 'Start a bulleted list.',
26045             cls: 'x-html-editor-tip'
26046         },
26047         insertorderedlist : {
26048             title: 'Numbered List',
26049             text: 'Start a numbered list.',
26050             cls: 'x-html-editor-tip'
26051         },
26052         createlink : {
26053             title: 'Hyperlink',
26054             text: 'Make the selected text a hyperlink.',
26055             cls: 'x-html-editor-tip'
26056         },
26057         sourceedit : {
26058             title: 'Source Edit',
26059             text: 'Switch to source editing mode.',
26060             cls: 'x-html-editor-tip'
26061         }
26062     },
26063     // private
26064     onDestroy : function(){
26065         if(this.rendered){
26066             
26067             this.tb.items.each(function(item){
26068                 if(item.menu){
26069                     item.menu.removeAll();
26070                     if(item.menu.el){
26071                         item.menu.el.destroy();
26072                     }
26073                 }
26074                 item.destroy();
26075             });
26076              
26077         }
26078     },
26079     onFirstFocus: function() {
26080         this.tb.items.each(function(item){
26081            item.enable();
26082         });
26083     }
26084 });
26085
26086
26087
26088
26089 // <script type="text/javascript">
26090 /*
26091  * Based on
26092  * Ext JS Library 1.1.1
26093  * Copyright(c) 2006-2007, Ext JS, LLC.
26094  *  
26095  
26096  */
26097
26098  
26099 /**
26100  * @class Roo.form.HtmlEditor.ToolbarContext
26101  * Context Toolbar
26102  * 
26103  * Usage:
26104  *
26105  new Roo.form.HtmlEditor({
26106     ....
26107     toolbars : [
26108         { xtype: 'ToolbarStandard', styles : {} }
26109         { xtype: 'ToolbarContext', disable : {} }
26110     ]
26111 })
26112
26113      
26114  * 
26115  * @config : {Object} disable List of elements to disable.. (not done yet.)
26116  * @config : {Object} styles  Map of styles available.
26117  * 
26118  */
26119
26120 Roo.form.HtmlEditor.ToolbarContext = function(config)
26121 {
26122     
26123     Roo.apply(this, config);
26124     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26125     // dont call parent... till later.
26126     this.styles = this.styles || {};
26127 }
26128 Roo.form.HtmlEditor.ToolbarContext.types = {
26129     'IMG' : {
26130         width : {
26131             title: "Width",
26132             width: 40
26133         },
26134         height:  {
26135             title: "Height",
26136             width: 40
26137         },
26138         align: {
26139             title: "Align",
26140             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26141             width : 80
26142             
26143         },
26144         border: {
26145             title: "Border",
26146             width: 40
26147         },
26148         alt: {
26149             title: "Alt",
26150             width: 120
26151         },
26152         src : {
26153             title: "Src",
26154             width: 220
26155         }
26156         
26157     },
26158     'A' : {
26159         name : {
26160             title: "Name",
26161             width: 50
26162         },
26163         href:  {
26164             title: "Href",
26165             width: 220
26166         } // border?
26167         
26168     },
26169     'TABLE' : {
26170         rows : {
26171             title: "Rows",
26172             width: 20
26173         },
26174         cols : {
26175             title: "Cols",
26176             width: 20
26177         },
26178         width : {
26179             title: "Width",
26180             width: 40
26181         },
26182         height : {
26183             title: "Height",
26184             width: 40
26185         },
26186         border : {
26187             title: "Border",
26188             width: 20
26189         }
26190     },
26191     'TD' : {
26192         width : {
26193             title: "Width",
26194             width: 40
26195         },
26196         height : {
26197             title: "Height",
26198             width: 40
26199         },   
26200         align: {
26201             title: "Align",
26202             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26203             width: 80
26204         },
26205         valign: {
26206             title: "Valign",
26207             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26208             width: 80
26209         },
26210         colspan: {
26211             title: "Colspan",
26212             width: 20
26213             
26214         }
26215     },
26216     'INPUT' : {
26217         name : {
26218             title: "name",
26219             width: 120
26220         },
26221         value : {
26222             title: "Value",
26223             width: 120
26224         },
26225         width : {
26226             title: "Width",
26227             width: 40
26228         }
26229     },
26230     'LABEL' : {
26231         'for' : {
26232             title: "For",
26233             width: 120
26234         }
26235     },
26236     'TEXTAREA' : {
26237           name : {
26238             title: "name",
26239             width: 120
26240         },
26241         rows : {
26242             title: "Rows",
26243             width: 20
26244         },
26245         cols : {
26246             title: "Cols",
26247             width: 20
26248         }
26249     },
26250     'SELECT' : {
26251         name : {
26252             title: "name",
26253             width: 120
26254         },
26255         selectoptions : {
26256             title: "Options",
26257             width: 200
26258         }
26259     },
26260     
26261     // should we really allow this??
26262     // should this just be 
26263     'BODY' : {
26264         title : {
26265             title: "title",
26266             width: 200,
26267             disabled : true
26268         }
26269     },
26270     '*' : {
26271         // empty..
26272     }
26273 };
26274
26275
26276
26277 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26278     
26279     tb: false,
26280     
26281     rendered: false,
26282     
26283     editor : false,
26284     /**
26285      * @cfg {Object} disable  List of toolbar elements to disable
26286          
26287      */
26288     disable : false,
26289     /**
26290      * @cfg {Object} styles List of styles 
26291      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26292      *
26293      * These must be defined in the page, so they get rendered correctly..
26294      * .headline { }
26295      * TD.underline { }
26296      * 
26297      */
26298     styles : false,
26299     
26300     
26301     
26302     toolbars : false,
26303     
26304     init : function(editor)
26305     {
26306         this.editor = editor;
26307         
26308         
26309         var fid = editor.frameId;
26310         var etb = this;
26311         function btn(id, toggle, handler){
26312             var xid = fid + '-'+ id ;
26313             return {
26314                 id : xid,
26315                 cmd : id,
26316                 cls : 'x-btn-icon x-edit-'+id,
26317                 enableToggle:toggle !== false,
26318                 scope: editor, // was editor...
26319                 handler:handler||editor.relayBtnCmd,
26320                 clickEvent:'mousedown',
26321                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26322                 tabIndex:-1
26323             };
26324         }
26325         // create a new element.
26326         var wdiv = editor.wrap.createChild({
26327                 tag: 'div'
26328             }, editor.wrap.dom.firstChild.nextSibling, true);
26329         
26330         // can we do this more than once??
26331         
26332          // stop form submits
26333       
26334  
26335         // disable everything...
26336         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26337         this.toolbars = {};
26338            
26339         for (var i in  ty) {
26340           
26341             this.toolbars[i] = this.buildToolbar(ty[i],i);
26342         }
26343         this.tb = this.toolbars.BODY;
26344         this.tb.el.show();
26345         this.buildFooter();
26346         this.footer.show();
26347          
26348         this.rendered = true;
26349         
26350         // the all the btns;
26351         editor.on('editorevent', this.updateToolbar, this);
26352         // other toolbars need to implement this..
26353         //editor.on('editmodechange', this.updateToolbar, this);
26354     },
26355     
26356     
26357     
26358     /**
26359      * Protected method that will not generally be called directly. It triggers
26360      * a toolbar update by reading the markup state of the current selection in the editor.
26361      */
26362     updateToolbar: function(editor,ev,sel){
26363
26364         //Roo.log(ev);
26365         // capture mouse up - this is handy for selecting images..
26366         // perhaps should go somewhere else...
26367         if(!this.editor.activated){
26368              this.editor.onFirstFocus();
26369             return;
26370         }
26371         
26372         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
26373         // selectNode - might want to handle IE?
26374         if ((ev.type == 'mouseup' || ev.type == 'click' ) &&
26375             ev.target && ev.target.tagName == 'IMG') {
26376             // they have click on an image...
26377             // let's see if we can change the selection...
26378             sel = ev.target;
26379          
26380               var nodeRange = sel.ownerDocument.createRange();
26381             try {
26382                 nodeRange.selectNode(sel);
26383             } catch (e) {
26384                 nodeRange.selectNodeContents(sel);
26385             }
26386             //nodeRange.collapse(true);
26387             var s = editor.win.getSelection();
26388             s.removeAllRanges();
26389             s.addRange(nodeRange);
26390         }  
26391         
26392       
26393         var updateFooter = sel ? false : true;
26394         
26395         
26396         var ans = this.editor.getAllAncestors();
26397         
26398         // pick
26399         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26400         
26401         if (!sel) { 
26402             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26403             sel = sel ? sel : this.editor.doc.body;
26404             sel = sel.tagName.length ? sel : this.editor.doc.body;
26405             
26406         }
26407         // pick a menu that exists..
26408         var tn = sel.tagName.toUpperCase();
26409         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26410         
26411         tn = sel.tagName.toUpperCase();
26412         
26413         var lastSel = this.tb.selectedNode
26414         
26415         this.tb.selectedNode = sel;
26416         
26417         // if current menu does not match..
26418         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26419                 
26420             this.tb.el.hide();
26421             ///console.log("show: " + tn);
26422             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26423             this.tb.el.show();
26424             // update name
26425             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26426             
26427             
26428             // update attributes
26429             if (this.tb.fields) {
26430                 this.tb.fields.each(function(e) {
26431                    e.setValue(sel.getAttribute(e.name));
26432                 });
26433             }
26434             
26435             // update styles
26436             var st = this.tb.fields.item(0);
26437             st.store.removeAll();
26438             var cn = sel.className.split(/\s+/);
26439             
26440             var avs = [];
26441             if (this.styles['*']) {
26442                 
26443                 Roo.each(this.styles['*'], function(v) {
26444                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26445                 });
26446             }
26447             if (this.styles[tn]) { 
26448                 Roo.each(this.styles[tn], function(v) {
26449                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26450                 });
26451             }
26452             
26453             st.store.loadData(avs);
26454             st.collapse();
26455             st.setValue(cn);
26456             
26457             // flag our selected Node.
26458             this.tb.selectedNode = sel;
26459            
26460            
26461             Roo.menu.MenuMgr.hideAll();
26462
26463         }
26464         
26465         if (!updateFooter) {
26466             return;
26467         }
26468         // update the footer
26469         //
26470         var html = '';
26471         
26472         this.footerEls = ans.reverse();
26473         Roo.each(this.footerEls, function(a,i) {
26474             if (!a) { return; }
26475             html += html.length ? ' &gt; '  :  '';
26476             
26477             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26478             
26479         });
26480        
26481         // 
26482         var sz = this.footDisp.up('td').getSize();
26483         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26484         this.footDisp.dom.style.marginLeft = '5px';
26485         
26486         this.footDisp.dom.style.overflow = 'hidden';
26487         
26488         this.footDisp.dom.innerHTML = html;
26489             
26490         //this.editorsyncValue();
26491     },
26492    
26493        
26494     // private
26495     onDestroy : function(){
26496         if(this.rendered){
26497             
26498             this.tb.items.each(function(item){
26499                 if(item.menu){
26500                     item.menu.removeAll();
26501                     if(item.menu.el){
26502                         item.menu.el.destroy();
26503                     }
26504                 }
26505                 item.destroy();
26506             });
26507              
26508         }
26509     },
26510     onFirstFocus: function() {
26511         // need to do this for all the toolbars..
26512         this.tb.items.each(function(item){
26513            item.enable();
26514         });
26515     },
26516     buildToolbar: function(tlist, nm)
26517     {
26518         var editor = this.editor;
26519          // create a new element.
26520         var wdiv = editor.wrap.createChild({
26521                 tag: 'div'
26522             }, editor.wrap.dom.firstChild.nextSibling, true);
26523         
26524        
26525         var tb = new Roo.Toolbar(wdiv);
26526         // add the name..
26527         
26528         tb.add(nm+ ":&nbsp;");
26529         
26530         // styles...
26531         if (this.styles) {
26532             
26533             // this needs a multi-select checkbox...
26534             tb.addField( new Roo.form.ComboBox({
26535                 store: new Roo.data.SimpleStore({
26536                     id : 'val',
26537                     fields: ['val', 'selected'],
26538                     data : [] 
26539                 }),
26540                 name : 'className',
26541                 displayField:'val',
26542                 typeAhead: false,
26543                 mode: 'local',
26544                 editable : false,
26545                 triggerAction: 'all',
26546                 emptyText:'Select Style',
26547                 selectOnFocus:true,
26548                 width: 130,
26549                 listeners : {
26550                     'select': function(c, r, i) {
26551                         // initial support only for on class per el..
26552                         tb.selectedNode.className =  r ? r.get('val') : '';
26553                     }
26554                 }
26555     
26556             }));
26557         }
26558             
26559         
26560         
26561         for (var i in tlist) {
26562             
26563             var item = tlist[i];
26564             tb.add(item.title + ":&nbsp;");
26565             
26566             
26567             
26568             
26569             if (item.opts) {
26570                 // opts == pulldown..
26571                 tb.addField( new Roo.form.ComboBox({
26572                     store: new Roo.data.SimpleStore({
26573                         id : 'val',
26574                         fields: ['val'],
26575                         data : item.opts  
26576                     }),
26577                     name : i,
26578                     displayField:'val',
26579                     typeAhead: false,
26580                     mode: 'local',
26581                     editable : false,
26582                     triggerAction: 'all',
26583                     emptyText:'Select',
26584                     selectOnFocus:true,
26585                     width: item.width ? item.width  : 130,
26586                     listeners : {
26587                         'select': function(c, r, i) {
26588                             tb.selectedNode.setAttribute(c.name, r.get('val'));
26589                         }
26590                     }
26591
26592                 }));
26593                 continue;
26594                     
26595                  
26596                 
26597                 tb.addField( new Roo.form.TextField({
26598                     name: i,
26599                     width: 100,
26600                     //allowBlank:false,
26601                     value: ''
26602                 }));
26603                 continue;
26604             }
26605             tb.addField( new Roo.form.TextField({
26606                 name: i,
26607                 width: item.width,
26608                 //allowBlank:true,
26609                 value: '',
26610                 listeners: {
26611                     'change' : function(f, nv, ov) {
26612                         tb.selectedNode.setAttribute(f.name, nv);
26613                     }
26614                 }
26615             }));
26616              
26617         }
26618         tb.el.on('click', function(e){
26619             e.preventDefault(); // what does this do?
26620         });
26621         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
26622         tb.el.hide();
26623         tb.name = nm;
26624         // dont need to disable them... as they will get hidden
26625         return tb;
26626          
26627         
26628     },
26629     buildFooter : function()
26630     {
26631         
26632         var fel = this.editor.wrap.createChild();
26633         this.footer = new Roo.Toolbar(fel);
26634         // toolbar has scrolly on left / right?
26635         var footDisp= new Roo.Toolbar.Fill();
26636         var _t = this;
26637         this.footer.add(
26638             {
26639                 text : '&lt;',
26640                 xtype: 'Button',
26641                 handler : function() {
26642                     _t.footDisp.scrollTo('left',0,true)
26643                 }
26644             }
26645         );
26646         this.footer.add( footDisp );
26647         this.footer.add( 
26648             {
26649                 text : '&gt;',
26650                 xtype: 'Button',
26651                 handler : function() {
26652                     // no animation..
26653                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
26654                 }
26655             }
26656         );
26657         var fel = Roo.get(footDisp.el);
26658         fel.addClass('x-editor-context');
26659         this.footDispWrap = fel; 
26660         this.footDispWrap.overflow  = 'hidden';
26661         
26662         this.footDisp = fel.createChild();
26663         this.footDispWrap.on('click', this.onContextClick, this)
26664         
26665         
26666     },
26667     onContextClick : function (ev,dom)
26668     {
26669         ev.preventDefault();
26670         var  cn = dom.className;
26671         Roo.log(cn);
26672         if (!cn.match(/x-ed-loc-/)) {
26673             return;
26674         }
26675         var n = cn.split('-').pop();
26676         var ans = this.footerEls;
26677         var sel = ans[n];
26678         
26679          // pick
26680         var range = this.editor.createRange();
26681         
26682         range.selectNodeContents(sel);
26683         //range.selectNode(sel);
26684         
26685         
26686         var selection = this.editor.getSelection();
26687         selection.removeAllRanges();
26688         selection.addRange(range);
26689         
26690         
26691         
26692         this.updateToolbar(null, null, sel);
26693         
26694         
26695     }
26696     
26697     
26698     
26699     
26700     
26701 });
26702
26703
26704
26705
26706
26707 /*
26708  * Based on:
26709  * Ext JS Library 1.1.1
26710  * Copyright(c) 2006-2007, Ext JS, LLC.
26711  *
26712  * Originally Released Under LGPL - original licence link has changed is not relivant.
26713  *
26714  * Fork - LGPL
26715  * <script type="text/javascript">
26716  */
26717  
26718 /**
26719  * @class Roo.form.BasicForm
26720  * @extends Roo.util.Observable
26721  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26722  * @constructor
26723  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26724  * @param {Object} config Configuration options
26725  */
26726 Roo.form.BasicForm = function(el, config){
26727     this.allItems = [];
26728     this.childForms = [];
26729     Roo.apply(this, config);
26730     /*
26731      * The Roo.form.Field items in this form.
26732      * @type MixedCollection
26733      */
26734      
26735      
26736     this.items = new Roo.util.MixedCollection(false, function(o){
26737         return o.id || (o.id = Roo.id());
26738     });
26739     this.addEvents({
26740         /**
26741          * @event beforeaction
26742          * Fires before any action is performed. Return false to cancel the action.
26743          * @param {Form} this
26744          * @param {Action} action The action to be performed
26745          */
26746         beforeaction: true,
26747         /**
26748          * @event actionfailed
26749          * Fires when an action fails.
26750          * @param {Form} this
26751          * @param {Action} action The action that failed
26752          */
26753         actionfailed : true,
26754         /**
26755          * @event actioncomplete
26756          * Fires when an action is completed.
26757          * @param {Form} this
26758          * @param {Action} action The action that completed
26759          */
26760         actioncomplete : true
26761     });
26762     if(el){
26763         this.initEl(el);
26764     }
26765     Roo.form.BasicForm.superclass.constructor.call(this);
26766 };
26767
26768 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26769     /**
26770      * @cfg {String} method
26771      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26772      */
26773     /**
26774      * @cfg {DataReader} reader
26775      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26776      * This is optional as there is built-in support for processing JSON.
26777      */
26778     /**
26779      * @cfg {DataReader} errorReader
26780      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26781      * This is completely optional as there is built-in support for processing JSON.
26782      */
26783     /**
26784      * @cfg {String} url
26785      * The URL to use for form actions if one isn't supplied in the action options.
26786      */
26787     /**
26788      * @cfg {Boolean} fileUpload
26789      * Set to true if this form is a file upload.
26790      */
26791      
26792     /**
26793      * @cfg {Object} baseParams
26794      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26795      */
26796      /**
26797      
26798     /**
26799      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26800      */
26801     timeout: 30,
26802
26803     // private
26804     activeAction : null,
26805
26806     /**
26807      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26808      * or setValues() data instead of when the form was first created.
26809      */
26810     trackResetOnLoad : false,
26811     
26812     
26813     /**
26814      * childForms - used for multi-tab forms
26815      * @type {Array}
26816      */
26817     childForms : false,
26818     
26819     /**
26820      * allItems - full list of fields.
26821      * @type {Array}
26822      */
26823     allItems : false,
26824     
26825     /**
26826      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26827      * element by passing it or its id or mask the form itself by passing in true.
26828      * @type Mixed
26829      */
26830     waitMsgTarget : false,
26831
26832     // private
26833     initEl : function(el){
26834         this.el = Roo.get(el);
26835         this.id = this.el.id || Roo.id();
26836         this.el.on('submit', this.onSubmit, this);
26837         this.el.addClass('x-form');
26838     },
26839
26840     // private
26841     onSubmit : function(e){
26842         e.stopEvent();
26843     },
26844
26845     /**
26846      * Returns true if client-side validation on the form is successful.
26847      * @return Boolean
26848      */
26849     isValid : function(){
26850         var valid = true;
26851         this.items.each(function(f){
26852            if(!f.validate()){
26853                valid = false;
26854            }
26855         });
26856         return valid;
26857     },
26858
26859     /**
26860      * Returns true if any fields in this form have changed since their original load.
26861      * @return Boolean
26862      */
26863     isDirty : function(){
26864         var dirty = false;
26865         this.items.each(function(f){
26866            if(f.isDirty()){
26867                dirty = true;
26868                return false;
26869            }
26870         });
26871         return dirty;
26872     },
26873
26874     /**
26875      * Performs a predefined action (submit or load) or custom actions you define on this form.
26876      * @param {String} actionName The name of the action type
26877      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26878      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26879      * accept other config options):
26880      * <pre>
26881 Property          Type             Description
26882 ----------------  ---------------  ----------------------------------------------------------------------------------
26883 url               String           The url for the action (defaults to the form's url)
26884 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26885 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26886 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26887                                    validate the form on the client (defaults to false)
26888      * </pre>
26889      * @return {BasicForm} this
26890      */
26891     doAction : function(action, options){
26892         if(typeof action == 'string'){
26893             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26894         }
26895         if(this.fireEvent('beforeaction', this, action) !== false){
26896             this.beforeAction(action);
26897             action.run.defer(100, action);
26898         }
26899         return this;
26900     },
26901
26902     /**
26903      * Shortcut to do a submit action.
26904      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26905      * @return {BasicForm} this
26906      */
26907     submit : function(options){
26908         this.doAction('submit', options);
26909         return this;
26910     },
26911
26912     /**
26913      * Shortcut to do a load action.
26914      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26915      * @return {BasicForm} this
26916      */
26917     load : function(options){
26918         this.doAction('load', options);
26919         return this;
26920     },
26921
26922     /**
26923      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26924      * @param {Record} record The record to edit
26925      * @return {BasicForm} this
26926      */
26927     updateRecord : function(record){
26928         record.beginEdit();
26929         var fs = record.fields;
26930         fs.each(function(f){
26931             var field = this.findField(f.name);
26932             if(field){
26933                 record.set(f.name, field.getValue());
26934             }
26935         }, this);
26936         record.endEdit();
26937         return this;
26938     },
26939
26940     /**
26941      * Loads an Roo.data.Record into this form.
26942      * @param {Record} record The record to load
26943      * @return {BasicForm} this
26944      */
26945     loadRecord : function(record){
26946         this.setValues(record.data);
26947         return this;
26948     },
26949
26950     // private
26951     beforeAction : function(action){
26952         var o = action.options;
26953         
26954        
26955         if(this.waitMsgTarget === true){
26956             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26957         }else if(this.waitMsgTarget){
26958             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26959             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26960         }else {
26961             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26962         }
26963          
26964     },
26965
26966     // private
26967     afterAction : function(action, success){
26968         this.activeAction = null;
26969         var o = action.options;
26970         
26971         if(this.waitMsgTarget === true){
26972             this.el.unmask();
26973         }else if(this.waitMsgTarget){
26974             this.waitMsgTarget.unmask();
26975         }else{
26976             Roo.MessageBox.updateProgress(1);
26977             Roo.MessageBox.hide();
26978         }
26979          
26980         if(success){
26981             if(o.reset){
26982                 this.reset();
26983             }
26984             Roo.callback(o.success, o.scope, [this, action]);
26985             this.fireEvent('actioncomplete', this, action);
26986             
26987         }else{
26988             
26989             // failure condition..
26990             // we have a scenario where updates need confirming.
26991             // eg. if a locking scenario exists..
26992             // we look for { errors : { needs_confirm : true }} in the response.
26993             if (typeof(action.result.errors.needs_confirm) != 'undefined') {
26994                 var _t = this;
26995                 Roo.MessageBox.confirm(
26996                     "Change requires confirmation",
26997                     action.result.errorMsg,
26998                     function(r) {
26999                         if (r != 'yes') {
27000                             return;
27001                         }
27002                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27003                     }
27004                     
27005                 );
27006                 
27007                 
27008                 
27009                 return;
27010             }
27011             
27012             Roo.callback(o.failure, o.scope, [this, action]);
27013             // show an error message if no failed handler is set..
27014             if (!this.hasListener('actionfailed')) {
27015                 Roo.MessageBox.alert("Error",
27016                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27017                         action.result.errorMsg :
27018                         "Saving Failed, please check your entries"
27019                 );
27020             }
27021             
27022             this.fireEvent('actionfailed', this, action);
27023         }
27024         
27025     },
27026
27027     /**
27028      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27029      * @param {String} id The value to search for
27030      * @return Field
27031      */
27032     findField : function(id){
27033         var field = this.items.get(id);
27034         if(!field){
27035             this.items.each(function(f){
27036                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27037                     field = f;
27038                     return false;
27039                 }
27040             });
27041         }
27042         return field || null;
27043     },
27044
27045     /**
27046      * Add a secondary form to this one, 
27047      * Used to provide tabbed forms. One form is primary, with hidden values 
27048      * which mirror the elements from the other forms.
27049      * 
27050      * @param {Roo.form.Form} form to add.
27051      * 
27052      */
27053     addForm : function(form)
27054     {
27055        
27056         if (this.childForms.indexOf(form) > -1) {
27057             // already added..
27058             return;
27059         }
27060         this.childForms.push(form);
27061         var n = '';
27062         Roo.each(form.allItems, function (fe) {
27063             
27064             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27065             if (this.findField(n)) { // already added..
27066                 return;
27067             }
27068             var add = new Roo.form.Hidden({
27069                 name : n
27070             });
27071             add.render(this.el);
27072             
27073             this.add( add );
27074         }, this);
27075         
27076     },
27077     /**
27078      * Mark fields in this form invalid in bulk.
27079      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27080      * @return {BasicForm} this
27081      */
27082     markInvalid : function(errors){
27083         if(errors instanceof Array){
27084             for(var i = 0, len = errors.length; i < len; i++){
27085                 var fieldError = errors[i];
27086                 var f = this.findField(fieldError.id);
27087                 if(f){
27088                     f.markInvalid(fieldError.msg);
27089                 }
27090             }
27091         }else{
27092             var field, id;
27093             for(id in errors){
27094                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27095                     field.markInvalid(errors[id]);
27096                 }
27097             }
27098         }
27099         Roo.each(this.childForms || [], function (f) {
27100             f.markInvalid(errors);
27101         });
27102         
27103         return this;
27104     },
27105
27106     /**
27107      * Set values for fields in this form in bulk.
27108      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27109      * @return {BasicForm} this
27110      */
27111     setValues : function(values){
27112         if(values instanceof Array){ // array of objects
27113             for(var i = 0, len = values.length; i < len; i++){
27114                 var v = values[i];
27115                 var f = this.findField(v.id);
27116                 if(f){
27117                     f.setValue(v.value);
27118                     if(this.trackResetOnLoad){
27119                         f.originalValue = f.getValue();
27120                     }
27121                 }
27122             }
27123         }else{ // object hash
27124             var field, id;
27125             for(id in values){
27126                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27127                     
27128                     if (field.setFromData && 
27129                         field.valueField && 
27130                         field.displayField &&
27131                         // combos' with local stores can 
27132                         // be queried via setValue()
27133                         // to set their value..
27134                         (field.store && !field.store.isLocal)
27135                         ) {
27136                         // it's a combo
27137                         var sd = { };
27138                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27139                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27140                         field.setFromData(sd);
27141                         
27142                     } else {
27143                         field.setValue(values[id]);
27144                     }
27145                     
27146                     
27147                     if(this.trackResetOnLoad){
27148                         field.originalValue = field.getValue();
27149                     }
27150                 }
27151             }
27152         }
27153          
27154         Roo.each(this.childForms || [], function (f) {
27155             f.setValues(values);
27156         });
27157                 
27158         return this;
27159     },
27160
27161     /**
27162      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27163      * they are returned as an array.
27164      * @param {Boolean} asString
27165      * @return {Object}
27166      */
27167     getValues : function(asString){
27168         if (this.childForms) {
27169             // copy values from the child forms
27170             Roo.each(this.childForms, function (f) {
27171                 this.setValues(f.getValues());
27172             }, this);
27173         }
27174         
27175         
27176         
27177         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27178         if(asString === true){
27179             return fs;
27180         }
27181         return Roo.urlDecode(fs);
27182     },
27183     
27184     /**
27185      * Returns the fields in this form as an object with key/value pairs. 
27186      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27187      * @return {Object}
27188      */
27189     getFieldValues : function(with_hidden)
27190     {
27191         if (this.childForms) {
27192             // copy values from the child forms
27193             // should this call getFieldValues - probably not as we do not currently copy
27194             // hidden fields when we generate..
27195             Roo.each(this.childForms, function (f) {
27196                 this.setValues(f.getValues());
27197             }, this);
27198         }
27199         
27200         var ret = {};
27201         this.items.each(function(f){
27202             if (!f.getName()) {
27203                 return;
27204             }
27205             var v = f.getValue();
27206             // not sure if this supported any more..
27207             if ((typeof(v) == 'object') && f.getRawValue) {
27208                 v = f.getRawValue() ; // dates..
27209             }
27210             // combo boxes where name != hiddenName...
27211             if (f.name != f.getName()) {
27212                 ret[f.name] = f.getRawValue();
27213             }
27214             ret[f.getName()] = v;
27215         });
27216         
27217         return ret;
27218     },
27219
27220     /**
27221      * Clears all invalid messages in this form.
27222      * @return {BasicForm} this
27223      */
27224     clearInvalid : function(){
27225         this.items.each(function(f){
27226            f.clearInvalid();
27227         });
27228         
27229         Roo.each(this.childForms || [], function (f) {
27230             f.clearInvalid();
27231         });
27232         
27233         
27234         return this;
27235     },
27236
27237     /**
27238      * Resets this form.
27239      * @return {BasicForm} this
27240      */
27241     reset : function(){
27242         this.items.each(function(f){
27243             f.reset();
27244         });
27245         
27246         Roo.each(this.childForms || [], function (f) {
27247             f.reset();
27248         });
27249        
27250         
27251         return this;
27252     },
27253
27254     /**
27255      * Add Roo.form components to this form.
27256      * @param {Field} field1
27257      * @param {Field} field2 (optional)
27258      * @param {Field} etc (optional)
27259      * @return {BasicForm} this
27260      */
27261     add : function(){
27262         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27263         return this;
27264     },
27265
27266
27267     /**
27268      * Removes a field from the items collection (does NOT remove its markup).
27269      * @param {Field} field
27270      * @return {BasicForm} this
27271      */
27272     remove : function(field){
27273         this.items.remove(field);
27274         return this;
27275     },
27276
27277     /**
27278      * Looks at the fields in this form, checks them for an id attribute,
27279      * and calls applyTo on the existing dom element with that id.
27280      * @return {BasicForm} this
27281      */
27282     render : function(){
27283         this.items.each(function(f){
27284             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27285                 f.applyTo(f.id);
27286             }
27287         });
27288         return this;
27289     },
27290
27291     /**
27292      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27293      * @param {Object} values
27294      * @return {BasicForm} this
27295      */
27296     applyToFields : function(o){
27297         this.items.each(function(f){
27298            Roo.apply(f, o);
27299         });
27300         return this;
27301     },
27302
27303     /**
27304      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27305      * @param {Object} values
27306      * @return {BasicForm} this
27307      */
27308     applyIfToFields : function(o){
27309         this.items.each(function(f){
27310            Roo.applyIf(f, o);
27311         });
27312         return this;
27313     }
27314 });
27315
27316 // back compat
27317 Roo.BasicForm = Roo.form.BasicForm;/*
27318  * Based on:
27319  * Ext JS Library 1.1.1
27320  * Copyright(c) 2006-2007, Ext JS, LLC.
27321  *
27322  * Originally Released Under LGPL - original licence link has changed is not relivant.
27323  *
27324  * Fork - LGPL
27325  * <script type="text/javascript">
27326  */
27327
27328 /**
27329  * @class Roo.form.Form
27330  * @extends Roo.form.BasicForm
27331  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27332  * @constructor
27333  * @param {Object} config Configuration options
27334  */
27335 Roo.form.Form = function(config){
27336     var xitems =  [];
27337     if (config.items) {
27338         xitems = config.items;
27339         delete config.items;
27340     }
27341    
27342     
27343     Roo.form.Form.superclass.constructor.call(this, null, config);
27344     this.url = this.url || this.action;
27345     if(!this.root){
27346         this.root = new Roo.form.Layout(Roo.applyIf({
27347             id: Roo.id()
27348         }, config));
27349     }
27350     this.active = this.root;
27351     /**
27352      * Array of all the buttons that have been added to this form via {@link addButton}
27353      * @type Array
27354      */
27355     this.buttons = [];
27356     this.allItems = [];
27357     this.addEvents({
27358         /**
27359          * @event clientvalidation
27360          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27361          * @param {Form} this
27362          * @param {Boolean} valid true if the form has passed client-side validation
27363          */
27364         clientvalidation: true,
27365         /**
27366          * @event rendered
27367          * Fires when the form is rendered
27368          * @param {Roo.form.Form} form
27369          */
27370         rendered : true
27371     });
27372     
27373     if (this.progressUrl) {
27374             // push a hidden field onto the list of fields..
27375             this.addxtype( {
27376                     xns: Roo.form, 
27377                     xtype : 'Hidden', 
27378                     name : 'UPLOAD_IDENTIFIER' 
27379             });
27380         }
27381         
27382     
27383     Roo.each(xitems, this.addxtype, this);
27384     
27385     
27386     
27387 };
27388
27389 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27390     /**
27391      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27392      */
27393     /**
27394      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27395      */
27396     /**
27397      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27398      */
27399     buttonAlign:'center',
27400
27401     /**
27402      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27403      */
27404     minButtonWidth:75,
27405
27406     /**
27407      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27408      * This property cascades to child containers if not set.
27409      */
27410     labelAlign:'left',
27411
27412     /**
27413      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27414      * fires a looping event with that state. This is required to bind buttons to the valid
27415      * state using the config value formBind:true on the button.
27416      */
27417     monitorValid : false,
27418
27419     /**
27420      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27421      */
27422     monitorPoll : 200,
27423     
27424     /**
27425      * @cfg {String} progressUrl - Url to return progress data 
27426      */
27427     
27428     progressUrl : false,
27429   
27430     /**
27431      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27432      * fields are added and the column is closed. If no fields are passed the column remains open
27433      * until end() is called.
27434      * @param {Object} config The config to pass to the column
27435      * @param {Field} field1 (optional)
27436      * @param {Field} field2 (optional)
27437      * @param {Field} etc (optional)
27438      * @return Column The column container object
27439      */
27440     column : function(c){
27441         var col = new Roo.form.Column(c);
27442         this.start(col);
27443         if(arguments.length > 1){ // duplicate code required because of Opera
27444             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27445             this.end();
27446         }
27447         return col;
27448     },
27449
27450     /**
27451      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27452      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27453      * until end() is called.
27454      * @param {Object} config The config to pass to the fieldset
27455      * @param {Field} field1 (optional)
27456      * @param {Field} field2 (optional)
27457      * @param {Field} etc (optional)
27458      * @return FieldSet The fieldset container object
27459      */
27460     fieldset : function(c){
27461         var fs = new Roo.form.FieldSet(c);
27462         this.start(fs);
27463         if(arguments.length > 1){ // duplicate code required because of Opera
27464             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27465             this.end();
27466         }
27467         return fs;
27468     },
27469
27470     /**
27471      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27472      * fields are added and the container is closed. If no fields are passed the container remains open
27473      * until end() is called.
27474      * @param {Object} config The config to pass to the Layout
27475      * @param {Field} field1 (optional)
27476      * @param {Field} field2 (optional)
27477      * @param {Field} etc (optional)
27478      * @return Layout The container object
27479      */
27480     container : function(c){
27481         var l = new Roo.form.Layout(c);
27482         this.start(l);
27483         if(arguments.length > 1){ // duplicate code required because of Opera
27484             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27485             this.end();
27486         }
27487         return l;
27488     },
27489
27490     /**
27491      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27492      * @param {Object} container A Roo.form.Layout or subclass of Layout
27493      * @return {Form} this
27494      */
27495     start : function(c){
27496         // cascade label info
27497         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27498         this.active.stack.push(c);
27499         c.ownerCt = this.active;
27500         this.active = c;
27501         return this;
27502     },
27503
27504     /**
27505      * Closes the current open container
27506      * @return {Form} this
27507      */
27508     end : function(){
27509         if(this.active == this.root){
27510             return this;
27511         }
27512         this.active = this.active.ownerCt;
27513         return this;
27514     },
27515
27516     /**
27517      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27518      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27519      * as the label of the field.
27520      * @param {Field} field1
27521      * @param {Field} field2 (optional)
27522      * @param {Field} etc. (optional)
27523      * @return {Form} this
27524      */
27525     add : function(){
27526         this.active.stack.push.apply(this.active.stack, arguments);
27527         this.allItems.push.apply(this.allItems,arguments);
27528         var r = [];
27529         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27530             if(a[i].isFormField){
27531                 r.push(a[i]);
27532             }
27533         }
27534         if(r.length > 0){
27535             Roo.form.Form.superclass.add.apply(this, r);
27536         }
27537         return this;
27538     },
27539     
27540
27541     
27542     
27543     
27544      /**
27545      * Find any element that has been added to a form, using it's ID or name
27546      * This can include framesets, columns etc. along with regular fields..
27547      * @param {String} id - id or name to find.
27548      
27549      * @return {Element} e - or false if nothing found.
27550      */
27551     findbyId : function(id)
27552     {
27553         var ret = false;
27554         if (!id) {
27555             return ret;
27556         }
27557         Roo.each(this.allItems, function(f){
27558             if (f.id == id || f.name == id ){
27559                 ret = f;
27560                 return false;
27561             }
27562         });
27563         return ret;
27564     },
27565
27566     
27567     
27568     /**
27569      * Render this form into the passed container. This should only be called once!
27570      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27571      * @return {Form} this
27572      */
27573     render : function(ct)
27574     {
27575         
27576         
27577         
27578         ct = Roo.get(ct);
27579         var o = this.autoCreate || {
27580             tag: 'form',
27581             method : this.method || 'POST',
27582             id : this.id || Roo.id()
27583         };
27584         this.initEl(ct.createChild(o));
27585
27586         this.root.render(this.el);
27587         
27588        
27589              
27590         this.items.each(function(f){
27591             f.render('x-form-el-'+f.id);
27592         });
27593
27594         if(this.buttons.length > 0){
27595             // tables are required to maintain order and for correct IE layout
27596             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27597                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27598                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27599             }}, null, true);
27600             var tr = tb.getElementsByTagName('tr')[0];
27601             for(var i = 0, len = this.buttons.length; i < len; i++) {
27602                 var b = this.buttons[i];
27603                 var td = document.createElement('td');
27604                 td.className = 'x-form-btn-td';
27605                 b.render(tr.appendChild(td));
27606             }
27607         }
27608         if(this.monitorValid){ // initialize after render
27609             this.startMonitoring();
27610         }
27611         this.fireEvent('rendered', this);
27612         return this;
27613     },
27614
27615     /**
27616      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27617      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27618      * object or a valid Roo.DomHelper element config
27619      * @param {Function} handler The function called when the button is clicked
27620      * @param {Object} scope (optional) The scope of the handler function
27621      * @return {Roo.Button}
27622      */
27623     addButton : function(config, handler, scope){
27624         var bc = {
27625             handler: handler,
27626             scope: scope,
27627             minWidth: this.minButtonWidth,
27628             hideParent:true
27629         };
27630         if(typeof config == "string"){
27631             bc.text = config;
27632         }else{
27633             Roo.apply(bc, config);
27634         }
27635         var btn = new Roo.Button(null, bc);
27636         this.buttons.push(btn);
27637         return btn;
27638     },
27639
27640      /**
27641      * Adds a series of form elements (using the xtype property as the factory method.
27642      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27643      * @param {Object} config 
27644      */
27645     
27646     addxtype : function()
27647     {
27648         var ar = Array.prototype.slice.call(arguments, 0);
27649         var ret = false;
27650         for(var i = 0; i < ar.length; i++) {
27651             if (!ar[i]) {
27652                 continue; // skip -- if this happends something invalid got sent, we 
27653                 // should ignore it, as basically that interface element will not show up
27654                 // and that should be pretty obvious!!
27655             }
27656             
27657             if (Roo.form[ar[i].xtype]) {
27658                 ar[i].form = this;
27659                 var fe = Roo.factory(ar[i], Roo.form);
27660                 if (!ret) {
27661                     ret = fe;
27662                 }
27663                 fe.form = this;
27664                 if (fe.store) {
27665                     fe.store.form = this;
27666                 }
27667                 if (fe.isLayout) {  
27668                          
27669                     this.start(fe);
27670                     this.allItems.push(fe);
27671                     if (fe.items && fe.addxtype) {
27672                         fe.addxtype.apply(fe, fe.items);
27673                         delete fe.items;
27674                     }
27675                      this.end();
27676                     continue;
27677                 }
27678                 
27679                 
27680                  
27681                 this.add(fe);
27682               //  console.log('adding ' + ar[i].xtype);
27683             }
27684             if (ar[i].xtype == 'Button') {  
27685                 //console.log('adding button');
27686                 //console.log(ar[i]);
27687                 this.addButton(ar[i]);
27688                 this.allItems.push(fe);
27689                 continue;
27690             }
27691             
27692             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27693                 alert('end is not supported on xtype any more, use items');
27694             //    this.end();
27695             //    //console.log('adding end');
27696             }
27697             
27698         }
27699         return ret;
27700     },
27701     
27702     /**
27703      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27704      * option "monitorValid"
27705      */
27706     startMonitoring : function(){
27707         if(!this.bound){
27708             this.bound = true;
27709             Roo.TaskMgr.start({
27710                 run : this.bindHandler,
27711                 interval : this.monitorPoll || 200,
27712                 scope: this
27713             });
27714         }
27715     },
27716
27717     /**
27718      * Stops monitoring of the valid state of this form
27719      */
27720     stopMonitoring : function(){
27721         this.bound = false;
27722     },
27723
27724     // private
27725     bindHandler : function(){
27726         if(!this.bound){
27727             return false; // stops binding
27728         }
27729         var valid = true;
27730         this.items.each(function(f){
27731             if(!f.isValid(true)){
27732                 valid = false;
27733                 return false;
27734             }
27735         });
27736         for(var i = 0, len = this.buttons.length; i < len; i++){
27737             var btn = this.buttons[i];
27738             if(btn.formBind === true && btn.disabled === valid){
27739                 btn.setDisabled(!valid);
27740             }
27741         }
27742         this.fireEvent('clientvalidation', this, valid);
27743     }
27744     
27745     
27746     
27747     
27748     
27749     
27750     
27751     
27752 });
27753
27754
27755 // back compat
27756 Roo.Form = Roo.form.Form;
27757 /*
27758  * Based on:
27759  * Ext JS Library 1.1.1
27760  * Copyright(c) 2006-2007, Ext JS, LLC.
27761  *
27762  * Originally Released Under LGPL - original licence link has changed is not relivant.
27763  *
27764  * Fork - LGPL
27765  * <script type="text/javascript">
27766  */
27767  
27768  /**
27769  * @class Roo.form.Action
27770  * Internal Class used to handle form actions
27771  * @constructor
27772  * @param {Roo.form.BasicForm} el The form element or its id
27773  * @param {Object} config Configuration options
27774  */
27775  
27776  
27777 // define the action interface
27778 Roo.form.Action = function(form, options){
27779     this.form = form;
27780     this.options = options || {};
27781 };
27782 /**
27783  * Client Validation Failed
27784  * @const 
27785  */
27786 Roo.form.Action.CLIENT_INVALID = 'client';
27787 /**
27788  * Server Validation Failed
27789  * @const 
27790  */
27791  Roo.form.Action.SERVER_INVALID = 'server';
27792  /**
27793  * Connect to Server Failed
27794  * @const 
27795  */
27796 Roo.form.Action.CONNECT_FAILURE = 'connect';
27797 /**
27798  * Reading Data from Server Failed
27799  * @const 
27800  */
27801 Roo.form.Action.LOAD_FAILURE = 'load';
27802
27803 Roo.form.Action.prototype = {
27804     type : 'default',
27805     failureType : undefined,
27806     response : undefined,
27807     result : undefined,
27808
27809     // interface method
27810     run : function(options){
27811
27812     },
27813
27814     // interface method
27815     success : function(response){
27816
27817     },
27818
27819     // interface method
27820     handleResponse : function(response){
27821
27822     },
27823
27824     // default connection failure
27825     failure : function(response){
27826         
27827         this.response = response;
27828         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27829         this.form.afterAction(this, false);
27830     },
27831
27832     processResponse : function(response){
27833         this.response = response;
27834         if(!response.responseText){
27835             return true;
27836         }
27837         this.result = this.handleResponse(response);
27838         return this.result;
27839     },
27840
27841     // utility functions used internally
27842     getUrl : function(appendParams){
27843         var url = this.options.url || this.form.url || this.form.el.dom.action;
27844         if(appendParams){
27845             var p = this.getParams();
27846             if(p){
27847                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27848             }
27849         }
27850         return url;
27851     },
27852
27853     getMethod : function(){
27854         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27855     },
27856
27857     getParams : function(){
27858         var bp = this.form.baseParams;
27859         var p = this.options.params;
27860         if(p){
27861             if(typeof p == "object"){
27862                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27863             }else if(typeof p == 'string' && bp){
27864                 p += '&' + Roo.urlEncode(bp);
27865             }
27866         }else if(bp){
27867             p = Roo.urlEncode(bp);
27868         }
27869         return p;
27870     },
27871
27872     createCallback : function(){
27873         return {
27874             success: this.success,
27875             failure: this.failure,
27876             scope: this,
27877             timeout: (this.form.timeout*1000),
27878             upload: this.form.fileUpload ? this.success : undefined
27879         };
27880     }
27881 };
27882
27883 Roo.form.Action.Submit = function(form, options){
27884     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27885 };
27886
27887 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27888     type : 'submit',
27889
27890     haveProgress : false,
27891     uploadComplete : false,
27892     
27893     // uploadProgress indicator.
27894     uploadProgress : function()
27895     {
27896         if (!this.form.progressUrl) {
27897             return;
27898         }
27899         
27900         if (!this.haveProgress) {
27901             Roo.MessageBox.progress("Uploading", "Uploading");
27902         }
27903         if (this.uploadComplete) {
27904            Roo.MessageBox.hide();
27905            return;
27906         }
27907         
27908         this.haveProgress = true;
27909    
27910         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27911         
27912         var c = new Roo.data.Connection();
27913         c.request({
27914             url : this.form.progressUrl,
27915             params: {
27916                 id : uid
27917             },
27918             method: 'GET',
27919             success : function(req){
27920                //console.log(data);
27921                 var rdata = false;
27922                 var edata;
27923                 try  {
27924                    rdata = Roo.decode(req.responseText)
27925                 } catch (e) {
27926                     Roo.log("Invalid data from server..");
27927                     Roo.log(edata);
27928                     return;
27929                 }
27930                 if (!rdata || !rdata.success) {
27931                     Roo.log(rdata);
27932                     return;
27933                 }
27934                 var data = rdata.data;
27935                 
27936                 if (this.uploadComplete) {
27937                    Roo.MessageBox.hide();
27938                    return;
27939                 }
27940                    
27941                 if (data){
27942                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27943                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27944                     );
27945                 }
27946                 this.uploadProgress.defer(2000,this);
27947             },
27948        
27949             failure: function(data) {
27950                 Roo.log('progress url failed ');
27951                 Roo.log(data);
27952             },
27953             scope : this
27954         });
27955            
27956     },
27957     
27958     
27959     run : function()
27960     {
27961         // run get Values on the form, so it syncs any secondary forms.
27962         this.form.getValues();
27963         
27964         var o = this.options;
27965         var method = this.getMethod();
27966         var isPost = method == 'POST';
27967         if(o.clientValidation === false || this.form.isValid()){
27968             
27969             if (this.form.progressUrl) {
27970                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27971                     (new Date() * 1) + '' + Math.random());
27972                     
27973             } 
27974             
27975             
27976             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27977                 form:this.form.el.dom,
27978                 url:this.getUrl(!isPost),
27979                 method: method,
27980                 params:isPost ? this.getParams() : null,
27981                 isUpload: this.form.fileUpload
27982             }));
27983             
27984             this.uploadProgress();
27985
27986         }else if (o.clientValidation !== false){ // client validation failed
27987             this.failureType = Roo.form.Action.CLIENT_INVALID;
27988             this.form.afterAction(this, false);
27989         }
27990     },
27991
27992     success : function(response)
27993     {
27994         this.uploadComplete= true;
27995         if (this.haveProgress) {
27996             Roo.MessageBox.hide();
27997         }
27998         
27999         
28000         var result = this.processResponse(response);
28001         if(result === true || result.success){
28002             this.form.afterAction(this, true);
28003             return;
28004         }
28005         if(result.errors){
28006             this.form.markInvalid(result.errors);
28007             this.failureType = Roo.form.Action.SERVER_INVALID;
28008         }
28009         this.form.afterAction(this, false);
28010     },
28011     failure : function(response)
28012     {
28013         this.uploadComplete= true;
28014         if (this.haveProgress) {
28015             Roo.MessageBox.hide();
28016         }
28017         
28018         this.response = response;
28019         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28020         this.form.afterAction(this, false);
28021     },
28022     
28023     handleResponse : function(response){
28024         if(this.form.errorReader){
28025             var rs = this.form.errorReader.read(response);
28026             var errors = [];
28027             if(rs.records){
28028                 for(var i = 0, len = rs.records.length; i < len; i++) {
28029                     var r = rs.records[i];
28030                     errors[i] = r.data;
28031                 }
28032             }
28033             if(errors.length < 1){
28034                 errors = null;
28035             }
28036             return {
28037                 success : rs.success,
28038                 errors : errors
28039             };
28040         }
28041         var ret = false;
28042         try {
28043             ret = Roo.decode(response.responseText);
28044         } catch (e) {
28045             ret = {
28046                 success: false,
28047                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28048                 errors : []
28049             };
28050         }
28051         return ret;
28052         
28053     }
28054 });
28055
28056
28057 Roo.form.Action.Load = function(form, options){
28058     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28059     this.reader = this.form.reader;
28060 };
28061
28062 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28063     type : 'load',
28064
28065     run : function(){
28066         
28067         Roo.Ajax.request(Roo.apply(
28068                 this.createCallback(), {
28069                     method:this.getMethod(),
28070                     url:this.getUrl(false),
28071                     params:this.getParams()
28072         }));
28073     },
28074
28075     success : function(response){
28076         
28077         var result = this.processResponse(response);
28078         if(result === true || !result.success || !result.data){
28079             this.failureType = Roo.form.Action.LOAD_FAILURE;
28080             this.form.afterAction(this, false);
28081             return;
28082         }
28083         this.form.clearInvalid();
28084         this.form.setValues(result.data);
28085         this.form.afterAction(this, true);
28086     },
28087
28088     handleResponse : function(response){
28089         if(this.form.reader){
28090             var rs = this.form.reader.read(response);
28091             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28092             return {
28093                 success : rs.success,
28094                 data : data
28095             };
28096         }
28097         return Roo.decode(response.responseText);
28098     }
28099 });
28100
28101 Roo.form.Action.ACTION_TYPES = {
28102     'load' : Roo.form.Action.Load,
28103     'submit' : Roo.form.Action.Submit
28104 };/*
28105  * Based on:
28106  * Ext JS Library 1.1.1
28107  * Copyright(c) 2006-2007, Ext JS, LLC.
28108  *
28109  * Originally Released Under LGPL - original licence link has changed is not relivant.
28110  *
28111  * Fork - LGPL
28112  * <script type="text/javascript">
28113  */
28114  
28115 /**
28116  * @class Roo.form.Layout
28117  * @extends Roo.Component
28118  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28119  * @constructor
28120  * @param {Object} config Configuration options
28121  */
28122 Roo.form.Layout = function(config){
28123     var xitems = [];
28124     if (config.items) {
28125         xitems = config.items;
28126         delete config.items;
28127     }
28128     Roo.form.Layout.superclass.constructor.call(this, config);
28129     this.stack = [];
28130     Roo.each(xitems, this.addxtype, this);
28131      
28132 };
28133
28134 Roo.extend(Roo.form.Layout, Roo.Component, {
28135     /**
28136      * @cfg {String/Object} autoCreate
28137      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28138      */
28139     /**
28140      * @cfg {String/Object/Function} style
28141      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28142      * a function which returns such a specification.
28143      */
28144     /**
28145      * @cfg {String} labelAlign
28146      * Valid values are "left," "top" and "right" (defaults to "left")
28147      */
28148     /**
28149      * @cfg {Number} labelWidth
28150      * Fixed width in pixels of all field labels (defaults to undefined)
28151      */
28152     /**
28153      * @cfg {Boolean} clear
28154      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28155      */
28156     clear : true,
28157     /**
28158      * @cfg {String} labelSeparator
28159      * The separator to use after field labels (defaults to ':')
28160      */
28161     labelSeparator : ':',
28162     /**
28163      * @cfg {Boolean} hideLabels
28164      * True to suppress the display of field labels in this layout (defaults to false)
28165      */
28166     hideLabels : false,
28167
28168     // private
28169     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28170     
28171     isLayout : true,
28172     
28173     // private
28174     onRender : function(ct, position){
28175         if(this.el){ // from markup
28176             this.el = Roo.get(this.el);
28177         }else {  // generate
28178             var cfg = this.getAutoCreate();
28179             this.el = ct.createChild(cfg, position);
28180         }
28181         if(this.style){
28182             this.el.applyStyles(this.style);
28183         }
28184         if(this.labelAlign){
28185             this.el.addClass('x-form-label-'+this.labelAlign);
28186         }
28187         if(this.hideLabels){
28188             this.labelStyle = "display:none";
28189             this.elementStyle = "padding-left:0;";
28190         }else{
28191             if(typeof this.labelWidth == 'number'){
28192                 this.labelStyle = "width:"+this.labelWidth+"px;";
28193                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28194             }
28195             if(this.labelAlign == 'top'){
28196                 this.labelStyle = "width:auto;";
28197                 this.elementStyle = "padding-left:0;";
28198             }
28199         }
28200         var stack = this.stack;
28201         var slen = stack.length;
28202         if(slen > 0){
28203             if(!this.fieldTpl){
28204                 var t = new Roo.Template(
28205                     '<div class="x-form-item {5}">',
28206                         '<label for="{0}" style="{2}">{1}{4}</label>',
28207                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28208                         '</div>',
28209                     '</div><div class="x-form-clear-left"></div>'
28210                 );
28211                 t.disableFormats = true;
28212                 t.compile();
28213                 Roo.form.Layout.prototype.fieldTpl = t;
28214             }
28215             for(var i = 0; i < slen; i++) {
28216                 if(stack[i].isFormField){
28217                     this.renderField(stack[i]);
28218                 }else{
28219                     this.renderComponent(stack[i]);
28220                 }
28221             }
28222         }
28223         if(this.clear){
28224             this.el.createChild({cls:'x-form-clear'});
28225         }
28226     },
28227
28228     // private
28229     renderField : function(f){
28230         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28231                f.id, //0
28232                f.fieldLabel, //1
28233                f.labelStyle||this.labelStyle||'', //2
28234                this.elementStyle||'', //3
28235                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28236                f.itemCls||this.itemCls||''  //5
28237        ], true).getPrevSibling());
28238     },
28239
28240     // private
28241     renderComponent : function(c){
28242         c.render(c.isLayout ? this.el : this.el.createChild());    
28243     },
28244     /**
28245      * Adds a object form elements (using the xtype property as the factory method.)
28246      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28247      * @param {Object} config 
28248      */
28249     addxtype : function(o)
28250     {
28251         // create the lement.
28252         o.form = this.form;
28253         var fe = Roo.factory(o, Roo.form);
28254         this.form.allItems.push(fe);
28255         this.stack.push(fe);
28256         
28257         if (fe.isFormField) {
28258             this.form.items.add(fe);
28259         }
28260          
28261         return fe;
28262     }
28263 });
28264
28265 /**
28266  * @class Roo.form.Column
28267  * @extends Roo.form.Layout
28268  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28269  * @constructor
28270  * @param {Object} config Configuration options
28271  */
28272 Roo.form.Column = function(config){
28273     Roo.form.Column.superclass.constructor.call(this, config);
28274 };
28275
28276 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28277     /**
28278      * @cfg {Number/String} width
28279      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28280      */
28281     /**
28282      * @cfg {String/Object} autoCreate
28283      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28284      */
28285
28286     // private
28287     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28288
28289     // private
28290     onRender : function(ct, position){
28291         Roo.form.Column.superclass.onRender.call(this, ct, position);
28292         if(this.width){
28293             this.el.setWidth(this.width);
28294         }
28295     }
28296 });
28297
28298
28299 /**
28300  * @class Roo.form.Row
28301  * @extends Roo.form.Layout
28302  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28303  * @constructor
28304  * @param {Object} config Configuration options
28305  */
28306
28307  
28308 Roo.form.Row = function(config){
28309     Roo.form.Row.superclass.constructor.call(this, config);
28310 };
28311  
28312 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28313       /**
28314      * @cfg {Number/String} width
28315      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28316      */
28317     /**
28318      * @cfg {Number/String} height
28319      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28320      */
28321     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28322     
28323     padWidth : 20,
28324     // private
28325     onRender : function(ct, position){
28326         //console.log('row render');
28327         if(!this.rowTpl){
28328             var t = new Roo.Template(
28329                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28330                     '<label for="{0}" style="{2}">{1}{4}</label>',
28331                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28332                     '</div>',
28333                 '</div>'
28334             );
28335             t.disableFormats = true;
28336             t.compile();
28337             Roo.form.Layout.prototype.rowTpl = t;
28338         }
28339         this.fieldTpl = this.rowTpl;
28340         
28341         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28342         var labelWidth = 100;
28343         
28344         if ((this.labelAlign != 'top')) {
28345             if (typeof this.labelWidth == 'number') {
28346                 labelWidth = this.labelWidth
28347             }
28348             this.padWidth =  20 + labelWidth;
28349             
28350         }
28351         
28352         Roo.form.Column.superclass.onRender.call(this, ct, position);
28353         if(this.width){
28354             this.el.setWidth(this.width);
28355         }
28356         if(this.height){
28357             this.el.setHeight(this.height);
28358         }
28359     },
28360     
28361     // private
28362     renderField : function(f){
28363         f.fieldEl = this.fieldTpl.append(this.el, [
28364                f.id, f.fieldLabel,
28365                f.labelStyle||this.labelStyle||'',
28366                this.elementStyle||'',
28367                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28368                f.itemCls||this.itemCls||'',
28369                f.width ? f.width + this.padWidth : 160 + this.padWidth
28370        ],true);
28371     }
28372 });
28373  
28374
28375 /**
28376  * @class Roo.form.FieldSet
28377  * @extends Roo.form.Layout
28378  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28379  * @constructor
28380  * @param {Object} config Configuration options
28381  */
28382 Roo.form.FieldSet = function(config){
28383     Roo.form.FieldSet.superclass.constructor.call(this, config);
28384 };
28385
28386 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28387     /**
28388      * @cfg {String} legend
28389      * The text to display as the legend for the FieldSet (defaults to '')
28390      */
28391     /**
28392      * @cfg {String/Object} autoCreate
28393      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28394      */
28395
28396     // private
28397     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28398
28399     // private
28400     onRender : function(ct, position){
28401         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28402         if(this.legend){
28403             this.setLegend(this.legend);
28404         }
28405     },
28406
28407     // private
28408     setLegend : function(text){
28409         if(this.rendered){
28410             this.el.child('legend').update(text);
28411         }
28412     }
28413 });/*
28414  * Based on:
28415  * Ext JS Library 1.1.1
28416  * Copyright(c) 2006-2007, Ext JS, LLC.
28417  *
28418  * Originally Released Under LGPL - original licence link has changed is not relivant.
28419  *
28420  * Fork - LGPL
28421  * <script type="text/javascript">
28422  */
28423 /**
28424  * @class Roo.form.VTypes
28425  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28426  * @singleton
28427  */
28428 Roo.form.VTypes = function(){
28429     // closure these in so they are only created once.
28430     var alpha = /^[a-zA-Z_]+$/;
28431     var alphanum = /^[a-zA-Z0-9_]+$/;
28432     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28433     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28434
28435     // All these messages and functions are configurable
28436     return {
28437         /**
28438          * The function used to validate email addresses
28439          * @param {String} value The email address
28440          */
28441         'email' : function(v){
28442             return email.test(v);
28443         },
28444         /**
28445          * The error text to display when the email validation function returns false
28446          * @type String
28447          */
28448         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28449         /**
28450          * The keystroke filter mask to be applied on email input
28451          * @type RegExp
28452          */
28453         'emailMask' : /[a-z0-9_\.\-@]/i,
28454
28455         /**
28456          * The function used to validate URLs
28457          * @param {String} value The URL
28458          */
28459         'url' : function(v){
28460             return url.test(v);
28461         },
28462         /**
28463          * The error text to display when the url validation function returns false
28464          * @type String
28465          */
28466         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28467         
28468         /**
28469          * The function used to validate alpha values
28470          * @param {String} value The value
28471          */
28472         'alpha' : function(v){
28473             return alpha.test(v);
28474         },
28475         /**
28476          * The error text to display when the alpha validation function returns false
28477          * @type String
28478          */
28479         'alphaText' : 'This field should only contain letters and _',
28480         /**
28481          * The keystroke filter mask to be applied on alpha input
28482          * @type RegExp
28483          */
28484         'alphaMask' : /[a-z_]/i,
28485
28486         /**
28487          * The function used to validate alphanumeric values
28488          * @param {String} value The value
28489          */
28490         'alphanum' : function(v){
28491             return alphanum.test(v);
28492         },
28493         /**
28494          * The error text to display when the alphanumeric validation function returns false
28495          * @type String
28496          */
28497         'alphanumText' : 'This field should only contain letters, numbers and _',
28498         /**
28499          * The keystroke filter mask to be applied on alphanumeric input
28500          * @type RegExp
28501          */
28502         'alphanumMask' : /[a-z0-9_]/i
28503     };
28504 }();//<script type="text/javascript">
28505
28506 /**
28507  * @class Roo.form.FCKeditor
28508  * @extends Roo.form.TextArea
28509  * Wrapper around the FCKEditor http://www.fckeditor.net
28510  * @constructor
28511  * Creates a new FCKeditor
28512  * @param {Object} config Configuration options
28513  */
28514 Roo.form.FCKeditor = function(config){
28515     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28516     this.addEvents({
28517          /**
28518          * @event editorinit
28519          * Fired when the editor is initialized - you can add extra handlers here..
28520          * @param {FCKeditor} this
28521          * @param {Object} the FCK object.
28522          */
28523         editorinit : true
28524     });
28525     
28526     
28527 };
28528 Roo.form.FCKeditor.editors = { };
28529 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28530 {
28531     //defaultAutoCreate : {
28532     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28533     //},
28534     // private
28535     /**
28536      * @cfg {Object} fck options - see fck manual for details.
28537      */
28538     fckconfig : false,
28539     
28540     /**
28541      * @cfg {Object} fck toolbar set (Basic or Default)
28542      */
28543     toolbarSet : 'Basic',
28544     /**
28545      * @cfg {Object} fck BasePath
28546      */ 
28547     basePath : '/fckeditor/',
28548     
28549     
28550     frame : false,
28551     
28552     value : '',
28553     
28554    
28555     onRender : function(ct, position)
28556     {
28557         if(!this.el){
28558             this.defaultAutoCreate = {
28559                 tag: "textarea",
28560                 style:"width:300px;height:60px;",
28561                 autocomplete: "off"
28562             };
28563         }
28564         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28565         /*
28566         if(this.grow){
28567             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28568             if(this.preventScrollbars){
28569                 this.el.setStyle("overflow", "hidden");
28570             }
28571             this.el.setHeight(this.growMin);
28572         }
28573         */
28574         //console.log('onrender' + this.getId() );
28575         Roo.form.FCKeditor.editors[this.getId()] = this;
28576          
28577
28578         this.replaceTextarea() ;
28579         
28580     },
28581     
28582     getEditor : function() {
28583         return this.fckEditor;
28584     },
28585     /**
28586      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28587      * @param {Mixed} value The value to set
28588      */
28589     
28590     
28591     setValue : function(value)
28592     {
28593         //console.log('setValue: ' + value);
28594         
28595         if(typeof(value) == 'undefined') { // not sure why this is happending...
28596             return;
28597         }
28598         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28599         
28600         //if(!this.el || !this.getEditor()) {
28601         //    this.value = value;
28602             //this.setValue.defer(100,this,[value]);    
28603         //    return;
28604         //} 
28605         
28606         if(!this.getEditor()) {
28607             return;
28608         }
28609         
28610         this.getEditor().SetData(value);
28611         
28612         //
28613
28614     },
28615
28616     /**
28617      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28618      * @return {Mixed} value The field value
28619      */
28620     getValue : function()
28621     {
28622         
28623         if (this.frame && this.frame.dom.style.display == 'none') {
28624             return Roo.form.FCKeditor.superclass.getValue.call(this);
28625         }
28626         
28627         if(!this.el || !this.getEditor()) {
28628            
28629            // this.getValue.defer(100,this); 
28630             return this.value;
28631         }
28632        
28633         
28634         var value=this.getEditor().GetData();
28635         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28636         return Roo.form.FCKeditor.superclass.getValue.call(this);
28637         
28638
28639     },
28640
28641     /**
28642      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28643      * @return {Mixed} value The field value
28644      */
28645     getRawValue : function()
28646     {
28647         if (this.frame && this.frame.dom.style.display == 'none') {
28648             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28649         }
28650         
28651         if(!this.el || !this.getEditor()) {
28652             //this.getRawValue.defer(100,this); 
28653             return this.value;
28654             return;
28655         }
28656         
28657         
28658         
28659         var value=this.getEditor().GetData();
28660         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28661         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28662          
28663     },
28664     
28665     setSize : function(w,h) {
28666         
28667         
28668         
28669         //if (this.frame && this.frame.dom.style.display == 'none') {
28670         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28671         //    return;
28672         //}
28673         //if(!this.el || !this.getEditor()) {
28674         //    this.setSize.defer(100,this, [w,h]); 
28675         //    return;
28676         //}
28677         
28678         
28679         
28680         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28681         
28682         this.frame.dom.setAttribute('width', w);
28683         this.frame.dom.setAttribute('height', h);
28684         this.frame.setSize(w,h);
28685         
28686     },
28687     
28688     toggleSourceEdit : function(value) {
28689         
28690       
28691          
28692         this.el.dom.style.display = value ? '' : 'none';
28693         this.frame.dom.style.display = value ?  'none' : '';
28694         
28695     },
28696     
28697     
28698     focus: function(tag)
28699     {
28700         if (this.frame.dom.style.display == 'none') {
28701             return Roo.form.FCKeditor.superclass.focus.call(this);
28702         }
28703         if(!this.el || !this.getEditor()) {
28704             this.focus.defer(100,this, [tag]); 
28705             return;
28706         }
28707         
28708         
28709         
28710         
28711         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28712         this.getEditor().Focus();
28713         if (tgs.length) {
28714             if (!this.getEditor().Selection.GetSelection()) {
28715                 this.focus.defer(100,this, [tag]); 
28716                 return;
28717             }
28718             
28719             
28720             var r = this.getEditor().EditorDocument.createRange();
28721             r.setStart(tgs[0],0);
28722             r.setEnd(tgs[0],0);
28723             this.getEditor().Selection.GetSelection().removeAllRanges();
28724             this.getEditor().Selection.GetSelection().addRange(r);
28725             this.getEditor().Focus();
28726         }
28727         
28728     },
28729     
28730     
28731     
28732     replaceTextarea : function()
28733     {
28734         if ( document.getElementById( this.getId() + '___Frame' ) )
28735             return ;
28736         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28737         //{
28738             // We must check the elements firstly using the Id and then the name.
28739         var oTextarea = document.getElementById( this.getId() );
28740         
28741         var colElementsByName = document.getElementsByName( this.getId() ) ;
28742          
28743         oTextarea.style.display = 'none' ;
28744
28745         if ( oTextarea.tabIndex ) {            
28746             this.TabIndex = oTextarea.tabIndex ;
28747         }
28748         
28749         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28750         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28751         this.frame = Roo.get(this.getId() + '___Frame')
28752     },
28753     
28754     _getConfigHtml : function()
28755     {
28756         var sConfig = '' ;
28757
28758         for ( var o in this.fckconfig ) {
28759             sConfig += sConfig.length > 0  ? '&amp;' : '';
28760             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28761         }
28762
28763         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28764     },
28765     
28766     
28767     _getIFrameHtml : function()
28768     {
28769         var sFile = 'fckeditor.html' ;
28770         /* no idea what this is about..
28771         try
28772         {
28773             if ( (/fcksource=true/i).test( window.top.location.search ) )
28774                 sFile = 'fckeditor.original.html' ;
28775         }
28776         catch (e) { 
28777         */
28778
28779         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28780         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28781         
28782         
28783         var html = '<iframe id="' + this.getId() +
28784             '___Frame" src="' + sLink +
28785             '" width="' + this.width +
28786             '" height="' + this.height + '"' +
28787             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28788             ' frameborder="0" scrolling="no"></iframe>' ;
28789
28790         return html ;
28791     },
28792     
28793     _insertHtmlBefore : function( html, element )
28794     {
28795         if ( element.insertAdjacentHTML )       {
28796             // IE
28797             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28798         } else { // Gecko
28799             var oRange = document.createRange() ;
28800             oRange.setStartBefore( element ) ;
28801             var oFragment = oRange.createContextualFragment( html );
28802             element.parentNode.insertBefore( oFragment, element ) ;
28803         }
28804     }
28805     
28806     
28807   
28808     
28809     
28810     
28811     
28812
28813 });
28814
28815 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28816
28817 function FCKeditor_OnComplete(editorInstance){
28818     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28819     f.fckEditor = editorInstance;
28820     //console.log("loaded");
28821     f.fireEvent('editorinit', f, editorInstance);
28822
28823   
28824
28825  
28826
28827
28828
28829
28830
28831
28832
28833
28834
28835
28836
28837
28838
28839
28840
28841 //<script type="text/javascript">
28842 /**
28843  * @class Roo.form.GridField
28844  * @extends Roo.form.Field
28845  * Embed a grid (or editable grid into a form)
28846  * STATUS ALPHA
28847  * 
28848  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28849  * it needs 
28850  * xgrid.store = Roo.data.Store
28851  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28852  * xgrid.store.reader = Roo.data.JsonReader 
28853  * 
28854  * 
28855  * @constructor
28856  * Creates a new GridField
28857  * @param {Object} config Configuration options
28858  */
28859 Roo.form.GridField = function(config){
28860     Roo.form.GridField.superclass.constructor.call(this, config);
28861      
28862 };
28863
28864 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28865     /**
28866      * @cfg {Number} width  - used to restrict width of grid..
28867      */
28868     width : 100,
28869     /**
28870      * @cfg {Number} height - used to restrict height of grid..
28871      */
28872     height : 50,
28873      /**
28874      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28875          * 
28876          *}
28877      */
28878     xgrid : false, 
28879     /**
28880      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28881      * {tag: "input", type: "checkbox", autocomplete: "off"})
28882      */
28883    // defaultAutoCreate : { tag: 'div' },
28884     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28885     /**
28886      * @cfg {String} addTitle Text to include for adding a title.
28887      */
28888     addTitle : false,
28889     //
28890     onResize : function(){
28891         Roo.form.Field.superclass.onResize.apply(this, arguments);
28892     },
28893
28894     initEvents : function(){
28895         // Roo.form.Checkbox.superclass.initEvents.call(this);
28896         // has no events...
28897        
28898     },
28899
28900
28901     getResizeEl : function(){
28902         return this.wrap;
28903     },
28904
28905     getPositionEl : function(){
28906         return this.wrap;
28907     },
28908
28909     // private
28910     onRender : function(ct, position){
28911         
28912         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28913         var style = this.style;
28914         delete this.style;
28915         
28916         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28917         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28918         this.viewEl = this.wrap.createChild({ tag: 'div' });
28919         if (style) {
28920             this.viewEl.applyStyles(style);
28921         }
28922         if (this.width) {
28923             this.viewEl.setWidth(this.width);
28924         }
28925         if (this.height) {
28926             this.viewEl.setHeight(this.height);
28927         }
28928         //if(this.inputValue !== undefined){
28929         //this.setValue(this.value);
28930         
28931         
28932         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28933         
28934         
28935         this.grid.render();
28936         this.grid.getDataSource().on('remove', this.refreshValue, this);
28937         this.grid.getDataSource().on('update', this.refreshValue, this);
28938         this.grid.on('afteredit', this.refreshValue, this);
28939  
28940     },
28941      
28942     
28943     /**
28944      * Sets the value of the item. 
28945      * @param {String} either an object  or a string..
28946      */
28947     setValue : function(v){
28948         //this.value = v;
28949         v = v || []; // empty set..
28950         // this does not seem smart - it really only affects memoryproxy grids..
28951         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28952             var ds = this.grid.getDataSource();
28953             // assumes a json reader..
28954             var data = {}
28955             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28956             ds.loadData( data);
28957         }
28958         // clear selection so it does not get stale.
28959         if (this.grid.sm) { 
28960             this.grid.sm.clearSelections();
28961         }
28962         
28963         Roo.form.GridField.superclass.setValue.call(this, v);
28964         this.refreshValue();
28965         // should load data in the grid really....
28966     },
28967     
28968     // private
28969     refreshValue: function() {
28970          var val = [];
28971         this.grid.getDataSource().each(function(r) {
28972             val.push(r.data);
28973         });
28974         this.el.dom.value = Roo.encode(val);
28975     }
28976     
28977      
28978     
28979     
28980 });/*
28981  * Based on:
28982  * Ext JS Library 1.1.1
28983  * Copyright(c) 2006-2007, Ext JS, LLC.
28984  *
28985  * Originally Released Under LGPL - original licence link has changed is not relivant.
28986  *
28987  * Fork - LGPL
28988  * <script type="text/javascript">
28989  */
28990 /**
28991  * @class Roo.form.DisplayField
28992  * @extends Roo.form.Field
28993  * A generic Field to display non-editable data.
28994  * @constructor
28995  * Creates a new Display Field item.
28996  * @param {Object} config Configuration options
28997  */
28998 Roo.form.DisplayField = function(config){
28999     Roo.form.DisplayField.superclass.constructor.call(this, config);
29000     
29001 };
29002
29003 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29004     inputType:      'hidden',
29005     allowBlank:     true,
29006     readOnly:         true,
29007     
29008  
29009     /**
29010      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29011      */
29012     focusClass : undefined,
29013     /**
29014      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29015      */
29016     fieldClass: 'x-form-field',
29017     
29018      /**
29019      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29020      */
29021     valueRenderer: undefined,
29022     
29023     width: 100,
29024     /**
29025      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29026      * {tag: "input", type: "checkbox", autocomplete: "off"})
29027      */
29028      
29029  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29030
29031     onResize : function(){
29032         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29033         
29034     },
29035
29036     initEvents : function(){
29037         // Roo.form.Checkbox.superclass.initEvents.call(this);
29038         // has no events...
29039        
29040     },
29041
29042
29043     getResizeEl : function(){
29044         return this.wrap;
29045     },
29046
29047     getPositionEl : function(){
29048         return this.wrap;
29049     },
29050
29051     // private
29052     onRender : function(ct, position){
29053         
29054         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29055         //if(this.inputValue !== undefined){
29056         this.wrap = this.el.wrap();
29057         
29058         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29059         
29060         if (this.bodyStyle) {
29061             this.viewEl.applyStyles(this.bodyStyle);
29062         }
29063         //this.viewEl.setStyle('padding', '2px');
29064         
29065         this.setValue(this.value);
29066         
29067     },
29068 /*
29069     // private
29070     initValue : Roo.emptyFn,
29071
29072   */
29073
29074         // private
29075     onClick : function(){
29076         
29077     },
29078
29079     /**
29080      * Sets the checked state of the checkbox.
29081      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29082      */
29083     setValue : function(v){
29084         this.value = v;
29085         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29086         // this might be called before we have a dom element..
29087         if (!this.viewEl) {
29088             return;
29089         }
29090         this.viewEl.dom.innerHTML = html;
29091         Roo.form.DisplayField.superclass.setValue.call(this, v);
29092
29093     }
29094 });/*
29095  * 
29096  * Licence- LGPL
29097  * 
29098  */
29099
29100 /**
29101  * @class Roo.form.DayPicker
29102  * @extends Roo.form.Field
29103  * A Day picker show [M] [T] [W] ....
29104  * @constructor
29105  * Creates a new Day Picker
29106  * @param {Object} config Configuration options
29107  */
29108 Roo.form.DayPicker= function(config){
29109     Roo.form.DayPicker.superclass.constructor.call(this, config);
29110      
29111 };
29112
29113 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29114     /**
29115      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29116      */
29117     focusClass : undefined,
29118     /**
29119      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29120      */
29121     fieldClass: "x-form-field",
29122    
29123     /**
29124      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29125      * {tag: "input", type: "checkbox", autocomplete: "off"})
29126      */
29127     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29128     
29129    
29130     actionMode : 'viewEl', 
29131     //
29132     // private
29133  
29134     inputType : 'hidden',
29135     
29136      
29137     inputElement: false, // real input element?
29138     basedOn: false, // ????
29139     
29140     isFormField: true, // not sure where this is needed!!!!
29141
29142     onResize : function(){
29143         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29144         if(!this.boxLabel){
29145             this.el.alignTo(this.wrap, 'c-c');
29146         }
29147     },
29148
29149     initEvents : function(){
29150         Roo.form.Checkbox.superclass.initEvents.call(this);
29151         this.el.on("click", this.onClick,  this);
29152         this.el.on("change", this.onClick,  this);
29153     },
29154
29155
29156     getResizeEl : function(){
29157         return this.wrap;
29158     },
29159
29160     getPositionEl : function(){
29161         return this.wrap;
29162     },
29163
29164     
29165     // private
29166     onRender : function(ct, position){
29167         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29168        
29169         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29170         
29171         var r1 = '<table><tr>';
29172         var r2 = '<tr class="x-form-daypick-icons">';
29173         for (var i=0; i < 7; i++) {
29174             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29175             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29176         }
29177         
29178         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29179         viewEl.select('img').on('click', this.onClick, this);
29180         this.viewEl = viewEl;   
29181         
29182         
29183         // this will not work on Chrome!!!
29184         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29185         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29186         
29187         
29188           
29189
29190     },
29191
29192     // private
29193     initValue : Roo.emptyFn,
29194
29195     /**
29196      * Returns the checked state of the checkbox.
29197      * @return {Boolean} True if checked, else false
29198      */
29199     getValue : function(){
29200         return this.el.dom.value;
29201         
29202     },
29203
29204         // private
29205     onClick : function(e){ 
29206         //this.setChecked(!this.checked);
29207         Roo.get(e.target).toggleClass('x-menu-item-checked');
29208         this.refreshValue();
29209         //if(this.el.dom.checked != this.checked){
29210         //    this.setValue(this.el.dom.checked);
29211        // }
29212     },
29213     
29214     // private
29215     refreshValue : function()
29216     {
29217         var val = '';
29218         this.viewEl.select('img',true).each(function(e,i,n)  {
29219             val += e.is(".x-menu-item-checked") ? String(n) : '';
29220         });
29221         this.setValue(val, true);
29222     },
29223
29224     /**
29225      * Sets the checked state of the checkbox.
29226      * On is always based on a string comparison between inputValue and the param.
29227      * @param {Boolean/String} value - the value to set 
29228      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29229      */
29230     setValue : function(v,suppressEvent){
29231         if (!this.el.dom) {
29232             return;
29233         }
29234         var old = this.el.dom.value ;
29235         this.el.dom.value = v;
29236         if (suppressEvent) {
29237             return ;
29238         }
29239          
29240         // update display..
29241         this.viewEl.select('img',true).each(function(e,i,n)  {
29242             
29243             var on = e.is(".x-menu-item-checked");
29244             var newv = v.indexOf(String(n)) > -1;
29245             if (on != newv) {
29246                 e.toggleClass('x-menu-item-checked');
29247             }
29248             
29249         });
29250         
29251         
29252         this.fireEvent('change', this, v, old);
29253         
29254         
29255     },
29256    
29257     // handle setting of hidden value by some other method!!?!?
29258     setFromHidden: function()
29259     {
29260         if(!this.el){
29261             return;
29262         }
29263         //console.log("SET FROM HIDDEN");
29264         //alert('setFrom hidden');
29265         this.setValue(this.el.dom.value);
29266     },
29267     
29268     onDestroy : function()
29269     {
29270         if(this.viewEl){
29271             Roo.get(this.viewEl).remove();
29272         }
29273          
29274         Roo.form.DayPicker.superclass.onDestroy.call(this);
29275     }
29276
29277 });/*
29278  * RooJS Library 1.1.1
29279  * Copyright(c) 2008-2011  Alan Knowles
29280  *
29281  * License - LGPL
29282  */
29283  
29284
29285 /**
29286  * @class Roo.form.ComboCheck
29287  * @extends Roo.form.ComboBox
29288  * A combobox for multiple select items.
29289  *
29290  * FIXME - could do with a reset button..
29291  * 
29292  * @constructor
29293  * Create a new ComboCheck
29294  * @param {Object} config Configuration options
29295  */
29296 Roo.form.ComboCheck = function(config){
29297     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29298     // should verify some data...
29299     // like
29300     // hiddenName = required..
29301     // displayField = required
29302     // valudField == required
29303     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29304     var _t = this;
29305     Roo.each(req, function(e) {
29306         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29307             throw "Roo.form.ComboCheck : missing value for: " + e;
29308         }
29309     });
29310     
29311     
29312 };
29313
29314 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29315      
29316      
29317     editable : false,
29318      
29319     selectedClass: 'x-menu-item-checked', 
29320     
29321     // private
29322     onRender : function(ct, position){
29323         var _t = this;
29324         
29325         
29326         
29327         if(!this.tpl){
29328             var cls = 'x-combo-list';
29329
29330             
29331             this.tpl =  new Roo.Template({
29332                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29333                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29334                    '<span>{' + this.displayField + '}</span>' +
29335                     '</div>' 
29336                 
29337             });
29338         }
29339  
29340         
29341         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29342         this.view.singleSelect = false;
29343         this.view.multiSelect = true;
29344         this.view.toggleSelect = true;
29345         this.pageTb.add(new Roo.Toolbar.Fill(), {
29346             
29347             text: 'Done',
29348             handler: function()
29349             {
29350                 _t.collapse();
29351             }
29352         });
29353     },
29354     
29355     onViewOver : function(e, t){
29356         // do nothing...
29357         return;
29358         
29359     },
29360     
29361     onViewClick : function(doFocus,index){
29362         return;
29363         
29364     },
29365     select: function () {
29366         //Roo.log("SELECT CALLED");
29367     },
29368      
29369     selectByValue : function(xv, scrollIntoView){
29370         var ar = this.getValueArray();
29371         var sels = [];
29372         
29373         Roo.each(ar, function(v) {
29374             if(v === undefined || v === null){
29375                 return;
29376             }
29377             var r = this.findRecord(this.valueField, v);
29378             if(r){
29379                 sels.push(this.store.indexOf(r))
29380                 
29381             }
29382         },this);
29383         this.view.select(sels);
29384         return false;
29385     },
29386     
29387     
29388     
29389     onSelect : function(record, index){
29390        // Roo.log("onselect Called");
29391        // this is only called by the clear button now..
29392         this.view.clearSelections();
29393         this.setValue('[]');
29394         if (this.value != this.valueBefore) {
29395             this.fireEvent('change', this, this.value, this.valueBefore);
29396         }
29397     },
29398     getValueArray : function()
29399     {
29400         var ar = [] ;
29401         
29402         try {
29403             //Roo.log(this.value);
29404             if (typeof(this.value) == 'undefined') {
29405                 return [];
29406             }
29407             var ar = Roo.decode(this.value);
29408             return  ar instanceof Array ? ar : []; //?? valid?
29409             
29410         } catch(e) {
29411             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29412             return [];
29413         }
29414          
29415     },
29416     expand : function ()
29417     {
29418         Roo.form.ComboCheck.superclass.expand.call(this);
29419         this.valueBefore = this.value;
29420         
29421
29422     },
29423     
29424     collapse : function(){
29425         Roo.form.ComboCheck.superclass.collapse.call(this);
29426         var sl = this.view.getSelectedIndexes();
29427         var st = this.store;
29428         var nv = [];
29429         var tv = [];
29430         var r;
29431         Roo.each(sl, function(i) {
29432             r = st.getAt(i);
29433             nv.push(r.get(this.valueField));
29434         },this);
29435         this.setValue(Roo.encode(nv));
29436         if (this.value != this.valueBefore) {
29437
29438             this.fireEvent('change', this, this.value, this.valueBefore);
29439         }
29440         
29441     },
29442     
29443     setValue : function(v){
29444         // Roo.log(v);
29445         this.value = v;
29446         
29447         var vals = this.getValueArray();
29448         var tv = [];
29449         Roo.each(vals, function(k) {
29450             var r = this.findRecord(this.valueField, k);
29451             if(r){
29452                 tv.push(r.data[this.displayField]);
29453             }else if(this.valueNotFoundText !== undefined){
29454                 tv.push( this.valueNotFoundText );
29455             }
29456         },this);
29457        // Roo.log(tv);
29458         
29459         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29460         this.hiddenField.value = v;
29461         this.value = v;
29462     }
29463     
29464 });//<script type="text/javasscript">
29465  
29466
29467 /**
29468  * @class Roo.DDView
29469  * A DnD enabled version of Roo.View.
29470  * @param {Element/String} container The Element in which to create the View.
29471  * @param {String} tpl The template string used to create the markup for each element of the View
29472  * @param {Object} config The configuration properties. These include all the config options of
29473  * {@link Roo.View} plus some specific to this class.<br>
29474  * <p>
29475  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29476  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29477  * <p>
29478  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29479 .x-view-drag-insert-above {
29480         border-top:1px dotted #3366cc;
29481 }
29482 .x-view-drag-insert-below {
29483         border-bottom:1px dotted #3366cc;
29484 }
29485 </code></pre>
29486  * 
29487  */
29488  
29489 Roo.DDView = function(container, tpl, config) {
29490     Roo.DDView.superclass.constructor.apply(this, arguments);
29491     this.getEl().setStyle("outline", "0px none");
29492     this.getEl().unselectable();
29493     if (this.dragGroup) {
29494                 this.setDraggable(this.dragGroup.split(","));
29495     }
29496     if (this.dropGroup) {
29497                 this.setDroppable(this.dropGroup.split(","));
29498     }
29499     if (this.deletable) {
29500         this.setDeletable();
29501     }
29502     this.isDirtyFlag = false;
29503         this.addEvents({
29504                 "drop" : true
29505         });
29506 };
29507
29508 Roo.extend(Roo.DDView, Roo.View, {
29509 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29510 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
29511 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
29512 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
29513
29514         isFormField: true,
29515
29516         reset: Roo.emptyFn,
29517         
29518         clearInvalid: Roo.form.Field.prototype.clearInvalid,
29519
29520         validate: function() {
29521                 return true;
29522         },
29523         
29524         destroy: function() {
29525                 this.purgeListeners();
29526                 this.getEl.removeAllListeners();
29527                 this.getEl().remove();
29528                 if (this.dragZone) {
29529                         if (this.dragZone.destroy) {
29530                                 this.dragZone.destroy();
29531                         }
29532                 }
29533                 if (this.dropZone) {
29534                         if (this.dropZone.destroy) {
29535                                 this.dropZone.destroy();
29536                         }
29537                 }
29538         },
29539
29540 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
29541         getName: function() {
29542                 return this.name;
29543         },
29544
29545 /**     Loads the View from a JSON string representing the Records to put into the Store. */
29546         setValue: function(v) {
29547                 if (!this.store) {
29548                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
29549                 }
29550                 var data = {};
29551                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
29552                 this.store.proxy = new Roo.data.MemoryProxy(data);
29553                 this.store.load();
29554         },
29555
29556 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
29557         getValue: function() {
29558                 var result = '(';
29559                 this.store.each(function(rec) {
29560                         result += rec.id + ',';
29561                 });
29562                 return result.substr(0, result.length - 1) + ')';
29563         },
29564         
29565         getIds: function() {
29566                 var i = 0, result = new Array(this.store.getCount());
29567                 this.store.each(function(rec) {
29568                         result[i++] = rec.id;
29569                 });
29570                 return result;
29571         },
29572         
29573         isDirty: function() {
29574                 return this.isDirtyFlag;
29575         },
29576
29577 /**
29578  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
29579  *      whole Element becomes the target, and this causes the drop gesture to append.
29580  */
29581     getTargetFromEvent : function(e) {
29582                 var target = e.getTarget();
29583                 while ((target !== null) && (target.parentNode != this.el.dom)) {
29584                 target = target.parentNode;
29585                 }
29586                 if (!target) {
29587                         target = this.el.dom.lastChild || this.el.dom;
29588                 }
29589                 return target;
29590     },
29591
29592 /**
29593  *      Create the drag data which consists of an object which has the property "ddel" as
29594  *      the drag proxy element. 
29595  */
29596     getDragData : function(e) {
29597         var target = this.findItemFromChild(e.getTarget());
29598                 if(target) {
29599                         this.handleSelection(e);
29600                         var selNodes = this.getSelectedNodes();
29601             var dragData = {
29602                 source: this,
29603                 copy: this.copy || (this.allowCopy && e.ctrlKey),
29604                 nodes: selNodes,
29605                 records: []
29606                         };
29607                         var selectedIndices = this.getSelectedIndexes();
29608                         for (var i = 0; i < selectedIndices.length; i++) {
29609                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
29610                         }
29611                         if (selNodes.length == 1) {
29612                                 dragData.ddel = target.cloneNode(true); // the div element
29613                         } else {
29614                                 var div = document.createElement('div'); // create the multi element drag "ghost"
29615                                 div.className = 'multi-proxy';
29616                                 for (var i = 0, len = selNodes.length; i < len; i++) {
29617                                         div.appendChild(selNodes[i].cloneNode(true));
29618                                 }
29619                                 dragData.ddel = div;
29620                         }
29621             //console.log(dragData)
29622             //console.log(dragData.ddel.innerHTML)
29623                         return dragData;
29624                 }
29625         //console.log('nodragData')
29626                 return false;
29627     },
29628     
29629 /**     Specify to which ddGroup items in this DDView may be dragged. */
29630     setDraggable: function(ddGroup) {
29631         if (ddGroup instanceof Array) {
29632                 Roo.each(ddGroup, this.setDraggable, this);
29633                 return;
29634         }
29635         if (this.dragZone) {
29636                 this.dragZone.addToGroup(ddGroup);
29637         } else {
29638                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
29639                                 containerScroll: true,
29640                                 ddGroup: ddGroup 
29641
29642                         });
29643 //                      Draggability implies selection. DragZone's mousedown selects the element.
29644                         if (!this.multiSelect) { this.singleSelect = true; }
29645
29646 //                      Wire the DragZone's handlers up to methods in *this*
29647                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
29648                 }
29649     },
29650
29651 /**     Specify from which ddGroup this DDView accepts drops. */
29652     setDroppable: function(ddGroup) {
29653         if (ddGroup instanceof Array) {
29654                 Roo.each(ddGroup, this.setDroppable, this);
29655                 return;
29656         }
29657         if (this.dropZone) {
29658                 this.dropZone.addToGroup(ddGroup);
29659         } else {
29660                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
29661                                 containerScroll: true,
29662                                 ddGroup: ddGroup
29663                         });
29664
29665 //                      Wire the DropZone's handlers up to methods in *this*
29666                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
29667                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
29668                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
29669                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
29670                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
29671                 }
29672     },
29673
29674 /**     Decide whether to drop above or below a View node. */
29675     getDropPoint : function(e, n, dd){
29676         if (n == this.el.dom) { return "above"; }
29677                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29678                 var c = t + (b - t) / 2;
29679                 var y = Roo.lib.Event.getPageY(e);
29680                 if(y <= c) {
29681                         return "above";
29682                 }else{
29683                         return "below";
29684                 }
29685     },
29686
29687     onNodeEnter : function(n, dd, e, data){
29688                 return false;
29689     },
29690     
29691     onNodeOver : function(n, dd, e, data){
29692                 var pt = this.getDropPoint(e, n, dd);
29693                 // set the insert point style on the target node
29694                 var dragElClass = this.dropNotAllowed;
29695                 if (pt) {
29696                         var targetElClass;
29697                         if (pt == "above"){
29698                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29699                                 targetElClass = "x-view-drag-insert-above";
29700                         } else {
29701                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29702                                 targetElClass = "x-view-drag-insert-below";
29703                         }
29704                         if (this.lastInsertClass != targetElClass){
29705                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29706                                 this.lastInsertClass = targetElClass;
29707                         }
29708                 }
29709                 return dragElClass;
29710         },
29711
29712     onNodeOut : function(n, dd, e, data){
29713                 this.removeDropIndicators(n);
29714     },
29715
29716     onNodeDrop : function(n, dd, e, data){
29717         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29718                 return false;
29719         }
29720         var pt = this.getDropPoint(e, n, dd);
29721                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29722                 if (pt == "below") { insertAt++; }
29723                 for (var i = 0; i < data.records.length; i++) {
29724                         var r = data.records[i];
29725                         var dup = this.store.getById(r.id);
29726                         if (dup && (dd != this.dragZone)) {
29727                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29728                         } else {
29729                                 if (data.copy) {
29730                                         this.store.insert(insertAt++, r.copy());
29731                                 } else {
29732                                         data.source.isDirtyFlag = true;
29733                                         r.store.remove(r);
29734                                         this.store.insert(insertAt++, r);
29735                                 }
29736                                 this.isDirtyFlag = true;
29737                         }
29738                 }
29739                 this.dragZone.cachedTarget = null;
29740                 return true;
29741     },
29742
29743     removeDropIndicators : function(n){
29744                 if(n){
29745                         Roo.fly(n).removeClass([
29746                                 "x-view-drag-insert-above",
29747                                 "x-view-drag-insert-below"]);
29748                         this.lastInsertClass = "_noclass";
29749                 }
29750     },
29751
29752 /**
29753  *      Utility method. Add a delete option to the DDView's context menu.
29754  *      @param {String} imageUrl The URL of the "delete" icon image.
29755  */
29756         setDeletable: function(imageUrl) {
29757                 if (!this.singleSelect && !this.multiSelect) {
29758                         this.singleSelect = true;
29759                 }
29760                 var c = this.getContextMenu();
29761                 this.contextMenu.on("itemclick", function(item) {
29762                         switch (item.id) {
29763                                 case "delete":
29764                                         this.remove(this.getSelectedIndexes());
29765                                         break;
29766                         }
29767                 }, this);
29768                 this.contextMenu.add({
29769                         icon: imageUrl,
29770                         id: "delete",
29771                         text: 'Delete'
29772                 });
29773         },
29774         
29775 /**     Return the context menu for this DDView. */
29776         getContextMenu: function() {
29777                 if (!this.contextMenu) {
29778 //                      Create the View's context menu
29779                         this.contextMenu = new Roo.menu.Menu({
29780                                 id: this.id + "-contextmenu"
29781                         });
29782                         this.el.on("contextmenu", this.showContextMenu, this);
29783                 }
29784                 return this.contextMenu;
29785         },
29786         
29787         disableContextMenu: function() {
29788                 if (this.contextMenu) {
29789                         this.el.un("contextmenu", this.showContextMenu, this);
29790                 }
29791         },
29792
29793         showContextMenu: function(e, item) {
29794         item = this.findItemFromChild(e.getTarget());
29795                 if (item) {
29796                         e.stopEvent();
29797                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29798                         this.contextMenu.showAt(e.getXY());
29799             }
29800     },
29801
29802 /**
29803  *      Remove {@link Roo.data.Record}s at the specified indices.
29804  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29805  */
29806     remove: function(selectedIndices) {
29807                 selectedIndices = [].concat(selectedIndices);
29808                 for (var i = 0; i < selectedIndices.length; i++) {
29809                         var rec = this.store.getAt(selectedIndices[i]);
29810                         this.store.remove(rec);
29811                 }
29812     },
29813
29814 /**
29815  *      Double click fires the event, but also, if this is draggable, and there is only one other
29816  *      related DropZone, it transfers the selected node.
29817  */
29818     onDblClick : function(e){
29819         var item = this.findItemFromChild(e.getTarget());
29820         if(item){
29821             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29822                 return false;
29823             }
29824             if (this.dragGroup) {
29825                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29826                     while (targets.indexOf(this.dropZone) > -1) {
29827                             targets.remove(this.dropZone);
29828                                 }
29829                     if (targets.length == 1) {
29830                                         this.dragZone.cachedTarget = null;
29831                         var el = Roo.get(targets[0].getEl());
29832                         var box = el.getBox(true);
29833                         targets[0].onNodeDrop(el.dom, {
29834                                 target: el.dom,
29835                                 xy: [box.x, box.y + box.height - 1]
29836                         }, null, this.getDragData(e));
29837                     }
29838                 }
29839         }
29840     },
29841     
29842     handleSelection: function(e) {
29843                 this.dragZone.cachedTarget = null;
29844         var item = this.findItemFromChild(e.getTarget());
29845         if (!item) {
29846                 this.clearSelections(true);
29847                 return;
29848         }
29849                 if (item && (this.multiSelect || this.singleSelect)){
29850                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29851                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29852                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29853                                 this.unselect(item);
29854                         } else {
29855                                 this.select(item, this.multiSelect && e.ctrlKey);
29856                                 this.lastSelection = item;
29857                         }
29858                 }
29859     },
29860
29861     onItemClick : function(item, index, e){
29862                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29863                         return false;
29864                 }
29865                 return true;
29866     },
29867
29868     unselect : function(nodeInfo, suppressEvent){
29869                 var node = this.getNode(nodeInfo);
29870                 if(node && this.isSelected(node)){
29871                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29872                                 Roo.fly(node).removeClass(this.selectedClass);
29873                                 this.selections.remove(node);
29874                                 if(!suppressEvent){
29875                                         this.fireEvent("selectionchange", this, this.selections);
29876                                 }
29877                         }
29878                 }
29879     }
29880 });
29881 /*
29882  * Based on:
29883  * Ext JS Library 1.1.1
29884  * Copyright(c) 2006-2007, Ext JS, LLC.
29885  *
29886  * Originally Released Under LGPL - original licence link has changed is not relivant.
29887  *
29888  * Fork - LGPL
29889  * <script type="text/javascript">
29890  */
29891  
29892 /**
29893  * @class Roo.LayoutManager
29894  * @extends Roo.util.Observable
29895  * Base class for layout managers.
29896  */
29897 Roo.LayoutManager = function(container, config){
29898     Roo.LayoutManager.superclass.constructor.call(this);
29899     this.el = Roo.get(container);
29900     // ie scrollbar fix
29901     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29902         document.body.scroll = "no";
29903     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29904         this.el.position('relative');
29905     }
29906     this.id = this.el.id;
29907     this.el.addClass("x-layout-container");
29908     /** false to disable window resize monitoring @type Boolean */
29909     this.monitorWindowResize = true;
29910     this.regions = {};
29911     this.addEvents({
29912         /**
29913          * @event layout
29914          * Fires when a layout is performed. 
29915          * @param {Roo.LayoutManager} this
29916          */
29917         "layout" : true,
29918         /**
29919          * @event regionresized
29920          * Fires when the user resizes a region. 
29921          * @param {Roo.LayoutRegion} region The resized region
29922          * @param {Number} newSize The new size (width for east/west, height for north/south)
29923          */
29924         "regionresized" : true,
29925         /**
29926          * @event regioncollapsed
29927          * Fires when a region is collapsed. 
29928          * @param {Roo.LayoutRegion} region The collapsed region
29929          */
29930         "regioncollapsed" : true,
29931         /**
29932          * @event regionexpanded
29933          * Fires when a region is expanded.  
29934          * @param {Roo.LayoutRegion} region The expanded region
29935          */
29936         "regionexpanded" : true
29937     });
29938     this.updating = false;
29939     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29940 };
29941
29942 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29943     /**
29944      * Returns true if this layout is currently being updated
29945      * @return {Boolean}
29946      */
29947     isUpdating : function(){
29948         return this.updating; 
29949     },
29950     
29951     /**
29952      * Suspend the LayoutManager from doing auto-layouts while
29953      * making multiple add or remove calls
29954      */
29955     beginUpdate : function(){
29956         this.updating = true;    
29957     },
29958     
29959     /**
29960      * Restore auto-layouts and optionally disable the manager from performing a layout
29961      * @param {Boolean} noLayout true to disable a layout update 
29962      */
29963     endUpdate : function(noLayout){
29964         this.updating = false;
29965         if(!noLayout){
29966             this.layout();
29967         }    
29968     },
29969     
29970     layout: function(){
29971         
29972     },
29973     
29974     onRegionResized : function(region, newSize){
29975         this.fireEvent("regionresized", region, newSize);
29976         this.layout();
29977     },
29978     
29979     onRegionCollapsed : function(region){
29980         this.fireEvent("regioncollapsed", region);
29981     },
29982     
29983     onRegionExpanded : function(region){
29984         this.fireEvent("regionexpanded", region);
29985     },
29986         
29987     /**
29988      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29989      * performs box-model adjustments.
29990      * @return {Object} The size as an object {width: (the width), height: (the height)}
29991      */
29992     getViewSize : function(){
29993         var size;
29994         if(this.el.dom != document.body){
29995             size = this.el.getSize();
29996         }else{
29997             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29998         }
29999         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30000         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30001         return size;
30002     },
30003     
30004     /**
30005      * Returns the Element this layout is bound to.
30006      * @return {Roo.Element}
30007      */
30008     getEl : function(){
30009         return this.el;
30010     },
30011     
30012     /**
30013      * Returns the specified region.
30014      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30015      * @return {Roo.LayoutRegion}
30016      */
30017     getRegion : function(target){
30018         return this.regions[target.toLowerCase()];
30019     },
30020     
30021     onWindowResize : function(){
30022         if(this.monitorWindowResize){
30023             this.layout();
30024         }
30025     }
30026 });/*
30027  * Based on:
30028  * Ext JS Library 1.1.1
30029  * Copyright(c) 2006-2007, Ext JS, LLC.
30030  *
30031  * Originally Released Under LGPL - original licence link has changed is not relivant.
30032  *
30033  * Fork - LGPL
30034  * <script type="text/javascript">
30035  */
30036 /**
30037  * @class Roo.BorderLayout
30038  * @extends Roo.LayoutManager
30039  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30040  * please see: <br><br>
30041  * <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>
30042  * <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>
30043  * Example:
30044  <pre><code>
30045  var layout = new Roo.BorderLayout(document.body, {
30046     north: {
30047         initialSize: 25,
30048         titlebar: false
30049     },
30050     west: {
30051         split:true,
30052         initialSize: 200,
30053         minSize: 175,
30054         maxSize: 400,
30055         titlebar: true,
30056         collapsible: true
30057     },
30058     east: {
30059         split:true,
30060         initialSize: 202,
30061         minSize: 175,
30062         maxSize: 400,
30063         titlebar: true,
30064         collapsible: true
30065     },
30066     south: {
30067         split:true,
30068         initialSize: 100,
30069         minSize: 100,
30070         maxSize: 200,
30071         titlebar: true,
30072         collapsible: true
30073     },
30074     center: {
30075         titlebar: true,
30076         autoScroll:true,
30077         resizeTabs: true,
30078         minTabWidth: 50,
30079         preferredTabWidth: 150
30080     }
30081 });
30082
30083 // shorthand
30084 var CP = Roo.ContentPanel;
30085
30086 layout.beginUpdate();
30087 layout.add("north", new CP("north", "North"));
30088 layout.add("south", new CP("south", {title: "South", closable: true}));
30089 layout.add("west", new CP("west", {title: "West"}));
30090 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30091 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30092 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30093 layout.getRegion("center").showPanel("center1");
30094 layout.endUpdate();
30095 </code></pre>
30096
30097 <b>The container the layout is rendered into can be either the body element or any other element.
30098 If it is not the body element, the container needs to either be an absolute positioned element,
30099 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30100 the container size if it is not the body element.</b>
30101
30102 * @constructor
30103 * Create a new BorderLayout
30104 * @param {String/HTMLElement/Element} container The container this layout is bound to
30105 * @param {Object} config Configuration options
30106  */
30107 Roo.BorderLayout = function(container, config){
30108     config = config || {};
30109     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30110     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30111     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30112         var target = this.factory.validRegions[i];
30113         if(config[target]){
30114             this.addRegion(target, config[target]);
30115         }
30116     }
30117 };
30118
30119 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30120     /**
30121      * Creates and adds a new region if it doesn't already exist.
30122      * @param {String} target The target region key (north, south, east, west or center).
30123      * @param {Object} config The regions config object
30124      * @return {BorderLayoutRegion} The new region
30125      */
30126     addRegion : function(target, config){
30127         if(!this.regions[target]){
30128             var r = this.factory.create(target, this, config);
30129             this.bindRegion(target, r);
30130         }
30131         return this.regions[target];
30132     },
30133
30134     // private (kinda)
30135     bindRegion : function(name, r){
30136         this.regions[name] = r;
30137         r.on("visibilitychange", this.layout, this);
30138         r.on("paneladded", this.layout, this);
30139         r.on("panelremoved", this.layout, this);
30140         r.on("invalidated", this.layout, this);
30141         r.on("resized", this.onRegionResized, this);
30142         r.on("collapsed", this.onRegionCollapsed, this);
30143         r.on("expanded", this.onRegionExpanded, this);
30144     },
30145
30146     /**
30147      * Performs a layout update.
30148      */
30149     layout : function(){
30150         if(this.updating) return;
30151         var size = this.getViewSize();
30152         var w = size.width;
30153         var h = size.height;
30154         var centerW = w;
30155         var centerH = h;
30156         var centerY = 0;
30157         var centerX = 0;
30158         //var x = 0, y = 0;
30159
30160         var rs = this.regions;
30161         var north = rs["north"];
30162         var south = rs["south"]; 
30163         var west = rs["west"];
30164         var east = rs["east"];
30165         var center = rs["center"];
30166         //if(this.hideOnLayout){ // not supported anymore
30167             //c.el.setStyle("display", "none");
30168         //}
30169         if(north && north.isVisible()){
30170             var b = north.getBox();
30171             var m = north.getMargins();
30172             b.width = w - (m.left+m.right);
30173             b.x = m.left;
30174             b.y = m.top;
30175             centerY = b.height + b.y + m.bottom;
30176             centerH -= centerY;
30177             north.updateBox(this.safeBox(b));
30178         }
30179         if(south && south.isVisible()){
30180             var b = south.getBox();
30181             var m = south.getMargins();
30182             b.width = w - (m.left+m.right);
30183             b.x = m.left;
30184             var totalHeight = (b.height + m.top + m.bottom);
30185             b.y = h - totalHeight + m.top;
30186             centerH -= totalHeight;
30187             south.updateBox(this.safeBox(b));
30188         }
30189         if(west && west.isVisible()){
30190             var b = west.getBox();
30191             var m = west.getMargins();
30192             b.height = centerH - (m.top+m.bottom);
30193             b.x = m.left;
30194             b.y = centerY + m.top;
30195             var totalWidth = (b.width + m.left + m.right);
30196             centerX += totalWidth;
30197             centerW -= totalWidth;
30198             west.updateBox(this.safeBox(b));
30199         }
30200         if(east && east.isVisible()){
30201             var b = east.getBox();
30202             var m = east.getMargins();
30203             b.height = centerH - (m.top+m.bottom);
30204             var totalWidth = (b.width + m.left + m.right);
30205             b.x = w - totalWidth + m.left;
30206             b.y = centerY + m.top;
30207             centerW -= totalWidth;
30208             east.updateBox(this.safeBox(b));
30209         }
30210         if(center){
30211             var m = center.getMargins();
30212             var centerBox = {
30213                 x: centerX + m.left,
30214                 y: centerY + m.top,
30215                 width: centerW - (m.left+m.right),
30216                 height: centerH - (m.top+m.bottom)
30217             };
30218             //if(this.hideOnLayout){
30219                 //center.el.setStyle("display", "block");
30220             //}
30221             center.updateBox(this.safeBox(centerBox));
30222         }
30223         this.el.repaint();
30224         this.fireEvent("layout", this);
30225     },
30226
30227     // private
30228     safeBox : function(box){
30229         box.width = Math.max(0, box.width);
30230         box.height = Math.max(0, box.height);
30231         return box;
30232     },
30233
30234     /**
30235      * Adds a ContentPanel (or subclass) to this layout.
30236      * @param {String} target The target region key (north, south, east, west or center).
30237      * @param {Roo.ContentPanel} panel The panel to add
30238      * @return {Roo.ContentPanel} The added panel
30239      */
30240     add : function(target, panel){
30241          
30242         target = target.toLowerCase();
30243         return this.regions[target].add(panel);
30244     },
30245
30246     /**
30247      * Remove a ContentPanel (or subclass) to this layout.
30248      * @param {String} target The target region key (north, south, east, west or center).
30249      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30250      * @return {Roo.ContentPanel} The removed panel
30251      */
30252     remove : function(target, panel){
30253         target = target.toLowerCase();
30254         return this.regions[target].remove(panel);
30255     },
30256
30257     /**
30258      * Searches all regions for a panel with the specified id
30259      * @param {String} panelId
30260      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30261      */
30262     findPanel : function(panelId){
30263         var rs = this.regions;
30264         for(var target in rs){
30265             if(typeof rs[target] != "function"){
30266                 var p = rs[target].getPanel(panelId);
30267                 if(p){
30268                     return p;
30269                 }
30270             }
30271         }
30272         return null;
30273     },
30274
30275     /**
30276      * Searches all regions for a panel with the specified id and activates (shows) it.
30277      * @param {String/ContentPanel} panelId The panels id or the panel itself
30278      * @return {Roo.ContentPanel} The shown panel or null
30279      */
30280     showPanel : function(panelId) {
30281       var rs = this.regions;
30282       for(var target in rs){
30283          var r = rs[target];
30284          if(typeof r != "function"){
30285             if(r.hasPanel(panelId)){
30286                return r.showPanel(panelId);
30287             }
30288          }
30289       }
30290       return null;
30291    },
30292
30293    /**
30294      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30295      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30296      */
30297     restoreState : function(provider){
30298         if(!provider){
30299             provider = Roo.state.Manager;
30300         }
30301         var sm = new Roo.LayoutStateManager();
30302         sm.init(this, provider);
30303     },
30304
30305     /**
30306      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30307      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30308      * a valid ContentPanel config object.  Example:
30309      * <pre><code>
30310 // Create the main layout
30311 var layout = new Roo.BorderLayout('main-ct', {
30312     west: {
30313         split:true,
30314         minSize: 175,
30315         titlebar: true
30316     },
30317     center: {
30318         title:'Components'
30319     }
30320 }, 'main-ct');
30321
30322 // Create and add multiple ContentPanels at once via configs
30323 layout.batchAdd({
30324    west: {
30325        id: 'source-files',
30326        autoCreate:true,
30327        title:'Ext Source Files',
30328        autoScroll:true,
30329        fitToFrame:true
30330    },
30331    center : {
30332        el: cview,
30333        autoScroll:true,
30334        fitToFrame:true,
30335        toolbar: tb,
30336        resizeEl:'cbody'
30337    }
30338 });
30339 </code></pre>
30340      * @param {Object} regions An object containing ContentPanel configs by region name
30341      */
30342     batchAdd : function(regions){
30343         this.beginUpdate();
30344         for(var rname in regions){
30345             var lr = this.regions[rname];
30346             if(lr){
30347                 this.addTypedPanels(lr, regions[rname]);
30348             }
30349         }
30350         this.endUpdate();
30351     },
30352
30353     // private
30354     addTypedPanels : function(lr, ps){
30355         if(typeof ps == 'string'){
30356             lr.add(new Roo.ContentPanel(ps));
30357         }
30358         else if(ps instanceof Array){
30359             for(var i =0, len = ps.length; i < len; i++){
30360                 this.addTypedPanels(lr, ps[i]);
30361             }
30362         }
30363         else if(!ps.events){ // raw config?
30364             var el = ps.el;
30365             delete ps.el; // prevent conflict
30366             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30367         }
30368         else {  // panel object assumed!
30369             lr.add(ps);
30370         }
30371     },
30372     /**
30373      * Adds a xtype elements to the layout.
30374      * <pre><code>
30375
30376 layout.addxtype({
30377        xtype : 'ContentPanel',
30378        region: 'west',
30379        items: [ .... ]
30380    }
30381 );
30382
30383 layout.addxtype({
30384         xtype : 'NestedLayoutPanel',
30385         region: 'west',
30386         layout: {
30387            center: { },
30388            west: { }   
30389         },
30390         items : [ ... list of content panels or nested layout panels.. ]
30391    }
30392 );
30393 </code></pre>
30394      * @param {Object} cfg Xtype definition of item to add.
30395      */
30396     addxtype : function(cfg)
30397     {
30398         // basically accepts a pannel...
30399         // can accept a layout region..!?!?
30400         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30401         
30402         if (!cfg.xtype.match(/Panel$/)) {
30403             return false;
30404         }
30405         var ret = false;
30406         
30407         if (typeof(cfg.region) == 'undefined') {
30408             Roo.log("Failed to add Panel, region was not set");
30409             Roo.log(cfg);
30410             return false;
30411         }
30412         var region = cfg.region;
30413         delete cfg.region;
30414         
30415           
30416         var xitems = [];
30417         if (cfg.items) {
30418             xitems = cfg.items;
30419             delete cfg.items;
30420         }
30421         var nb = false;
30422         
30423         switch(cfg.xtype) 
30424         {
30425             case 'ContentPanel':  // ContentPanel (el, cfg)
30426             case 'ScrollPanel':  // ContentPanel (el, cfg)
30427                 if(cfg.autoCreate) {
30428                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30429                 } else {
30430                     var el = this.el.createChild();
30431                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30432                 }
30433                 
30434                 this.add(region, ret);
30435                 break;
30436             
30437             
30438             case 'TreePanel': // our new panel!
30439                 cfg.el = this.el.createChild();
30440                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30441                 this.add(region, ret);
30442                 break;
30443             
30444             case 'NestedLayoutPanel': 
30445                 // create a new Layout (which is  a Border Layout...
30446                 var el = this.el.createChild();
30447                 var clayout = cfg.layout;
30448                 delete cfg.layout;
30449                 clayout.items   = clayout.items  || [];
30450                 // replace this exitems with the clayout ones..
30451                 xitems = clayout.items;
30452                  
30453                 
30454                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30455                     cfg.background = false;
30456                 }
30457                 var layout = new Roo.BorderLayout(el, clayout);
30458                 
30459                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30460                 //console.log('adding nested layout panel '  + cfg.toSource());
30461                 this.add(region, ret);
30462                 nb = {}; /// find first...
30463                 break;
30464                 
30465             case 'GridPanel': 
30466             
30467                 // needs grid and region
30468                 
30469                 //var el = this.getRegion(region).el.createChild();
30470                 var el = this.el.createChild();
30471                 // create the grid first...
30472                 
30473                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30474                 delete cfg.grid;
30475                 if (region == 'center' && this.active ) {
30476                     cfg.background = false;
30477                 }
30478                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30479                 
30480                 this.add(region, ret);
30481                 if (cfg.background) {
30482                     ret.on('activate', function(gp) {
30483                         if (!gp.grid.rendered) {
30484                             gp.grid.render();
30485                         }
30486                     });
30487                 } else {
30488                     grid.render();
30489                 }
30490                 break;
30491            
30492                
30493                 
30494                 
30495             default: 
30496                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30497                 return null;
30498              // GridPanel (grid, cfg)
30499             
30500         }
30501         this.beginUpdate();
30502         // add children..
30503         var region = '';
30504         var abn = {};
30505         Roo.each(xitems, function(i)  {
30506             region = nb && i.region ? i.region : false;
30507             
30508             var add = ret.addxtype(i);
30509            
30510             if (region) {
30511                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
30512                 if (!i.background) {
30513                     abn[region] = nb[region] ;
30514                 }
30515             }
30516             
30517         });
30518         this.endUpdate();
30519
30520         // make the last non-background panel active..
30521         //if (nb) { Roo.log(abn); }
30522         if (nb) {
30523             
30524             for(var r in abn) {
30525                 region = this.getRegion(r);
30526                 if (region) {
30527                     // tried using nb[r], but it does not work..
30528                      
30529                     region.showPanel(abn[r]);
30530                    
30531                 }
30532             }
30533         }
30534         return ret;
30535         
30536     }
30537 });
30538
30539 /**
30540  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
30541  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
30542  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
30543  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
30544  * <pre><code>
30545 // shorthand
30546 var CP = Roo.ContentPanel;
30547
30548 var layout = Roo.BorderLayout.create({
30549     north: {
30550         initialSize: 25,
30551         titlebar: false,
30552         panels: [new CP("north", "North")]
30553     },
30554     west: {
30555         split:true,
30556         initialSize: 200,
30557         minSize: 175,
30558         maxSize: 400,
30559         titlebar: true,
30560         collapsible: true,
30561         panels: [new CP("west", {title: "West"})]
30562     },
30563     east: {
30564         split:true,
30565         initialSize: 202,
30566         minSize: 175,
30567         maxSize: 400,
30568         titlebar: true,
30569         collapsible: true,
30570         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
30571     },
30572     south: {
30573         split:true,
30574         initialSize: 100,
30575         minSize: 100,
30576         maxSize: 200,
30577         titlebar: true,
30578         collapsible: true,
30579         panels: [new CP("south", {title: "South", closable: true})]
30580     },
30581     center: {
30582         titlebar: true,
30583         autoScroll:true,
30584         resizeTabs: true,
30585         minTabWidth: 50,
30586         preferredTabWidth: 150,
30587         panels: [
30588             new CP("center1", {title: "Close Me", closable: true}),
30589             new CP("center2", {title: "Center Panel", closable: false})
30590         ]
30591     }
30592 }, document.body);
30593
30594 layout.getRegion("center").showPanel("center1");
30595 </code></pre>
30596  * @param config
30597  * @param targetEl
30598  */
30599 Roo.BorderLayout.create = function(config, targetEl){
30600     var layout = new Roo.BorderLayout(targetEl || document.body, config);
30601     layout.beginUpdate();
30602     var regions = Roo.BorderLayout.RegionFactory.validRegions;
30603     for(var j = 0, jlen = regions.length; j < jlen; j++){
30604         var lr = regions[j];
30605         if(layout.regions[lr] && config[lr].panels){
30606             var r = layout.regions[lr];
30607             var ps = config[lr].panels;
30608             layout.addTypedPanels(r, ps);
30609         }
30610     }
30611     layout.endUpdate();
30612     return layout;
30613 };
30614
30615 // private
30616 Roo.BorderLayout.RegionFactory = {
30617     // private
30618     validRegions : ["north","south","east","west","center"],
30619
30620     // private
30621     create : function(target, mgr, config){
30622         target = target.toLowerCase();
30623         if(config.lightweight || config.basic){
30624             return new Roo.BasicLayoutRegion(mgr, config, target);
30625         }
30626         switch(target){
30627             case "north":
30628                 return new Roo.NorthLayoutRegion(mgr, config);
30629             case "south":
30630                 return new Roo.SouthLayoutRegion(mgr, config);
30631             case "east":
30632                 return new Roo.EastLayoutRegion(mgr, config);
30633             case "west":
30634                 return new Roo.WestLayoutRegion(mgr, config);
30635             case "center":
30636                 return new Roo.CenterLayoutRegion(mgr, config);
30637         }
30638         throw 'Layout region "'+target+'" not supported.';
30639     }
30640 };/*
30641  * Based on:
30642  * Ext JS Library 1.1.1
30643  * Copyright(c) 2006-2007, Ext JS, LLC.
30644  *
30645  * Originally Released Under LGPL - original licence link has changed is not relivant.
30646  *
30647  * Fork - LGPL
30648  * <script type="text/javascript">
30649  */
30650  
30651 /**
30652  * @class Roo.BasicLayoutRegion
30653  * @extends Roo.util.Observable
30654  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
30655  * and does not have a titlebar, tabs or any other features. All it does is size and position 
30656  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
30657  */
30658 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
30659     this.mgr = mgr;
30660     this.position  = pos;
30661     this.events = {
30662         /**
30663          * @scope Roo.BasicLayoutRegion
30664          */
30665         
30666         /**
30667          * @event beforeremove
30668          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30669          * @param {Roo.LayoutRegion} this
30670          * @param {Roo.ContentPanel} panel The panel
30671          * @param {Object} e The cancel event object
30672          */
30673         "beforeremove" : true,
30674         /**
30675          * @event invalidated
30676          * Fires when the layout for this region is changed.
30677          * @param {Roo.LayoutRegion} this
30678          */
30679         "invalidated" : true,
30680         /**
30681          * @event visibilitychange
30682          * Fires when this region is shown or hidden 
30683          * @param {Roo.LayoutRegion} this
30684          * @param {Boolean} visibility true or false
30685          */
30686         "visibilitychange" : true,
30687         /**
30688          * @event paneladded
30689          * Fires when a panel is added. 
30690          * @param {Roo.LayoutRegion} this
30691          * @param {Roo.ContentPanel} panel The panel
30692          */
30693         "paneladded" : true,
30694         /**
30695          * @event panelremoved
30696          * Fires when a panel is removed. 
30697          * @param {Roo.LayoutRegion} this
30698          * @param {Roo.ContentPanel} panel The panel
30699          */
30700         "panelremoved" : true,
30701         /**
30702          * @event collapsed
30703          * Fires when this region is collapsed.
30704          * @param {Roo.LayoutRegion} this
30705          */
30706         "collapsed" : true,
30707         /**
30708          * @event expanded
30709          * Fires when this region is expanded.
30710          * @param {Roo.LayoutRegion} this
30711          */
30712         "expanded" : true,
30713         /**
30714          * @event slideshow
30715          * Fires when this region is slid into view.
30716          * @param {Roo.LayoutRegion} this
30717          */
30718         "slideshow" : true,
30719         /**
30720          * @event slidehide
30721          * Fires when this region slides out of view. 
30722          * @param {Roo.LayoutRegion} this
30723          */
30724         "slidehide" : true,
30725         /**
30726          * @event panelactivated
30727          * Fires when a panel is activated. 
30728          * @param {Roo.LayoutRegion} this
30729          * @param {Roo.ContentPanel} panel The activated panel
30730          */
30731         "panelactivated" : true,
30732         /**
30733          * @event resized
30734          * Fires when the user resizes this region. 
30735          * @param {Roo.LayoutRegion} this
30736          * @param {Number} newSize The new size (width for east/west, height for north/south)
30737          */
30738         "resized" : true
30739     };
30740     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30741     this.panels = new Roo.util.MixedCollection();
30742     this.panels.getKey = this.getPanelId.createDelegate(this);
30743     this.box = null;
30744     this.activePanel = null;
30745     // ensure listeners are added...
30746     
30747     if (config.listeners || config.events) {
30748         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30749             listeners : config.listeners || {},
30750             events : config.events || {}
30751         });
30752     }
30753     
30754     if(skipConfig !== true){
30755         this.applyConfig(config);
30756     }
30757 };
30758
30759 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30760     getPanelId : function(p){
30761         return p.getId();
30762     },
30763     
30764     applyConfig : function(config){
30765         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30766         this.config = config;
30767         
30768     },
30769     
30770     /**
30771      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30772      * the width, for horizontal (north, south) the height.
30773      * @param {Number} newSize The new width or height
30774      */
30775     resizeTo : function(newSize){
30776         var el = this.el ? this.el :
30777                  (this.activePanel ? this.activePanel.getEl() : null);
30778         if(el){
30779             switch(this.position){
30780                 case "east":
30781                 case "west":
30782                     el.setWidth(newSize);
30783                     this.fireEvent("resized", this, newSize);
30784                 break;
30785                 case "north":
30786                 case "south":
30787                     el.setHeight(newSize);
30788                     this.fireEvent("resized", this, newSize);
30789                 break;                
30790             }
30791         }
30792     },
30793     
30794     getBox : function(){
30795         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30796     },
30797     
30798     getMargins : function(){
30799         return this.margins;
30800     },
30801     
30802     updateBox : function(box){
30803         this.box = box;
30804         var el = this.activePanel.getEl();
30805         el.dom.style.left = box.x + "px";
30806         el.dom.style.top = box.y + "px";
30807         this.activePanel.setSize(box.width, box.height);
30808     },
30809     
30810     /**
30811      * Returns the container element for this region.
30812      * @return {Roo.Element}
30813      */
30814     getEl : function(){
30815         return this.activePanel;
30816     },
30817     
30818     /**
30819      * Returns true if this region is currently visible.
30820      * @return {Boolean}
30821      */
30822     isVisible : function(){
30823         return this.activePanel ? true : false;
30824     },
30825     
30826     setActivePanel : function(panel){
30827         panel = this.getPanel(panel);
30828         if(this.activePanel && this.activePanel != panel){
30829             this.activePanel.setActiveState(false);
30830             this.activePanel.getEl().setLeftTop(-10000,-10000);
30831         }
30832         this.activePanel = panel;
30833         panel.setActiveState(true);
30834         if(this.box){
30835             panel.setSize(this.box.width, this.box.height);
30836         }
30837         this.fireEvent("panelactivated", this, panel);
30838         this.fireEvent("invalidated");
30839     },
30840     
30841     /**
30842      * Show the specified panel.
30843      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30844      * @return {Roo.ContentPanel} The shown panel or null
30845      */
30846     showPanel : function(panel){
30847         if(panel = this.getPanel(panel)){
30848             this.setActivePanel(panel);
30849         }
30850         return panel;
30851     },
30852     
30853     /**
30854      * Get the active panel for this region.
30855      * @return {Roo.ContentPanel} The active panel or null
30856      */
30857     getActivePanel : function(){
30858         return this.activePanel;
30859     },
30860     
30861     /**
30862      * Add the passed ContentPanel(s)
30863      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30864      * @return {Roo.ContentPanel} The panel added (if only one was added)
30865      */
30866     add : function(panel){
30867         if(arguments.length > 1){
30868             for(var i = 0, len = arguments.length; i < len; i++) {
30869                 this.add(arguments[i]);
30870             }
30871             return null;
30872         }
30873         if(this.hasPanel(panel)){
30874             this.showPanel(panel);
30875             return panel;
30876         }
30877         var el = panel.getEl();
30878         if(el.dom.parentNode != this.mgr.el.dom){
30879             this.mgr.el.dom.appendChild(el.dom);
30880         }
30881         if(panel.setRegion){
30882             panel.setRegion(this);
30883         }
30884         this.panels.add(panel);
30885         el.setStyle("position", "absolute");
30886         if(!panel.background){
30887             this.setActivePanel(panel);
30888             if(this.config.initialSize && this.panels.getCount()==1){
30889                 this.resizeTo(this.config.initialSize);
30890             }
30891         }
30892         this.fireEvent("paneladded", this, panel);
30893         return panel;
30894     },
30895     
30896     /**
30897      * Returns true if the panel is in this region.
30898      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30899      * @return {Boolean}
30900      */
30901     hasPanel : function(panel){
30902         if(typeof panel == "object"){ // must be panel obj
30903             panel = panel.getId();
30904         }
30905         return this.getPanel(panel) ? true : false;
30906     },
30907     
30908     /**
30909      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30910      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30911      * @param {Boolean} preservePanel Overrides the config preservePanel option
30912      * @return {Roo.ContentPanel} The panel that was removed
30913      */
30914     remove : function(panel, preservePanel){
30915         panel = this.getPanel(panel);
30916         if(!panel){
30917             return null;
30918         }
30919         var e = {};
30920         this.fireEvent("beforeremove", this, panel, e);
30921         if(e.cancel === true){
30922             return null;
30923         }
30924         var panelId = panel.getId();
30925         this.panels.removeKey(panelId);
30926         return panel;
30927     },
30928     
30929     /**
30930      * Returns the panel specified or null if it's not in this region.
30931      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30932      * @return {Roo.ContentPanel}
30933      */
30934     getPanel : function(id){
30935         if(typeof id == "object"){ // must be panel obj
30936             return id;
30937         }
30938         return this.panels.get(id);
30939     },
30940     
30941     /**
30942      * Returns this regions position (north/south/east/west/center).
30943      * @return {String} 
30944      */
30945     getPosition: function(){
30946         return this.position;    
30947     }
30948 });/*
30949  * Based on:
30950  * Ext JS Library 1.1.1
30951  * Copyright(c) 2006-2007, Ext JS, LLC.
30952  *
30953  * Originally Released Under LGPL - original licence link has changed is not relivant.
30954  *
30955  * Fork - LGPL
30956  * <script type="text/javascript">
30957  */
30958  
30959 /**
30960  * @class Roo.LayoutRegion
30961  * @extends Roo.BasicLayoutRegion
30962  * This class represents a region in a layout manager.
30963  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30964  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30965  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30966  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30967  * @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})
30968  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
30969  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30970  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30971  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30972  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30973  * @cfg {String}    title           The title for the region (overrides panel titles)
30974  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30975  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30976  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30977  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30978  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30979  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30980  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30981  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30982  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30983  * @cfg {Boolean}   showPin         True to show a pin button
30984  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30985  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30986  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30987  * @cfg {Number}    width           For East/West panels
30988  * @cfg {Number}    height          For North/South panels
30989  * @cfg {Boolean}   split           To show the splitter
30990  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30991  */
30992 Roo.LayoutRegion = function(mgr, config, pos){
30993     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30994     var dh = Roo.DomHelper;
30995     /** This region's container element 
30996     * @type Roo.Element */
30997     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30998     /** This region's title element 
30999     * @type Roo.Element */
31000
31001     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31002         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31003         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31004     ]}, true);
31005     this.titleEl.enableDisplayMode();
31006     /** This region's title text element 
31007     * @type HTMLElement */
31008     this.titleTextEl = this.titleEl.dom.firstChild;
31009     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31010     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31011     this.closeBtn.enableDisplayMode();
31012     this.closeBtn.on("click", this.closeClicked, this);
31013     this.closeBtn.hide();
31014
31015     this.createBody(config);
31016     this.visible = true;
31017     this.collapsed = false;
31018
31019     if(config.hideWhenEmpty){
31020         this.hide();
31021         this.on("paneladded", this.validateVisibility, this);
31022         this.on("panelremoved", this.validateVisibility, this);
31023     }
31024     this.applyConfig(config);
31025 };
31026
31027 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31028
31029     createBody : function(){
31030         /** This region's body element 
31031         * @type Roo.Element */
31032         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31033     },
31034
31035     applyConfig : function(c){
31036         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31037             var dh = Roo.DomHelper;
31038             if(c.titlebar !== false){
31039                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31040                 this.collapseBtn.on("click", this.collapse, this);
31041                 this.collapseBtn.enableDisplayMode();
31042
31043                 if(c.showPin === true || this.showPin){
31044                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31045                     this.stickBtn.enableDisplayMode();
31046                     this.stickBtn.on("click", this.expand, this);
31047                     this.stickBtn.hide();
31048                 }
31049             }
31050             /** This region's collapsed element
31051             * @type Roo.Element */
31052             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31053                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31054             ]}, true);
31055             if(c.floatable !== false){
31056                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31057                this.collapsedEl.on("click", this.collapseClick, this);
31058             }
31059
31060             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31061                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31062                    id: "message", unselectable: "on", style:{"float":"left"}});
31063                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31064              }
31065             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31066             this.expandBtn.on("click", this.expand, this);
31067         }
31068         if(this.collapseBtn){
31069             this.collapseBtn.setVisible(c.collapsible == true);
31070         }
31071         this.cmargins = c.cmargins || this.cmargins ||
31072                          (this.position == "west" || this.position == "east" ?
31073                              {top: 0, left: 2, right:2, bottom: 0} :
31074                              {top: 2, left: 0, right:0, bottom: 2});
31075         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31076         this.bottomTabs = c.tabPosition != "top";
31077         this.autoScroll = c.autoScroll || false;
31078         if(this.autoScroll){
31079             this.bodyEl.setStyle("overflow", "auto");
31080         }else{
31081             this.bodyEl.setStyle("overflow", "hidden");
31082         }
31083         //if(c.titlebar !== false){
31084             if((!c.titlebar && !c.title) || c.titlebar === false){
31085                 this.titleEl.hide();
31086             }else{
31087                 this.titleEl.show();
31088                 if(c.title){
31089                     this.titleTextEl.innerHTML = c.title;
31090                 }
31091             }
31092         //}
31093         this.duration = c.duration || .30;
31094         this.slideDuration = c.slideDuration || .45;
31095         this.config = c;
31096         if(c.collapsed){
31097             this.collapse(true);
31098         }
31099         if(c.hidden){
31100             this.hide();
31101         }
31102     },
31103     /**
31104      * Returns true if this region is currently visible.
31105      * @return {Boolean}
31106      */
31107     isVisible : function(){
31108         return this.visible;
31109     },
31110
31111     /**
31112      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31113      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31114      */
31115     setCollapsedTitle : function(title){
31116         title = title || "&#160;";
31117         if(this.collapsedTitleTextEl){
31118             this.collapsedTitleTextEl.innerHTML = title;
31119         }
31120     },
31121
31122     getBox : function(){
31123         var b;
31124         if(!this.collapsed){
31125             b = this.el.getBox(false, true);
31126         }else{
31127             b = this.collapsedEl.getBox(false, true);
31128         }
31129         return b;
31130     },
31131
31132     getMargins : function(){
31133         return this.collapsed ? this.cmargins : this.margins;
31134     },
31135
31136     highlight : function(){
31137         this.el.addClass("x-layout-panel-dragover");
31138     },
31139
31140     unhighlight : function(){
31141         this.el.removeClass("x-layout-panel-dragover");
31142     },
31143
31144     updateBox : function(box){
31145         this.box = box;
31146         if(!this.collapsed){
31147             this.el.dom.style.left = box.x + "px";
31148             this.el.dom.style.top = box.y + "px";
31149             this.updateBody(box.width, box.height);
31150         }else{
31151             this.collapsedEl.dom.style.left = box.x + "px";
31152             this.collapsedEl.dom.style.top = box.y + "px";
31153             this.collapsedEl.setSize(box.width, box.height);
31154         }
31155         if(this.tabs){
31156             this.tabs.autoSizeTabs();
31157         }
31158     },
31159
31160     updateBody : function(w, h){
31161         if(w !== null){
31162             this.el.setWidth(w);
31163             w -= this.el.getBorderWidth("rl");
31164             if(this.config.adjustments){
31165                 w += this.config.adjustments[0];
31166             }
31167         }
31168         if(h !== null){
31169             this.el.setHeight(h);
31170             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31171             h -= this.el.getBorderWidth("tb");
31172             if(this.config.adjustments){
31173                 h += this.config.adjustments[1];
31174             }
31175             this.bodyEl.setHeight(h);
31176             if(this.tabs){
31177                 h = this.tabs.syncHeight(h);
31178             }
31179         }
31180         if(this.panelSize){
31181             w = w !== null ? w : this.panelSize.width;
31182             h = h !== null ? h : this.panelSize.height;
31183         }
31184         if(this.activePanel){
31185             var el = this.activePanel.getEl();
31186             w = w !== null ? w : el.getWidth();
31187             h = h !== null ? h : el.getHeight();
31188             this.panelSize = {width: w, height: h};
31189             this.activePanel.setSize(w, h);
31190         }
31191         if(Roo.isIE && this.tabs){
31192             this.tabs.el.repaint();
31193         }
31194     },
31195
31196     /**
31197      * Returns the container element for this region.
31198      * @return {Roo.Element}
31199      */
31200     getEl : function(){
31201         return this.el;
31202     },
31203
31204     /**
31205      * Hides this region.
31206      */
31207     hide : function(){
31208         if(!this.collapsed){
31209             this.el.dom.style.left = "-2000px";
31210             this.el.hide();
31211         }else{
31212             this.collapsedEl.dom.style.left = "-2000px";
31213             this.collapsedEl.hide();
31214         }
31215         this.visible = false;
31216         this.fireEvent("visibilitychange", this, false);
31217     },
31218
31219     /**
31220      * Shows this region if it was previously hidden.
31221      */
31222     show : function(){
31223         if(!this.collapsed){
31224             this.el.show();
31225         }else{
31226             this.collapsedEl.show();
31227         }
31228         this.visible = true;
31229         this.fireEvent("visibilitychange", this, true);
31230     },
31231
31232     closeClicked : function(){
31233         if(this.activePanel){
31234             this.remove(this.activePanel);
31235         }
31236     },
31237
31238     collapseClick : function(e){
31239         if(this.isSlid){
31240            e.stopPropagation();
31241            this.slideIn();
31242         }else{
31243            e.stopPropagation();
31244            this.slideOut();
31245         }
31246     },
31247
31248     /**
31249      * Collapses this region.
31250      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31251      */
31252     collapse : function(skipAnim){
31253         if(this.collapsed) return;
31254         this.collapsed = true;
31255         if(this.split){
31256             this.split.el.hide();
31257         }
31258         if(this.config.animate && skipAnim !== true){
31259             this.fireEvent("invalidated", this);
31260             this.animateCollapse();
31261         }else{
31262             this.el.setLocation(-20000,-20000);
31263             this.el.hide();
31264             this.collapsedEl.show();
31265             this.fireEvent("collapsed", this);
31266             this.fireEvent("invalidated", this);
31267         }
31268     },
31269
31270     animateCollapse : function(){
31271         // overridden
31272     },
31273
31274     /**
31275      * Expands this region if it was previously collapsed.
31276      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31277      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31278      */
31279     expand : function(e, skipAnim){
31280         if(e) e.stopPropagation();
31281         if(!this.collapsed || this.el.hasActiveFx()) return;
31282         if(this.isSlid){
31283             this.afterSlideIn();
31284             skipAnim = true;
31285         }
31286         this.collapsed = false;
31287         if(this.config.animate && skipAnim !== true){
31288             this.animateExpand();
31289         }else{
31290             this.el.show();
31291             if(this.split){
31292                 this.split.el.show();
31293             }
31294             this.collapsedEl.setLocation(-2000,-2000);
31295             this.collapsedEl.hide();
31296             this.fireEvent("invalidated", this);
31297             this.fireEvent("expanded", this);
31298         }
31299     },
31300
31301     animateExpand : function(){
31302         // overridden
31303     },
31304
31305     initTabs : function()
31306     {
31307         this.bodyEl.setStyle("overflow", "hidden");
31308         var ts = new Roo.TabPanel(
31309                 this.bodyEl.dom,
31310                 {
31311                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31312                     disableTooltips: this.config.disableTabTips,
31313                     toolbar : this.config.toolbar
31314                 }
31315         );
31316         if(this.config.hideTabs){
31317             ts.stripWrap.setDisplayed(false);
31318         }
31319         this.tabs = ts;
31320         ts.resizeTabs = this.config.resizeTabs === true;
31321         ts.minTabWidth = this.config.minTabWidth || 40;
31322         ts.maxTabWidth = this.config.maxTabWidth || 250;
31323         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31324         ts.monitorResize = false;
31325         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31326         ts.bodyEl.addClass('x-layout-tabs-body');
31327         this.panels.each(this.initPanelAsTab, this);
31328     },
31329
31330     initPanelAsTab : function(panel){
31331         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31332                     this.config.closeOnTab && panel.isClosable());
31333         if(panel.tabTip !== undefined){
31334             ti.setTooltip(panel.tabTip);
31335         }
31336         ti.on("activate", function(){
31337               this.setActivePanel(panel);
31338         }, this);
31339         if(this.config.closeOnTab){
31340             ti.on("beforeclose", function(t, e){
31341                 e.cancel = true;
31342                 this.remove(panel);
31343             }, this);
31344         }
31345         return ti;
31346     },
31347
31348     updatePanelTitle : function(panel, title){
31349         if(this.activePanel == panel){
31350             this.updateTitle(title);
31351         }
31352         if(this.tabs){
31353             var ti = this.tabs.getTab(panel.getEl().id);
31354             ti.setText(title);
31355             if(panel.tabTip !== undefined){
31356                 ti.setTooltip(panel.tabTip);
31357             }
31358         }
31359     },
31360
31361     updateTitle : function(title){
31362         if(this.titleTextEl && !this.config.title){
31363             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31364         }
31365     },
31366
31367     setActivePanel : function(panel){
31368         panel = this.getPanel(panel);
31369         if(this.activePanel && this.activePanel != panel){
31370             this.activePanel.setActiveState(false);
31371         }
31372         this.activePanel = panel;
31373         panel.setActiveState(true);
31374         if(this.panelSize){
31375             panel.setSize(this.panelSize.width, this.panelSize.height);
31376         }
31377         if(this.closeBtn){
31378             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31379         }
31380         this.updateTitle(panel.getTitle());
31381         if(this.tabs){
31382             this.fireEvent("invalidated", this);
31383         }
31384         this.fireEvent("panelactivated", this, panel);
31385     },
31386
31387     /**
31388      * Shows the specified panel.
31389      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31390      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31391      */
31392     showPanel : function(panel){
31393         if(panel = this.getPanel(panel)){
31394             if(this.tabs){
31395                 var tab = this.tabs.getTab(panel.getEl().id);
31396                 if(tab.isHidden()){
31397                     this.tabs.unhideTab(tab.id);
31398                 }
31399                 tab.activate();
31400             }else{
31401                 this.setActivePanel(panel);
31402             }
31403         }
31404         return panel;
31405     },
31406
31407     /**
31408      * Get the active panel for this region.
31409      * @return {Roo.ContentPanel} The active panel or null
31410      */
31411     getActivePanel : function(){
31412         return this.activePanel;
31413     },
31414
31415     validateVisibility : function(){
31416         if(this.panels.getCount() < 1){
31417             this.updateTitle("&#160;");
31418             this.closeBtn.hide();
31419             this.hide();
31420         }else{
31421             if(!this.isVisible()){
31422                 this.show();
31423             }
31424         }
31425     },
31426
31427     /**
31428      * Adds the passed ContentPanel(s) to this region.
31429      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31430      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31431      */
31432     add : function(panel){
31433         if(arguments.length > 1){
31434             for(var i = 0, len = arguments.length; i < len; i++) {
31435                 this.add(arguments[i]);
31436             }
31437             return null;
31438         }
31439         if(this.hasPanel(panel)){
31440             this.showPanel(panel);
31441             return panel;
31442         }
31443         panel.setRegion(this);
31444         this.panels.add(panel);
31445         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31446             this.bodyEl.dom.appendChild(panel.getEl().dom);
31447             if(panel.background !== true){
31448                 this.setActivePanel(panel);
31449             }
31450             this.fireEvent("paneladded", this, panel);
31451             return panel;
31452         }
31453         if(!this.tabs){
31454             this.initTabs();
31455         }else{
31456             this.initPanelAsTab(panel);
31457         }
31458         if(panel.background !== true){
31459             this.tabs.activate(panel.getEl().id);
31460         }
31461         this.fireEvent("paneladded", this, panel);
31462         return panel;
31463     },
31464
31465     /**
31466      * Hides the tab for the specified panel.
31467      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31468      */
31469     hidePanel : function(panel){
31470         if(this.tabs && (panel = this.getPanel(panel))){
31471             this.tabs.hideTab(panel.getEl().id);
31472         }
31473     },
31474
31475     /**
31476      * Unhides the tab for a previously hidden panel.
31477      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31478      */
31479     unhidePanel : function(panel){
31480         if(this.tabs && (panel = this.getPanel(panel))){
31481             this.tabs.unhideTab(panel.getEl().id);
31482         }
31483     },
31484
31485     clearPanels : function(){
31486         while(this.panels.getCount() > 0){
31487              this.remove(this.panels.first());
31488         }
31489     },
31490
31491     /**
31492      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31493      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31494      * @param {Boolean} preservePanel Overrides the config preservePanel option
31495      * @return {Roo.ContentPanel} The panel that was removed
31496      */
31497     remove : function(panel, preservePanel){
31498         panel = this.getPanel(panel);
31499         if(!panel){
31500             return null;
31501         }
31502         var e = {};
31503         this.fireEvent("beforeremove", this, panel, e);
31504         if(e.cancel === true){
31505             return null;
31506         }
31507         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31508         var panelId = panel.getId();
31509         this.panels.removeKey(panelId);
31510         if(preservePanel){
31511             document.body.appendChild(panel.getEl().dom);
31512         }
31513         if(this.tabs){
31514             this.tabs.removeTab(panel.getEl().id);
31515         }else if (!preservePanel){
31516             this.bodyEl.dom.removeChild(panel.getEl().dom);
31517         }
31518         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
31519             var p = this.panels.first();
31520             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
31521             tempEl.appendChild(p.getEl().dom);
31522             this.bodyEl.update("");
31523             this.bodyEl.dom.appendChild(p.getEl().dom);
31524             tempEl = null;
31525             this.updateTitle(p.getTitle());
31526             this.tabs = null;
31527             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31528             this.setActivePanel(p);
31529         }
31530         panel.setRegion(null);
31531         if(this.activePanel == panel){
31532             this.activePanel = null;
31533         }
31534         if(this.config.autoDestroy !== false && preservePanel !== true){
31535             try{panel.destroy();}catch(e){}
31536         }
31537         this.fireEvent("panelremoved", this, panel);
31538         return panel;
31539     },
31540
31541     /**
31542      * Returns the TabPanel component used by this region
31543      * @return {Roo.TabPanel}
31544      */
31545     getTabs : function(){
31546         return this.tabs;
31547     },
31548
31549     createTool : function(parentEl, className){
31550         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
31551             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
31552         btn.addClassOnOver("x-layout-tools-button-over");
31553         return btn;
31554     }
31555 });/*
31556  * Based on:
31557  * Ext JS Library 1.1.1
31558  * Copyright(c) 2006-2007, Ext JS, LLC.
31559  *
31560  * Originally Released Under LGPL - original licence link has changed is not relivant.
31561  *
31562  * Fork - LGPL
31563  * <script type="text/javascript">
31564  */
31565  
31566
31567
31568 /**
31569  * @class Roo.SplitLayoutRegion
31570  * @extends Roo.LayoutRegion
31571  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
31572  */
31573 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
31574     this.cursor = cursor;
31575     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
31576 };
31577
31578 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
31579     splitTip : "Drag to resize.",
31580     collapsibleSplitTip : "Drag to resize. Double click to hide.",
31581     useSplitTips : false,
31582
31583     applyConfig : function(config){
31584         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
31585         if(config.split){
31586             if(!this.split){
31587                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
31588                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
31589                 /** The SplitBar for this region 
31590                 * @type Roo.SplitBar */
31591                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
31592                 this.split.on("moved", this.onSplitMove, this);
31593                 this.split.useShim = config.useShim === true;
31594                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
31595                 if(this.useSplitTips){
31596                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
31597                 }
31598                 if(config.collapsible){
31599                     this.split.el.on("dblclick", this.collapse,  this);
31600                 }
31601             }
31602             if(typeof config.minSize != "undefined"){
31603                 this.split.minSize = config.minSize;
31604             }
31605             if(typeof config.maxSize != "undefined"){
31606                 this.split.maxSize = config.maxSize;
31607             }
31608             if(config.hideWhenEmpty || config.hidden || config.collapsed){
31609                 this.hideSplitter();
31610             }
31611         }
31612     },
31613
31614     getHMaxSize : function(){
31615          var cmax = this.config.maxSize || 10000;
31616          var center = this.mgr.getRegion("center");
31617          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
31618     },
31619
31620     getVMaxSize : function(){
31621          var cmax = this.config.maxSize || 10000;
31622          var center = this.mgr.getRegion("center");
31623          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
31624     },
31625
31626     onSplitMove : function(split, newSize){
31627         this.fireEvent("resized", this, newSize);
31628     },
31629     
31630     /** 
31631      * Returns the {@link Roo.SplitBar} for this region.
31632      * @return {Roo.SplitBar}
31633      */
31634     getSplitBar : function(){
31635         return this.split;
31636     },
31637     
31638     hide : function(){
31639         this.hideSplitter();
31640         Roo.SplitLayoutRegion.superclass.hide.call(this);
31641     },
31642
31643     hideSplitter : function(){
31644         if(this.split){
31645             this.split.el.setLocation(-2000,-2000);
31646             this.split.el.hide();
31647         }
31648     },
31649
31650     show : function(){
31651         if(this.split){
31652             this.split.el.show();
31653         }
31654         Roo.SplitLayoutRegion.superclass.show.call(this);
31655     },
31656     
31657     beforeSlide: function(){
31658         if(Roo.isGecko){// firefox overflow auto bug workaround
31659             this.bodyEl.clip();
31660             if(this.tabs) this.tabs.bodyEl.clip();
31661             if(this.activePanel){
31662                 this.activePanel.getEl().clip();
31663                 
31664                 if(this.activePanel.beforeSlide){
31665                     this.activePanel.beforeSlide();
31666                 }
31667             }
31668         }
31669     },
31670     
31671     afterSlide : function(){
31672         if(Roo.isGecko){// firefox overflow auto bug workaround
31673             this.bodyEl.unclip();
31674             if(this.tabs) this.tabs.bodyEl.unclip();
31675             if(this.activePanel){
31676                 this.activePanel.getEl().unclip();
31677                 if(this.activePanel.afterSlide){
31678                     this.activePanel.afterSlide();
31679                 }
31680             }
31681         }
31682     },
31683
31684     initAutoHide : function(){
31685         if(this.autoHide !== false){
31686             if(!this.autoHideHd){
31687                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31688                 this.autoHideHd = {
31689                     "mouseout": function(e){
31690                         if(!e.within(this.el, true)){
31691                             st.delay(500);
31692                         }
31693                     },
31694                     "mouseover" : function(e){
31695                         st.cancel();
31696                     },
31697                     scope : this
31698                 };
31699             }
31700             this.el.on(this.autoHideHd);
31701         }
31702     },
31703
31704     clearAutoHide : function(){
31705         if(this.autoHide !== false){
31706             this.el.un("mouseout", this.autoHideHd.mouseout);
31707             this.el.un("mouseover", this.autoHideHd.mouseover);
31708         }
31709     },
31710
31711     clearMonitor : function(){
31712         Roo.get(document).un("click", this.slideInIf, this);
31713     },
31714
31715     // these names are backwards but not changed for compat
31716     slideOut : function(){
31717         if(this.isSlid || this.el.hasActiveFx()){
31718             return;
31719         }
31720         this.isSlid = true;
31721         if(this.collapseBtn){
31722             this.collapseBtn.hide();
31723         }
31724         this.closeBtnState = this.closeBtn.getStyle('display');
31725         this.closeBtn.hide();
31726         if(this.stickBtn){
31727             this.stickBtn.show();
31728         }
31729         this.el.show();
31730         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31731         this.beforeSlide();
31732         this.el.setStyle("z-index", 10001);
31733         this.el.slideIn(this.getSlideAnchor(), {
31734             callback: function(){
31735                 this.afterSlide();
31736                 this.initAutoHide();
31737                 Roo.get(document).on("click", this.slideInIf, this);
31738                 this.fireEvent("slideshow", this);
31739             },
31740             scope: this,
31741             block: true
31742         });
31743     },
31744
31745     afterSlideIn : function(){
31746         this.clearAutoHide();
31747         this.isSlid = false;
31748         this.clearMonitor();
31749         this.el.setStyle("z-index", "");
31750         if(this.collapseBtn){
31751             this.collapseBtn.show();
31752         }
31753         this.closeBtn.setStyle('display', this.closeBtnState);
31754         if(this.stickBtn){
31755             this.stickBtn.hide();
31756         }
31757         this.fireEvent("slidehide", this);
31758     },
31759
31760     slideIn : function(cb){
31761         if(!this.isSlid || this.el.hasActiveFx()){
31762             Roo.callback(cb);
31763             return;
31764         }
31765         this.isSlid = false;
31766         this.beforeSlide();
31767         this.el.slideOut(this.getSlideAnchor(), {
31768             callback: function(){
31769                 this.el.setLeftTop(-10000, -10000);
31770                 this.afterSlide();
31771                 this.afterSlideIn();
31772                 Roo.callback(cb);
31773             },
31774             scope: this,
31775             block: true
31776         });
31777     },
31778     
31779     slideInIf : function(e){
31780         if(!e.within(this.el)){
31781             this.slideIn();
31782         }
31783     },
31784
31785     animateCollapse : function(){
31786         this.beforeSlide();
31787         this.el.setStyle("z-index", 20000);
31788         var anchor = this.getSlideAnchor();
31789         this.el.slideOut(anchor, {
31790             callback : function(){
31791                 this.el.setStyle("z-index", "");
31792                 this.collapsedEl.slideIn(anchor, {duration:.3});
31793                 this.afterSlide();
31794                 this.el.setLocation(-10000,-10000);
31795                 this.el.hide();
31796                 this.fireEvent("collapsed", this);
31797             },
31798             scope: this,
31799             block: true
31800         });
31801     },
31802
31803     animateExpand : function(){
31804         this.beforeSlide();
31805         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31806         this.el.setStyle("z-index", 20000);
31807         this.collapsedEl.hide({
31808             duration:.1
31809         });
31810         this.el.slideIn(this.getSlideAnchor(), {
31811             callback : function(){
31812                 this.el.setStyle("z-index", "");
31813                 this.afterSlide();
31814                 if(this.split){
31815                     this.split.el.show();
31816                 }
31817                 this.fireEvent("invalidated", this);
31818                 this.fireEvent("expanded", this);
31819             },
31820             scope: this,
31821             block: true
31822         });
31823     },
31824
31825     anchors : {
31826         "west" : "left",
31827         "east" : "right",
31828         "north" : "top",
31829         "south" : "bottom"
31830     },
31831
31832     sanchors : {
31833         "west" : "l",
31834         "east" : "r",
31835         "north" : "t",
31836         "south" : "b"
31837     },
31838
31839     canchors : {
31840         "west" : "tl-tr",
31841         "east" : "tr-tl",
31842         "north" : "tl-bl",
31843         "south" : "bl-tl"
31844     },
31845
31846     getAnchor : function(){
31847         return this.anchors[this.position];
31848     },
31849
31850     getCollapseAnchor : function(){
31851         return this.canchors[this.position];
31852     },
31853
31854     getSlideAnchor : function(){
31855         return this.sanchors[this.position];
31856     },
31857
31858     getAlignAdj : function(){
31859         var cm = this.cmargins;
31860         switch(this.position){
31861             case "west":
31862                 return [0, 0];
31863             break;
31864             case "east":
31865                 return [0, 0];
31866             break;
31867             case "north":
31868                 return [0, 0];
31869             break;
31870             case "south":
31871                 return [0, 0];
31872             break;
31873         }
31874     },
31875
31876     getExpandAdj : function(){
31877         var c = this.collapsedEl, cm = this.cmargins;
31878         switch(this.position){
31879             case "west":
31880                 return [-(cm.right+c.getWidth()+cm.left), 0];
31881             break;
31882             case "east":
31883                 return [cm.right+c.getWidth()+cm.left, 0];
31884             break;
31885             case "north":
31886                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31887             break;
31888             case "south":
31889                 return [0, cm.top+cm.bottom+c.getHeight()];
31890             break;
31891         }
31892     }
31893 });/*
31894  * Based on:
31895  * Ext JS Library 1.1.1
31896  * Copyright(c) 2006-2007, Ext JS, LLC.
31897  *
31898  * Originally Released Under LGPL - original licence link has changed is not relivant.
31899  *
31900  * Fork - LGPL
31901  * <script type="text/javascript">
31902  */
31903 /*
31904  * These classes are private internal classes
31905  */
31906 Roo.CenterLayoutRegion = function(mgr, config){
31907     Roo.LayoutRegion.call(this, mgr, config, "center");
31908     this.visible = true;
31909     this.minWidth = config.minWidth || 20;
31910     this.minHeight = config.minHeight || 20;
31911 };
31912
31913 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31914     hide : function(){
31915         // center panel can't be hidden
31916     },
31917     
31918     show : function(){
31919         // center panel can't be hidden
31920     },
31921     
31922     getMinWidth: function(){
31923         return this.minWidth;
31924     },
31925     
31926     getMinHeight: function(){
31927         return this.minHeight;
31928     }
31929 });
31930
31931
31932 Roo.NorthLayoutRegion = function(mgr, config){
31933     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31934     if(this.split){
31935         this.split.placement = Roo.SplitBar.TOP;
31936         this.split.orientation = Roo.SplitBar.VERTICAL;
31937         this.split.el.addClass("x-layout-split-v");
31938     }
31939     var size = config.initialSize || config.height;
31940     if(typeof size != "undefined"){
31941         this.el.setHeight(size);
31942     }
31943 };
31944 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31945     orientation: Roo.SplitBar.VERTICAL,
31946     getBox : function(){
31947         if(this.collapsed){
31948             return this.collapsedEl.getBox();
31949         }
31950         var box = this.el.getBox();
31951         if(this.split){
31952             box.height += this.split.el.getHeight();
31953         }
31954         return box;
31955     },
31956     
31957     updateBox : function(box){
31958         if(this.split && !this.collapsed){
31959             box.height -= this.split.el.getHeight();
31960             this.split.el.setLeft(box.x);
31961             this.split.el.setTop(box.y+box.height);
31962             this.split.el.setWidth(box.width);
31963         }
31964         if(this.collapsed){
31965             this.updateBody(box.width, null);
31966         }
31967         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31968     }
31969 });
31970
31971 Roo.SouthLayoutRegion = function(mgr, config){
31972     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31973     if(this.split){
31974         this.split.placement = Roo.SplitBar.BOTTOM;
31975         this.split.orientation = Roo.SplitBar.VERTICAL;
31976         this.split.el.addClass("x-layout-split-v");
31977     }
31978     var size = config.initialSize || config.height;
31979     if(typeof size != "undefined"){
31980         this.el.setHeight(size);
31981     }
31982 };
31983 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31984     orientation: Roo.SplitBar.VERTICAL,
31985     getBox : function(){
31986         if(this.collapsed){
31987             return this.collapsedEl.getBox();
31988         }
31989         var box = this.el.getBox();
31990         if(this.split){
31991             var sh = this.split.el.getHeight();
31992             box.height += sh;
31993             box.y -= sh;
31994         }
31995         return box;
31996     },
31997     
31998     updateBox : function(box){
31999         if(this.split && !this.collapsed){
32000             var sh = this.split.el.getHeight();
32001             box.height -= sh;
32002             box.y += sh;
32003             this.split.el.setLeft(box.x);
32004             this.split.el.setTop(box.y-sh);
32005             this.split.el.setWidth(box.width);
32006         }
32007         if(this.collapsed){
32008             this.updateBody(box.width, null);
32009         }
32010         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32011     }
32012 });
32013
32014 Roo.EastLayoutRegion = function(mgr, config){
32015     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32016     if(this.split){
32017         this.split.placement = Roo.SplitBar.RIGHT;
32018         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32019         this.split.el.addClass("x-layout-split-h");
32020     }
32021     var size = config.initialSize || config.width;
32022     if(typeof size != "undefined"){
32023         this.el.setWidth(size);
32024     }
32025 };
32026 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32027     orientation: Roo.SplitBar.HORIZONTAL,
32028     getBox : function(){
32029         if(this.collapsed){
32030             return this.collapsedEl.getBox();
32031         }
32032         var box = this.el.getBox();
32033         if(this.split){
32034             var sw = this.split.el.getWidth();
32035             box.width += sw;
32036             box.x -= sw;
32037         }
32038         return box;
32039     },
32040
32041     updateBox : function(box){
32042         if(this.split && !this.collapsed){
32043             var sw = this.split.el.getWidth();
32044             box.width -= sw;
32045             this.split.el.setLeft(box.x);
32046             this.split.el.setTop(box.y);
32047             this.split.el.setHeight(box.height);
32048             box.x += sw;
32049         }
32050         if(this.collapsed){
32051             this.updateBody(null, box.height);
32052         }
32053         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32054     }
32055 });
32056
32057 Roo.WestLayoutRegion = function(mgr, config){
32058     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32059     if(this.split){
32060         this.split.placement = Roo.SplitBar.LEFT;
32061         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32062         this.split.el.addClass("x-layout-split-h");
32063     }
32064     var size = config.initialSize || config.width;
32065     if(typeof size != "undefined"){
32066         this.el.setWidth(size);
32067     }
32068 };
32069 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32070     orientation: Roo.SplitBar.HORIZONTAL,
32071     getBox : function(){
32072         if(this.collapsed){
32073             return this.collapsedEl.getBox();
32074         }
32075         var box = this.el.getBox();
32076         if(this.split){
32077             box.width += this.split.el.getWidth();
32078         }
32079         return box;
32080     },
32081     
32082     updateBox : function(box){
32083         if(this.split && !this.collapsed){
32084             var sw = this.split.el.getWidth();
32085             box.width -= sw;
32086             this.split.el.setLeft(box.x+box.width);
32087             this.split.el.setTop(box.y);
32088             this.split.el.setHeight(box.height);
32089         }
32090         if(this.collapsed){
32091             this.updateBody(null, box.height);
32092         }
32093         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32094     }
32095 });
32096 /*
32097  * Based on:
32098  * Ext JS Library 1.1.1
32099  * Copyright(c) 2006-2007, Ext JS, LLC.
32100  *
32101  * Originally Released Under LGPL - original licence link has changed is not relivant.
32102  *
32103  * Fork - LGPL
32104  * <script type="text/javascript">
32105  */
32106  
32107  
32108 /*
32109  * Private internal class for reading and applying state
32110  */
32111 Roo.LayoutStateManager = function(layout){
32112      // default empty state
32113      this.state = {
32114         north: {},
32115         south: {},
32116         east: {},
32117         west: {}       
32118     };
32119 };
32120
32121 Roo.LayoutStateManager.prototype = {
32122     init : function(layout, provider){
32123         this.provider = provider;
32124         var state = provider.get(layout.id+"-layout-state");
32125         if(state){
32126             var wasUpdating = layout.isUpdating();
32127             if(!wasUpdating){
32128                 layout.beginUpdate();
32129             }
32130             for(var key in state){
32131                 if(typeof state[key] != "function"){
32132                     var rstate = state[key];
32133                     var r = layout.getRegion(key);
32134                     if(r && rstate){
32135                         if(rstate.size){
32136                             r.resizeTo(rstate.size);
32137                         }
32138                         if(rstate.collapsed == true){
32139                             r.collapse(true);
32140                         }else{
32141                             r.expand(null, true);
32142                         }
32143                     }
32144                 }
32145             }
32146             if(!wasUpdating){
32147                 layout.endUpdate();
32148             }
32149             this.state = state; 
32150         }
32151         this.layout = layout;
32152         layout.on("regionresized", this.onRegionResized, this);
32153         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32154         layout.on("regionexpanded", this.onRegionExpanded, this);
32155     },
32156     
32157     storeState : function(){
32158         this.provider.set(this.layout.id+"-layout-state", this.state);
32159     },
32160     
32161     onRegionResized : function(region, newSize){
32162         this.state[region.getPosition()].size = newSize;
32163         this.storeState();
32164     },
32165     
32166     onRegionCollapsed : function(region){
32167         this.state[region.getPosition()].collapsed = true;
32168         this.storeState();
32169     },
32170     
32171     onRegionExpanded : function(region){
32172         this.state[region.getPosition()].collapsed = false;
32173         this.storeState();
32174     }
32175 };/*
32176  * Based on:
32177  * Ext JS Library 1.1.1
32178  * Copyright(c) 2006-2007, Ext JS, LLC.
32179  *
32180  * Originally Released Under LGPL - original licence link has changed is not relivant.
32181  *
32182  * Fork - LGPL
32183  * <script type="text/javascript">
32184  */
32185 /**
32186  * @class Roo.ContentPanel
32187  * @extends Roo.util.Observable
32188  * A basic ContentPanel element.
32189  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32190  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32191  * @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
32192  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32193  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32194  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32195  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32196  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32197  * @cfg {String} title          The title for this panel
32198  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32199  * @cfg {String} url            Calls {@link #setUrl} with this value
32200  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32201  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32202  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32203  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32204
32205  * @constructor
32206  * Create a new ContentPanel.
32207  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32208  * @param {String/Object} config A string to set only the title or a config object
32209  * @param {String} content (optional) Set the HTML content for this panel
32210  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32211  */
32212 Roo.ContentPanel = function(el, config, content){
32213     
32214      
32215     /*
32216     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32217         config = el;
32218         el = Roo.id();
32219     }
32220     if (config && config.parentLayout) { 
32221         el = config.parentLayout.el.createChild(); 
32222     }
32223     */
32224     if(el.autoCreate){ // xtype is available if this is called from factory
32225         config = el;
32226         el = Roo.id();
32227     }
32228     this.el = Roo.get(el);
32229     if(!this.el && config && config.autoCreate){
32230         if(typeof config.autoCreate == "object"){
32231             if(!config.autoCreate.id){
32232                 config.autoCreate.id = config.id||el;
32233             }
32234             this.el = Roo.DomHelper.append(document.body,
32235                         config.autoCreate, true);
32236         }else{
32237             this.el = Roo.DomHelper.append(document.body,
32238                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32239         }
32240     }
32241     this.closable = false;
32242     this.loaded = false;
32243     this.active = false;
32244     if(typeof config == "string"){
32245         this.title = config;
32246     }else{
32247         Roo.apply(this, config);
32248     }
32249     
32250     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32251         this.wrapEl = this.el.wrap();
32252         this.toolbar.container = this.el.insertSibling(false, 'before');
32253         this.toolbar = new Roo.Toolbar(this.toolbar);
32254     }
32255     
32256     
32257     
32258     if(this.resizeEl){
32259         this.resizeEl = Roo.get(this.resizeEl, true);
32260     }else{
32261         this.resizeEl = this.el;
32262     }
32263     this.addEvents({
32264         /**
32265          * @event activate
32266          * Fires when this panel is activated. 
32267          * @param {Roo.ContentPanel} this
32268          */
32269         "activate" : true,
32270         /**
32271          * @event deactivate
32272          * Fires when this panel is activated. 
32273          * @param {Roo.ContentPanel} this
32274          */
32275         "deactivate" : true,
32276
32277         /**
32278          * @event resize
32279          * Fires when this panel is resized if fitToFrame is true.
32280          * @param {Roo.ContentPanel} this
32281          * @param {Number} width The width after any component adjustments
32282          * @param {Number} height The height after any component adjustments
32283          */
32284         "resize" : true,
32285         
32286          /**
32287          * @event render
32288          * Fires when this tab is created
32289          * @param {Roo.ContentPanel} this
32290          */
32291         "render" : true
32292         
32293         
32294         
32295     });
32296     if(this.autoScroll){
32297         this.resizeEl.setStyle("overflow", "auto");
32298     } else {
32299         // fix randome scrolling
32300         this.el.on('scroll', function() {
32301             Roo.log('fix random scolling');
32302             this.scrollTo('top',0); 
32303         });
32304     }
32305     content = content || this.content;
32306     if(content){
32307         this.setContent(content);
32308     }
32309     if(config && config.url){
32310         this.setUrl(this.url, this.params, this.loadOnce);
32311     }
32312     
32313     
32314     
32315     Roo.ContentPanel.superclass.constructor.call(this);
32316     
32317     this.fireEvent('render', this);
32318 };
32319
32320 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32321     tabTip:'',
32322     setRegion : function(region){
32323         this.region = region;
32324         if(region){
32325            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32326         }else{
32327            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32328         } 
32329     },
32330     
32331     /**
32332      * Returns the toolbar for this Panel if one was configured. 
32333      * @return {Roo.Toolbar} 
32334      */
32335     getToolbar : function(){
32336         return this.toolbar;
32337     },
32338     
32339     setActiveState : function(active){
32340         this.active = active;
32341         if(!active){
32342             this.fireEvent("deactivate", this);
32343         }else{
32344             this.fireEvent("activate", this);
32345         }
32346     },
32347     /**
32348      * Updates this panel's element
32349      * @param {String} content The new content
32350      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32351     */
32352     setContent : function(content, loadScripts){
32353         this.el.update(content, loadScripts);
32354     },
32355
32356     ignoreResize : function(w, h){
32357         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32358             return true;
32359         }else{
32360             this.lastSize = {width: w, height: h};
32361             return false;
32362         }
32363     },
32364     /**
32365      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32366      * @return {Roo.UpdateManager} The UpdateManager
32367      */
32368     getUpdateManager : function(){
32369         return this.el.getUpdateManager();
32370     },
32371      /**
32372      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32373      * @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:
32374 <pre><code>
32375 panel.load({
32376     url: "your-url.php",
32377     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32378     callback: yourFunction,
32379     scope: yourObject, //(optional scope)
32380     discardUrl: false,
32381     nocache: false,
32382     text: "Loading...",
32383     timeout: 30,
32384     scripts: false
32385 });
32386 </code></pre>
32387      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32388      * 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.
32389      * @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}
32390      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32391      * @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.
32392      * @return {Roo.ContentPanel} this
32393      */
32394     load : function(){
32395         var um = this.el.getUpdateManager();
32396         um.update.apply(um, arguments);
32397         return this;
32398     },
32399
32400
32401     /**
32402      * 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.
32403      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32404      * @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)
32405      * @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)
32406      * @return {Roo.UpdateManager} The UpdateManager
32407      */
32408     setUrl : function(url, params, loadOnce){
32409         if(this.refreshDelegate){
32410             this.removeListener("activate", this.refreshDelegate);
32411         }
32412         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32413         this.on("activate", this.refreshDelegate);
32414         return this.el.getUpdateManager();
32415     },
32416     
32417     _handleRefresh : function(url, params, loadOnce){
32418         if(!loadOnce || !this.loaded){
32419             var updater = this.el.getUpdateManager();
32420             updater.update(url, params, this._setLoaded.createDelegate(this));
32421         }
32422     },
32423     
32424     _setLoaded : function(){
32425         this.loaded = true;
32426     }, 
32427     
32428     /**
32429      * Returns this panel's id
32430      * @return {String} 
32431      */
32432     getId : function(){
32433         return this.el.id;
32434     },
32435     
32436     /** 
32437      * Returns this panel's element - used by regiosn to add.
32438      * @return {Roo.Element} 
32439      */
32440     getEl : function(){
32441         return this.wrapEl || this.el;
32442     },
32443     
32444     adjustForComponents : function(width, height){
32445         if(this.resizeEl != this.el){
32446             width -= this.el.getFrameWidth('lr');
32447             height -= this.el.getFrameWidth('tb');
32448         }
32449         if(this.toolbar){
32450             var te = this.toolbar.getEl();
32451             height -= te.getHeight();
32452             te.setWidth(width);
32453         }
32454         if(this.adjustments){
32455             width += this.adjustments[0];
32456             height += this.adjustments[1];
32457         }
32458         return {"width": width, "height": height};
32459     },
32460     
32461     setSize : function(width, height){
32462         if(this.fitToFrame && !this.ignoreResize(width, height)){
32463             if(this.fitContainer && this.resizeEl != this.el){
32464                 this.el.setSize(width, height);
32465             }
32466             var size = this.adjustForComponents(width, height);
32467             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32468             this.fireEvent('resize', this, size.width, size.height);
32469         }
32470     },
32471     
32472     /**
32473      * Returns this panel's title
32474      * @return {String} 
32475      */
32476     getTitle : function(){
32477         return this.title;
32478     },
32479     
32480     /**
32481      * Set this panel's title
32482      * @param {String} title
32483      */
32484     setTitle : function(title){
32485         this.title = title;
32486         if(this.region){
32487             this.region.updatePanelTitle(this, title);
32488         }
32489     },
32490     
32491     /**
32492      * Returns true is this panel was configured to be closable
32493      * @return {Boolean} 
32494      */
32495     isClosable : function(){
32496         return this.closable;
32497     },
32498     
32499     beforeSlide : function(){
32500         this.el.clip();
32501         this.resizeEl.clip();
32502     },
32503     
32504     afterSlide : function(){
32505         this.el.unclip();
32506         this.resizeEl.unclip();
32507     },
32508     
32509     /**
32510      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
32511      *   Will fail silently if the {@link #setUrl} method has not been called.
32512      *   This does not activate the panel, just updates its content.
32513      */
32514     refresh : function(){
32515         if(this.refreshDelegate){
32516            this.loaded = false;
32517            this.refreshDelegate();
32518         }
32519     },
32520     
32521     /**
32522      * Destroys this panel
32523      */
32524     destroy : function(){
32525         this.el.removeAllListeners();
32526         var tempEl = document.createElement("span");
32527         tempEl.appendChild(this.el.dom);
32528         tempEl.innerHTML = "";
32529         this.el.remove();
32530         this.el = null;
32531     },
32532     
32533     /**
32534      * form - if the content panel contains a form - this is a reference to it.
32535      * @type {Roo.form.Form}
32536      */
32537     form : false,
32538     /**
32539      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
32540      *    This contains a reference to it.
32541      * @type {Roo.View}
32542      */
32543     view : false,
32544     
32545       /**
32546      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
32547      * <pre><code>
32548
32549 layout.addxtype({
32550        xtype : 'Form',
32551        items: [ .... ]
32552    }
32553 );
32554
32555 </code></pre>
32556      * @param {Object} cfg Xtype definition of item to add.
32557      */
32558     
32559     addxtype : function(cfg) {
32560         // add form..
32561         if (cfg.xtype.match(/^Form$/)) {
32562             var el = this.el.createChild();
32563
32564             this.form = new  Roo.form.Form(cfg);
32565             
32566             
32567             if ( this.form.allItems.length) this.form.render(el.dom);
32568             return this.form;
32569         }
32570         // should only have one of theses..
32571         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
32572             // views..
32573             cfg.el = this.el.appendChild(document.createElement("div"));
32574             // factory?
32575             
32576             var ret = new Roo.factory(cfg);
32577             ret.render && ret.render(false, ''); // render blank..
32578             this.view = ret;
32579             return ret;
32580         }
32581         return false;
32582     }
32583 });
32584
32585 /**
32586  * @class Roo.GridPanel
32587  * @extends Roo.ContentPanel
32588  * @constructor
32589  * Create a new GridPanel.
32590  * @param {Roo.grid.Grid} grid The grid for this panel
32591  * @param {String/Object} config A string to set only the panel's title, or a config object
32592  */
32593 Roo.GridPanel = function(grid, config){
32594     
32595   
32596     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32597         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32598         
32599     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32600     
32601     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32602     
32603     if(this.toolbar){
32604         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32605     }
32606     // xtype created footer. - not sure if will work as we normally have to render first..
32607     if (this.footer && !this.footer.el && this.footer.xtype) {
32608         
32609         this.footer.container = this.grid.getView().getFooterPanel(true);
32610         this.footer.dataSource = this.grid.dataSource;
32611         this.footer = Roo.factory(this.footer, Roo);
32612         
32613     }
32614     
32615     grid.monitorWindowResize = false; // turn off autosizing
32616     grid.autoHeight = false;
32617     grid.autoWidth = false;
32618     this.grid = grid;
32619     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32620 };
32621
32622 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32623     getId : function(){
32624         return this.grid.id;
32625     },
32626     
32627     /**
32628      * Returns the grid for this panel
32629      * @return {Roo.grid.Grid} 
32630      */
32631     getGrid : function(){
32632         return this.grid;    
32633     },
32634     
32635     setSize : function(width, height){
32636         if(!this.ignoreResize(width, height)){
32637             var grid = this.grid;
32638             var size = this.adjustForComponents(width, height);
32639             grid.getGridEl().setSize(size.width, size.height);
32640             grid.autoSize();
32641         }
32642     },
32643     
32644     beforeSlide : function(){
32645         this.grid.getView().scroller.clip();
32646     },
32647     
32648     afterSlide : function(){
32649         this.grid.getView().scroller.unclip();
32650     },
32651     
32652     destroy : function(){
32653         this.grid.destroy();
32654         delete this.grid;
32655         Roo.GridPanel.superclass.destroy.call(this); 
32656     }
32657 });
32658
32659
32660 /**
32661  * @class Roo.NestedLayoutPanel
32662  * @extends Roo.ContentPanel
32663  * @constructor
32664  * Create a new NestedLayoutPanel.
32665  * 
32666  * 
32667  * @param {Roo.BorderLayout} layout The layout for this panel
32668  * @param {String/Object} config A string to set only the title or a config object
32669  */
32670 Roo.NestedLayoutPanel = function(layout, config)
32671 {
32672     // construct with only one argument..
32673     /* FIXME - implement nicer consturctors
32674     if (layout.layout) {
32675         config = layout;
32676         layout = config.layout;
32677         delete config.layout;
32678     }
32679     if (layout.xtype && !layout.getEl) {
32680         // then layout needs constructing..
32681         layout = Roo.factory(layout, Roo);
32682     }
32683     */
32684     
32685     
32686     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32687     
32688     layout.monitorWindowResize = false; // turn off autosizing
32689     this.layout = layout;
32690     this.layout.getEl().addClass("x-layout-nested-layout");
32691     
32692     
32693     
32694     
32695 };
32696
32697 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32698
32699     setSize : function(width, height){
32700         if(!this.ignoreResize(width, height)){
32701             var size = this.adjustForComponents(width, height);
32702             var el = this.layout.getEl();
32703             el.setSize(size.width, size.height);
32704             var touch = el.dom.offsetWidth;
32705             this.layout.layout();
32706             // ie requires a double layout on the first pass
32707             if(Roo.isIE && !this.initialized){
32708                 this.initialized = true;
32709                 this.layout.layout();
32710             }
32711         }
32712     },
32713     
32714     // activate all subpanels if not currently active..
32715     
32716     setActiveState : function(active){
32717         this.active = active;
32718         if(!active){
32719             this.fireEvent("deactivate", this);
32720             return;
32721         }
32722         
32723         this.fireEvent("activate", this);
32724         // not sure if this should happen before or after..
32725         if (!this.layout) {
32726             return; // should not happen..
32727         }
32728         var reg = false;
32729         for (var r in this.layout.regions) {
32730             reg = this.layout.getRegion(r);
32731             if (reg.getActivePanel()) {
32732                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32733                 reg.setActivePanel(reg.getActivePanel());
32734                 continue;
32735             }
32736             if (!reg.panels.length) {
32737                 continue;
32738             }
32739             reg.showPanel(reg.getPanel(0));
32740         }
32741         
32742         
32743         
32744         
32745     },
32746     
32747     /**
32748      * Returns the nested BorderLayout for this panel
32749      * @return {Roo.BorderLayout} 
32750      */
32751     getLayout : function(){
32752         return this.layout;
32753     },
32754     
32755      /**
32756      * Adds a xtype elements to the layout of the nested panel
32757      * <pre><code>
32758
32759 panel.addxtype({
32760        xtype : 'ContentPanel',
32761        region: 'west',
32762        items: [ .... ]
32763    }
32764 );
32765
32766 panel.addxtype({
32767         xtype : 'NestedLayoutPanel',
32768         region: 'west',
32769         layout: {
32770            center: { },
32771            west: { }   
32772         },
32773         items : [ ... list of content panels or nested layout panels.. ]
32774    }
32775 );
32776 </code></pre>
32777      * @param {Object} cfg Xtype definition of item to add.
32778      */
32779     addxtype : function(cfg) {
32780         return this.layout.addxtype(cfg);
32781     
32782     }
32783 });
32784
32785 Roo.ScrollPanel = function(el, config, content){
32786     config = config || {};
32787     config.fitToFrame = true;
32788     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32789     
32790     this.el.dom.style.overflow = "hidden";
32791     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32792     this.el.removeClass("x-layout-inactive-content");
32793     this.el.on("mousewheel", this.onWheel, this);
32794
32795     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32796     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32797     up.unselectable(); down.unselectable();
32798     up.on("click", this.scrollUp, this);
32799     down.on("click", this.scrollDown, this);
32800     up.addClassOnOver("x-scroller-btn-over");
32801     down.addClassOnOver("x-scroller-btn-over");
32802     up.addClassOnClick("x-scroller-btn-click");
32803     down.addClassOnClick("x-scroller-btn-click");
32804     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32805
32806     this.resizeEl = this.el;
32807     this.el = wrap; this.up = up; this.down = down;
32808 };
32809
32810 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32811     increment : 100,
32812     wheelIncrement : 5,
32813     scrollUp : function(){
32814         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32815     },
32816
32817     scrollDown : function(){
32818         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32819     },
32820
32821     afterScroll : function(){
32822         var el = this.resizeEl;
32823         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32824         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32825         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32826     },
32827
32828     setSize : function(){
32829         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32830         this.afterScroll();
32831     },
32832
32833     onWheel : function(e){
32834         var d = e.getWheelDelta();
32835         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32836         this.afterScroll();
32837         e.stopEvent();
32838     },
32839
32840     setContent : function(content, loadScripts){
32841         this.resizeEl.update(content, loadScripts);
32842     }
32843
32844 });
32845
32846
32847
32848
32849
32850
32851
32852
32853
32854 /**
32855  * @class Roo.TreePanel
32856  * @extends Roo.ContentPanel
32857  * @constructor
32858  * Create a new TreePanel. - defaults to fit/scoll contents.
32859  * @param {String/Object} config A string to set only the panel's title, or a config object
32860  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32861  */
32862 Roo.TreePanel = function(config){
32863     var el = config.el;
32864     var tree = config.tree;
32865     delete config.tree; 
32866     delete config.el; // hopefull!
32867     
32868     // wrapper for IE7 strict & safari scroll issue
32869     
32870     var treeEl = el.createChild();
32871     config.resizeEl = treeEl;
32872     
32873     
32874     
32875     Roo.TreePanel.superclass.constructor.call(this, el, config);
32876  
32877  
32878     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32879     //console.log(tree);
32880     this.on('activate', function()
32881     {
32882         if (this.tree.rendered) {
32883             return;
32884         }
32885         //console.log('render tree');
32886         this.tree.render();
32887     });
32888     
32889     this.on('resize',  function (cp, w, h) {
32890             this.tree.innerCt.setWidth(w);
32891             this.tree.innerCt.setHeight(h);
32892             this.tree.innerCt.setStyle('overflow-y', 'auto');
32893     });
32894
32895         
32896     
32897 };
32898
32899 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32900     fitToFrame : true,
32901     autoScroll : true
32902 });
32903
32904
32905
32906
32907
32908
32909
32910
32911
32912
32913
32914 /*
32915  * Based on:
32916  * Ext JS Library 1.1.1
32917  * Copyright(c) 2006-2007, Ext JS, LLC.
32918  *
32919  * Originally Released Under LGPL - original licence link has changed is not relivant.
32920  *
32921  * Fork - LGPL
32922  * <script type="text/javascript">
32923  */
32924  
32925
32926 /**
32927  * @class Roo.ReaderLayout
32928  * @extends Roo.BorderLayout
32929  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32930  * center region containing two nested regions (a top one for a list view and one for item preview below),
32931  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32932  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32933  * expedites the setup of the overall layout and regions for this common application style.
32934  * Example:
32935  <pre><code>
32936 var reader = new Roo.ReaderLayout();
32937 var CP = Roo.ContentPanel;  // shortcut for adding
32938
32939 reader.beginUpdate();
32940 reader.add("north", new CP("north", "North"));
32941 reader.add("west", new CP("west", {title: "West"}));
32942 reader.add("east", new CP("east", {title: "East"}));
32943
32944 reader.regions.listView.add(new CP("listView", "List"));
32945 reader.regions.preview.add(new CP("preview", "Preview"));
32946 reader.endUpdate();
32947 </code></pre>
32948 * @constructor
32949 * Create a new ReaderLayout
32950 * @param {Object} config Configuration options
32951 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32952 * document.body if omitted)
32953 */
32954 Roo.ReaderLayout = function(config, renderTo){
32955     var c = config || {size:{}};
32956     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32957         north: c.north !== false ? Roo.apply({
32958             split:false,
32959             initialSize: 32,
32960             titlebar: false
32961         }, c.north) : false,
32962         west: c.west !== false ? Roo.apply({
32963             split:true,
32964             initialSize: 200,
32965             minSize: 175,
32966             maxSize: 400,
32967             titlebar: true,
32968             collapsible: true,
32969             animate: true,
32970             margins:{left:5,right:0,bottom:5,top:5},
32971             cmargins:{left:5,right:5,bottom:5,top:5}
32972         }, c.west) : false,
32973         east: c.east !== false ? Roo.apply({
32974             split:true,
32975             initialSize: 200,
32976             minSize: 175,
32977             maxSize: 400,
32978             titlebar: true,
32979             collapsible: true,
32980             animate: true,
32981             margins:{left:0,right:5,bottom:5,top:5},
32982             cmargins:{left:5,right:5,bottom:5,top:5}
32983         }, c.east) : false,
32984         center: Roo.apply({
32985             tabPosition: 'top',
32986             autoScroll:false,
32987             closeOnTab: true,
32988             titlebar:false,
32989             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32990         }, c.center)
32991     });
32992
32993     this.el.addClass('x-reader');
32994
32995     this.beginUpdate();
32996
32997     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32998         south: c.preview !== false ? Roo.apply({
32999             split:true,
33000             initialSize: 200,
33001             minSize: 100,
33002             autoScroll:true,
33003             collapsible:true,
33004             titlebar: true,
33005             cmargins:{top:5,left:0, right:0, bottom:0}
33006         }, c.preview) : false,
33007         center: Roo.apply({
33008             autoScroll:false,
33009             titlebar:false,
33010             minHeight:200
33011         }, c.listView)
33012     });
33013     this.add('center', new Roo.NestedLayoutPanel(inner,
33014             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33015
33016     this.endUpdate();
33017
33018     this.regions.preview = inner.getRegion('south');
33019     this.regions.listView = inner.getRegion('center');
33020 };
33021
33022 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33023  * Based on:
33024  * Ext JS Library 1.1.1
33025  * Copyright(c) 2006-2007, Ext JS, LLC.
33026  *
33027  * Originally Released Under LGPL - original licence link has changed is not relivant.
33028  *
33029  * Fork - LGPL
33030  * <script type="text/javascript">
33031  */
33032  
33033 /**
33034  * @class Roo.grid.Grid
33035  * @extends Roo.util.Observable
33036  * This class represents the primary interface of a component based grid control.
33037  * <br><br>Usage:<pre><code>
33038  var grid = new Roo.grid.Grid("my-container-id", {
33039      ds: myDataStore,
33040      cm: myColModel,
33041      selModel: mySelectionModel,
33042      autoSizeColumns: true,
33043      monitorWindowResize: false,
33044      trackMouseOver: true
33045  });
33046  // set any options
33047  grid.render();
33048  * </code></pre>
33049  * <b>Common Problems:</b><br/>
33050  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33051  * element will correct this<br/>
33052  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33053  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33054  * are unpredictable.<br/>
33055  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33056  * grid to calculate dimensions/offsets.<br/>
33057   * @constructor
33058  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33059  * The container MUST have some type of size defined for the grid to fill. The container will be
33060  * automatically set to position relative if it isn't already.
33061  * @param {Object} config A config object that sets properties on this grid.
33062  */
33063 Roo.grid.Grid = function(container, config){
33064         // initialize the container
33065         this.container = Roo.get(container);
33066         this.container.update("");
33067         this.container.setStyle("overflow", "hidden");
33068     this.container.addClass('x-grid-container');
33069
33070     this.id = this.container.id;
33071
33072     Roo.apply(this, config);
33073     // check and correct shorthanded configs
33074     if(this.ds){
33075         this.dataSource = this.ds;
33076         delete this.ds;
33077     }
33078     if(this.cm){
33079         this.colModel = this.cm;
33080         delete this.cm;
33081     }
33082     if(this.sm){
33083         this.selModel = this.sm;
33084         delete this.sm;
33085     }
33086
33087     if (this.selModel) {
33088         this.selModel = Roo.factory(this.selModel, Roo.grid);
33089         this.sm = this.selModel;
33090         this.sm.xmodule = this.xmodule || false;
33091     }
33092     if (typeof(this.colModel.config) == 'undefined') {
33093         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33094         this.cm = this.colModel;
33095         this.cm.xmodule = this.xmodule || false;
33096     }
33097     if (this.dataSource) {
33098         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33099         this.ds = this.dataSource;
33100         this.ds.xmodule = this.xmodule || false;
33101          
33102     }
33103     
33104     
33105     
33106     if(this.width){
33107         this.container.setWidth(this.width);
33108     }
33109
33110     if(this.height){
33111         this.container.setHeight(this.height);
33112     }
33113     /** @private */
33114         this.addEvents({
33115         // raw events
33116         /**
33117          * @event click
33118          * The raw click event for the entire grid.
33119          * @param {Roo.EventObject} e
33120          */
33121         "click" : true,
33122         /**
33123          * @event dblclick
33124          * The raw dblclick event for the entire grid.
33125          * @param {Roo.EventObject} e
33126          */
33127         "dblclick" : true,
33128         /**
33129          * @event contextmenu
33130          * The raw contextmenu event for the entire grid.
33131          * @param {Roo.EventObject} e
33132          */
33133         "contextmenu" : true,
33134         /**
33135          * @event mousedown
33136          * The raw mousedown event for the entire grid.
33137          * @param {Roo.EventObject} e
33138          */
33139         "mousedown" : true,
33140         /**
33141          * @event mouseup
33142          * The raw mouseup event for the entire grid.
33143          * @param {Roo.EventObject} e
33144          */
33145         "mouseup" : true,
33146         /**
33147          * @event mouseover
33148          * The raw mouseover event for the entire grid.
33149          * @param {Roo.EventObject} e
33150          */
33151         "mouseover" : true,
33152         /**
33153          * @event mouseout
33154          * The raw mouseout event for the entire grid.
33155          * @param {Roo.EventObject} e
33156          */
33157         "mouseout" : true,
33158         /**
33159          * @event keypress
33160          * The raw keypress event for the entire grid.
33161          * @param {Roo.EventObject} e
33162          */
33163         "keypress" : true,
33164         /**
33165          * @event keydown
33166          * The raw keydown event for the entire grid.
33167          * @param {Roo.EventObject} e
33168          */
33169         "keydown" : true,
33170
33171         // custom events
33172
33173         /**
33174          * @event cellclick
33175          * Fires when a cell is clicked
33176          * @param {Grid} this
33177          * @param {Number} rowIndex
33178          * @param {Number} columnIndex
33179          * @param {Roo.EventObject} e
33180          */
33181         "cellclick" : true,
33182         /**
33183          * @event celldblclick
33184          * Fires when a cell is double clicked
33185          * @param {Grid} this
33186          * @param {Number} rowIndex
33187          * @param {Number} columnIndex
33188          * @param {Roo.EventObject} e
33189          */
33190         "celldblclick" : true,
33191         /**
33192          * @event rowclick
33193          * Fires when a row is clicked
33194          * @param {Grid} this
33195          * @param {Number} rowIndex
33196          * @param {Roo.EventObject} e
33197          */
33198         "rowclick" : true,
33199         /**
33200          * @event rowdblclick
33201          * Fires when a row is double clicked
33202          * @param {Grid} this
33203          * @param {Number} rowIndex
33204          * @param {Roo.EventObject} e
33205          */
33206         "rowdblclick" : true,
33207         /**
33208          * @event headerclick
33209          * Fires when a header is clicked
33210          * @param {Grid} this
33211          * @param {Number} columnIndex
33212          * @param {Roo.EventObject} e
33213          */
33214         "headerclick" : true,
33215         /**
33216          * @event headerdblclick
33217          * Fires when a header cell is double clicked
33218          * @param {Grid} this
33219          * @param {Number} columnIndex
33220          * @param {Roo.EventObject} e
33221          */
33222         "headerdblclick" : true,
33223         /**
33224          * @event rowcontextmenu
33225          * Fires when a row is right clicked
33226          * @param {Grid} this
33227          * @param {Number} rowIndex
33228          * @param {Roo.EventObject} e
33229          */
33230         "rowcontextmenu" : true,
33231         /**
33232          * @event cellcontextmenu
33233          * Fires when a cell is right clicked
33234          * @param {Grid} this
33235          * @param {Number} rowIndex
33236          * @param {Number} cellIndex
33237          * @param {Roo.EventObject} e
33238          */
33239          "cellcontextmenu" : true,
33240         /**
33241          * @event headercontextmenu
33242          * Fires when a header is right clicked
33243          * @param {Grid} this
33244          * @param {Number} columnIndex
33245          * @param {Roo.EventObject} e
33246          */
33247         "headercontextmenu" : true,
33248         /**
33249          * @event bodyscroll
33250          * Fires when the body element is scrolled
33251          * @param {Number} scrollLeft
33252          * @param {Number} scrollTop
33253          */
33254         "bodyscroll" : true,
33255         /**
33256          * @event columnresize
33257          * Fires when the user resizes a column
33258          * @param {Number} columnIndex
33259          * @param {Number} newSize
33260          */
33261         "columnresize" : true,
33262         /**
33263          * @event columnmove
33264          * Fires when the user moves a column
33265          * @param {Number} oldIndex
33266          * @param {Number} newIndex
33267          */
33268         "columnmove" : true,
33269         /**
33270          * @event startdrag
33271          * Fires when row(s) start being dragged
33272          * @param {Grid} this
33273          * @param {Roo.GridDD} dd The drag drop object
33274          * @param {event} e The raw browser event
33275          */
33276         "startdrag" : true,
33277         /**
33278          * @event enddrag
33279          * Fires when a drag operation is complete
33280          * @param {Grid} this
33281          * @param {Roo.GridDD} dd The drag drop object
33282          * @param {event} e The raw browser event
33283          */
33284         "enddrag" : true,
33285         /**
33286          * @event dragdrop
33287          * Fires when dragged row(s) are dropped on a valid DD target
33288          * @param {Grid} this
33289          * @param {Roo.GridDD} dd The drag drop object
33290          * @param {String} targetId The target drag drop object
33291          * @param {event} e The raw browser event
33292          */
33293         "dragdrop" : true,
33294         /**
33295          * @event dragover
33296          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33297          * @param {Grid} this
33298          * @param {Roo.GridDD} dd The drag drop object
33299          * @param {String} targetId The target drag drop object
33300          * @param {event} e The raw browser event
33301          */
33302         "dragover" : true,
33303         /**
33304          * @event dragenter
33305          *  Fires when the dragged row(s) first cross another DD target while being dragged
33306          * @param {Grid} this
33307          * @param {Roo.GridDD} dd The drag drop object
33308          * @param {String} targetId The target drag drop object
33309          * @param {event} e The raw browser event
33310          */
33311         "dragenter" : true,
33312         /**
33313          * @event dragout
33314          * Fires when the dragged row(s) leave another DD target while being dragged
33315          * @param {Grid} this
33316          * @param {Roo.GridDD} dd The drag drop object
33317          * @param {String} targetId The target drag drop object
33318          * @param {event} e The raw browser event
33319          */
33320         "dragout" : true,
33321         /**
33322          * @event rowclass
33323          * Fires when a row is rendered, so you can change add a style to it.
33324          * @param {GridView} gridview   The grid view
33325          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33326          */
33327         'rowclass' : true,
33328
33329         /**
33330          * @event render
33331          * Fires when the grid is rendered
33332          * @param {Grid} grid
33333          */
33334         'render' : true
33335     });
33336
33337     Roo.grid.Grid.superclass.constructor.call(this);
33338 };
33339 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33340     
33341     /**
33342      * @cfg {String} ddGroup - drag drop group.
33343      */
33344
33345     /**
33346      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33347      */
33348     minColumnWidth : 25,
33349
33350     /**
33351      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33352      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33353      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33354      */
33355     autoSizeColumns : false,
33356
33357     /**
33358      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33359      */
33360     autoSizeHeaders : true,
33361
33362     /**
33363      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33364      */
33365     monitorWindowResize : true,
33366
33367     /**
33368      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33369      * rows measured to get a columns size. Default is 0 (all rows).
33370      */
33371     maxRowsToMeasure : 0,
33372
33373     /**
33374      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33375      */
33376     trackMouseOver : true,
33377
33378     /**
33379     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33380     */
33381     
33382     /**
33383     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33384     */
33385     enableDragDrop : false,
33386     
33387     /**
33388     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33389     */
33390     enableColumnMove : true,
33391     
33392     /**
33393     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33394     */
33395     enableColumnHide : true,
33396     
33397     /**
33398     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33399     */
33400     enableRowHeightSync : false,
33401     
33402     /**
33403     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33404     */
33405     stripeRows : true,
33406     
33407     /**
33408     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33409     */
33410     autoHeight : false,
33411
33412     /**
33413      * @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.
33414      */
33415     autoExpandColumn : false,
33416
33417     /**
33418     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33419     * Default is 50.
33420     */
33421     autoExpandMin : 50,
33422
33423     /**
33424     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33425     */
33426     autoExpandMax : 1000,
33427
33428     /**
33429     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33430     */
33431     view : null,
33432
33433     /**
33434     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33435     */
33436     loadMask : false,
33437     /**
33438     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33439     */
33440     dropTarget: false,
33441     
33442    
33443     
33444     // private
33445     rendered : false,
33446
33447     /**
33448     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33449     * of a fixed width. Default is false.
33450     */
33451     /**
33452     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33453     */
33454     /**
33455      * Called once after all setup has been completed and the grid is ready to be rendered.
33456      * @return {Roo.grid.Grid} this
33457      */
33458     render : function()
33459     {
33460         var c = this.container;
33461         // try to detect autoHeight/width mode
33462         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33463             this.autoHeight = true;
33464         }
33465         var view = this.getView();
33466         view.init(this);
33467
33468         c.on("click", this.onClick, this);
33469         c.on("dblclick", this.onDblClick, this);
33470         c.on("contextmenu", this.onContextMenu, this);
33471         c.on("keydown", this.onKeyDown, this);
33472
33473         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33474
33475         this.getSelectionModel().init(this);
33476
33477         view.render();
33478
33479         if(this.loadMask){
33480             this.loadMask = new Roo.LoadMask(this.container,
33481                     Roo.apply({store:this.dataSource}, this.loadMask));
33482         }
33483         
33484         
33485         if (this.toolbar && this.toolbar.xtype) {
33486             this.toolbar.container = this.getView().getHeaderPanel(true);
33487             this.toolbar = new Roo.Toolbar(this.toolbar);
33488         }
33489         if (this.footer && this.footer.xtype) {
33490             this.footer.dataSource = this.getDataSource();
33491             this.footer.container = this.getView().getFooterPanel(true);
33492             this.footer = Roo.factory(this.footer, Roo);
33493         }
33494         if (this.dropTarget && this.dropTarget.xtype) {
33495             delete this.dropTarget.xtype;
33496             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33497         }
33498         
33499         
33500         this.rendered = true;
33501         this.fireEvent('render', this);
33502         return this;
33503     },
33504
33505         /**
33506          * Reconfigures the grid to use a different Store and Column Model.
33507          * The View will be bound to the new objects and refreshed.
33508          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33509          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33510          */
33511     reconfigure : function(dataSource, colModel){
33512         if(this.loadMask){
33513             this.loadMask.destroy();
33514             this.loadMask = new Roo.LoadMask(this.container,
33515                     Roo.apply({store:dataSource}, this.loadMask));
33516         }
33517         this.view.bind(dataSource, colModel);
33518         this.dataSource = dataSource;
33519         this.colModel = colModel;
33520         this.view.refresh(true);
33521     },
33522
33523     // private
33524     onKeyDown : function(e){
33525         this.fireEvent("keydown", e);
33526     },
33527
33528     /**
33529      * Destroy this grid.
33530      * @param {Boolean} removeEl True to remove the element
33531      */
33532     destroy : function(removeEl, keepListeners){
33533         if(this.loadMask){
33534             this.loadMask.destroy();
33535         }
33536         var c = this.container;
33537         c.removeAllListeners();
33538         this.view.destroy();
33539         this.colModel.purgeListeners();
33540         if(!keepListeners){
33541             this.purgeListeners();
33542         }
33543         c.update("");
33544         if(removeEl === true){
33545             c.remove();
33546         }
33547     },
33548
33549     // private
33550     processEvent : function(name, e){
33551         this.fireEvent(name, e);
33552         var t = e.getTarget();
33553         var v = this.view;
33554         var header = v.findHeaderIndex(t);
33555         if(header !== false){
33556             this.fireEvent("header" + name, this, header, e);
33557         }else{
33558             var row = v.findRowIndex(t);
33559             var cell = v.findCellIndex(t);
33560             if(row !== false){
33561                 this.fireEvent("row" + name, this, row, e);
33562                 if(cell !== false){
33563                     this.fireEvent("cell" + name, this, row, cell, e);
33564                 }
33565             }
33566         }
33567     },
33568
33569     // private
33570     onClick : function(e){
33571         this.processEvent("click", e);
33572     },
33573
33574     // private
33575     onContextMenu : function(e, t){
33576         this.processEvent("contextmenu", e);
33577     },
33578
33579     // private
33580     onDblClick : function(e){
33581         this.processEvent("dblclick", e);
33582     },
33583
33584     // private
33585     walkCells : function(row, col, step, fn, scope){
33586         var cm = this.colModel, clen = cm.getColumnCount();
33587         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33588         if(step < 0){
33589             if(col < 0){
33590                 row--;
33591                 first = false;
33592             }
33593             while(row >= 0){
33594                 if(!first){
33595                     col = clen-1;
33596                 }
33597                 first = false;
33598                 while(col >= 0){
33599                     if(fn.call(scope || this, row, col, cm) === true){
33600                         return [row, col];
33601                     }
33602                     col--;
33603                 }
33604                 row--;
33605             }
33606         } else {
33607             if(col >= clen){
33608                 row++;
33609                 first = false;
33610             }
33611             while(row < rlen){
33612                 if(!first){
33613                     col = 0;
33614                 }
33615                 first = false;
33616                 while(col < clen){
33617                     if(fn.call(scope || this, row, col, cm) === true){
33618                         return [row, col];
33619                     }
33620                     col++;
33621                 }
33622                 row++;
33623             }
33624         }
33625         return null;
33626     },
33627
33628     // private
33629     getSelections : function(){
33630         return this.selModel.getSelections();
33631     },
33632
33633     /**
33634      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33635      * but if manual update is required this method will initiate it.
33636      */
33637     autoSize : function(){
33638         if(this.rendered){
33639             this.view.layout();
33640             if(this.view.adjustForScroll){
33641                 this.view.adjustForScroll();
33642             }
33643         }
33644     },
33645
33646     /**
33647      * Returns the grid's underlying element.
33648      * @return {Element} The element
33649      */
33650     getGridEl : function(){
33651         return this.container;
33652     },
33653
33654     // private for compatibility, overridden by editor grid
33655     stopEditing : function(){},
33656
33657     /**
33658      * Returns the grid's SelectionModel.
33659      * @return {SelectionModel}
33660      */
33661     getSelectionModel : function(){
33662         if(!this.selModel){
33663             this.selModel = new Roo.grid.RowSelectionModel();
33664         }
33665         return this.selModel;
33666     },
33667
33668     /**
33669      * Returns the grid's DataSource.
33670      * @return {DataSource}
33671      */
33672     getDataSource : function(){
33673         return this.dataSource;
33674     },
33675
33676     /**
33677      * Returns the grid's ColumnModel.
33678      * @return {ColumnModel}
33679      */
33680     getColumnModel : function(){
33681         return this.colModel;
33682     },
33683
33684     /**
33685      * Returns the grid's GridView object.
33686      * @return {GridView}
33687      */
33688     getView : function(){
33689         if(!this.view){
33690             this.view = new Roo.grid.GridView(this.viewConfig);
33691         }
33692         return this.view;
33693     },
33694     /**
33695      * Called to get grid's drag proxy text, by default returns this.ddText.
33696      * @return {String}
33697      */
33698     getDragDropText : function(){
33699         var count = this.selModel.getCount();
33700         return String.format(this.ddText, count, count == 1 ? '' : 's');
33701     }
33702 });
33703 /**
33704  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33705  * %0 is replaced with the number of selected rows.
33706  * @type String
33707  */
33708 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33709  * Based on:
33710  * Ext JS Library 1.1.1
33711  * Copyright(c) 2006-2007, Ext JS, LLC.
33712  *
33713  * Originally Released Under LGPL - original licence link has changed is not relivant.
33714  *
33715  * Fork - LGPL
33716  * <script type="text/javascript">
33717  */
33718  
33719 Roo.grid.AbstractGridView = function(){
33720         this.grid = null;
33721         
33722         this.events = {
33723             "beforerowremoved" : true,
33724             "beforerowsinserted" : true,
33725             "beforerefresh" : true,
33726             "rowremoved" : true,
33727             "rowsinserted" : true,
33728             "rowupdated" : true,
33729             "refresh" : true
33730         };
33731     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33732 };
33733
33734 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33735     rowClass : "x-grid-row",
33736     cellClass : "x-grid-cell",
33737     tdClass : "x-grid-td",
33738     hdClass : "x-grid-hd",
33739     splitClass : "x-grid-hd-split",
33740     
33741         init: function(grid){
33742         this.grid = grid;
33743                 var cid = this.grid.getGridEl().id;
33744         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33745         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33746         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33747         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33748         },
33749         
33750         getColumnRenderers : function(){
33751         var renderers = [];
33752         var cm = this.grid.colModel;
33753         var colCount = cm.getColumnCount();
33754         for(var i = 0; i < colCount; i++){
33755             renderers[i] = cm.getRenderer(i);
33756         }
33757         return renderers;
33758     },
33759     
33760     getColumnIds : function(){
33761         var ids = [];
33762         var cm = this.grid.colModel;
33763         var colCount = cm.getColumnCount();
33764         for(var i = 0; i < colCount; i++){
33765             ids[i] = cm.getColumnId(i);
33766         }
33767         return ids;
33768     },
33769     
33770     getDataIndexes : function(){
33771         if(!this.indexMap){
33772             this.indexMap = this.buildIndexMap();
33773         }
33774         return this.indexMap.colToData;
33775     },
33776     
33777     getColumnIndexByDataIndex : function(dataIndex){
33778         if(!this.indexMap){
33779             this.indexMap = this.buildIndexMap();
33780         }
33781         return this.indexMap.dataToCol[dataIndex];
33782     },
33783     
33784     /**
33785      * Set a css style for a column dynamically. 
33786      * @param {Number} colIndex The index of the column
33787      * @param {String} name The css property name
33788      * @param {String} value The css value
33789      */
33790     setCSSStyle : function(colIndex, name, value){
33791         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33792         Roo.util.CSS.updateRule(selector, name, value);
33793     },
33794     
33795     generateRules : function(cm){
33796         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33797         Roo.util.CSS.removeStyleSheet(rulesId);
33798         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33799             var cid = cm.getColumnId(i);
33800             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33801                          this.tdSelector, cid, " {\n}\n",
33802                          this.hdSelector, cid, " {\n}\n",
33803                          this.splitSelector, cid, " {\n}\n");
33804         }
33805         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33806     }
33807 });/*
33808  * Based on:
33809  * Ext JS Library 1.1.1
33810  * Copyright(c) 2006-2007, Ext JS, LLC.
33811  *
33812  * Originally Released Under LGPL - original licence link has changed is not relivant.
33813  *
33814  * Fork - LGPL
33815  * <script type="text/javascript">
33816  */
33817
33818 // private
33819 // This is a support class used internally by the Grid components
33820 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33821     this.grid = grid;
33822     this.view = grid.getView();
33823     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33824     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33825     if(hd2){
33826         this.setHandleElId(Roo.id(hd));
33827         this.setOuterHandleElId(Roo.id(hd2));
33828     }
33829     this.scroll = false;
33830 };
33831 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33832     maxDragWidth: 120,
33833     getDragData : function(e){
33834         var t = Roo.lib.Event.getTarget(e);
33835         var h = this.view.findHeaderCell(t);
33836         if(h){
33837             return {ddel: h.firstChild, header:h};
33838         }
33839         return false;
33840     },
33841
33842     onInitDrag : function(e){
33843         this.view.headersDisabled = true;
33844         var clone = this.dragData.ddel.cloneNode(true);
33845         clone.id = Roo.id();
33846         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33847         this.proxy.update(clone);
33848         return true;
33849     },
33850
33851     afterValidDrop : function(){
33852         var v = this.view;
33853         setTimeout(function(){
33854             v.headersDisabled = false;
33855         }, 50);
33856     },
33857
33858     afterInvalidDrop : function(){
33859         var v = this.view;
33860         setTimeout(function(){
33861             v.headersDisabled = false;
33862         }, 50);
33863     }
33864 });
33865 /*
33866  * Based on:
33867  * Ext JS Library 1.1.1
33868  * Copyright(c) 2006-2007, Ext JS, LLC.
33869  *
33870  * Originally Released Under LGPL - original licence link has changed is not relivant.
33871  *
33872  * Fork - LGPL
33873  * <script type="text/javascript">
33874  */
33875 // private
33876 // This is a support class used internally by the Grid components
33877 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33878     this.grid = grid;
33879     this.view = grid.getView();
33880     // split the proxies so they don't interfere with mouse events
33881     this.proxyTop = Roo.DomHelper.append(document.body, {
33882         cls:"col-move-top", html:"&#160;"
33883     }, true);
33884     this.proxyBottom = Roo.DomHelper.append(document.body, {
33885         cls:"col-move-bottom", html:"&#160;"
33886     }, true);
33887     this.proxyTop.hide = this.proxyBottom.hide = function(){
33888         this.setLeftTop(-100,-100);
33889         this.setStyle("visibility", "hidden");
33890     };
33891     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33892     // temporarily disabled
33893     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33894     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33895 };
33896 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33897     proxyOffsets : [-4, -9],
33898     fly: Roo.Element.fly,
33899
33900     getTargetFromEvent : function(e){
33901         var t = Roo.lib.Event.getTarget(e);
33902         var cindex = this.view.findCellIndex(t);
33903         if(cindex !== false){
33904             return this.view.getHeaderCell(cindex);
33905         }
33906         return null;
33907     },
33908
33909     nextVisible : function(h){
33910         var v = this.view, cm = this.grid.colModel;
33911         h = h.nextSibling;
33912         while(h){
33913             if(!cm.isHidden(v.getCellIndex(h))){
33914                 return h;
33915             }
33916             h = h.nextSibling;
33917         }
33918         return null;
33919     },
33920
33921     prevVisible : function(h){
33922         var v = this.view, cm = this.grid.colModel;
33923         h = h.prevSibling;
33924         while(h){
33925             if(!cm.isHidden(v.getCellIndex(h))){
33926                 return h;
33927             }
33928             h = h.prevSibling;
33929         }
33930         return null;
33931     },
33932
33933     positionIndicator : function(h, n, e){
33934         var x = Roo.lib.Event.getPageX(e);
33935         var r = Roo.lib.Dom.getRegion(n.firstChild);
33936         var px, pt, py = r.top + this.proxyOffsets[1];
33937         if((r.right - x) <= (r.right-r.left)/2){
33938             px = r.right+this.view.borderWidth;
33939             pt = "after";
33940         }else{
33941             px = r.left;
33942             pt = "before";
33943         }
33944         var oldIndex = this.view.getCellIndex(h);
33945         var newIndex = this.view.getCellIndex(n);
33946
33947         if(this.grid.colModel.isFixed(newIndex)){
33948             return false;
33949         }
33950
33951         var locked = this.grid.colModel.isLocked(newIndex);
33952
33953         if(pt == "after"){
33954             newIndex++;
33955         }
33956         if(oldIndex < newIndex){
33957             newIndex--;
33958         }
33959         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33960             return false;
33961         }
33962         px +=  this.proxyOffsets[0];
33963         this.proxyTop.setLeftTop(px, py);
33964         this.proxyTop.show();
33965         if(!this.bottomOffset){
33966             this.bottomOffset = this.view.mainHd.getHeight();
33967         }
33968         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33969         this.proxyBottom.show();
33970         return pt;
33971     },
33972
33973     onNodeEnter : function(n, dd, e, data){
33974         if(data.header != n){
33975             this.positionIndicator(data.header, n, e);
33976         }
33977     },
33978
33979     onNodeOver : function(n, dd, e, data){
33980         var result = false;
33981         if(data.header != n){
33982             result = this.positionIndicator(data.header, n, e);
33983         }
33984         if(!result){
33985             this.proxyTop.hide();
33986             this.proxyBottom.hide();
33987         }
33988         return result ? this.dropAllowed : this.dropNotAllowed;
33989     },
33990
33991     onNodeOut : function(n, dd, e, data){
33992         this.proxyTop.hide();
33993         this.proxyBottom.hide();
33994     },
33995
33996     onNodeDrop : function(n, dd, e, data){
33997         var h = data.header;
33998         if(h != n){
33999             var cm = this.grid.colModel;
34000             var x = Roo.lib.Event.getPageX(e);
34001             var r = Roo.lib.Dom.getRegion(n.firstChild);
34002             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34003             var oldIndex = this.view.getCellIndex(h);
34004             var newIndex = this.view.getCellIndex(n);
34005             var locked = cm.isLocked(newIndex);
34006             if(pt == "after"){
34007                 newIndex++;
34008             }
34009             if(oldIndex < newIndex){
34010                 newIndex--;
34011             }
34012             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34013                 return false;
34014             }
34015             cm.setLocked(oldIndex, locked, true);
34016             cm.moveColumn(oldIndex, newIndex);
34017             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34018             return true;
34019         }
34020         return false;
34021     }
34022 });
34023 /*
34024  * Based on:
34025  * Ext JS Library 1.1.1
34026  * Copyright(c) 2006-2007, Ext JS, LLC.
34027  *
34028  * Originally Released Under LGPL - original licence link has changed is not relivant.
34029  *
34030  * Fork - LGPL
34031  * <script type="text/javascript">
34032  */
34033   
34034 /**
34035  * @class Roo.grid.GridView
34036  * @extends Roo.util.Observable
34037  *
34038  * @constructor
34039  * @param {Object} config
34040  */
34041 Roo.grid.GridView = function(config){
34042     Roo.grid.GridView.superclass.constructor.call(this);
34043     this.el = null;
34044
34045     Roo.apply(this, config);
34046 };
34047
34048 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34049
34050     
34051     rowClass : "x-grid-row",
34052
34053     cellClass : "x-grid-col",
34054
34055     tdClass : "x-grid-td",
34056
34057     hdClass : "x-grid-hd",
34058
34059     splitClass : "x-grid-split",
34060
34061     sortClasses : ["sort-asc", "sort-desc"],
34062
34063     enableMoveAnim : false,
34064
34065     hlColor: "C3DAF9",
34066
34067     dh : Roo.DomHelper,
34068
34069     fly : Roo.Element.fly,
34070
34071     css : Roo.util.CSS,
34072
34073     borderWidth: 1,
34074
34075     splitOffset: 3,
34076
34077     scrollIncrement : 22,
34078
34079     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34080
34081     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34082
34083     bind : function(ds, cm){
34084         if(this.ds){
34085             this.ds.un("load", this.onLoad, this);
34086             this.ds.un("datachanged", this.onDataChange, this);
34087             this.ds.un("add", this.onAdd, this);
34088             this.ds.un("remove", this.onRemove, this);
34089             this.ds.un("update", this.onUpdate, this);
34090             this.ds.un("clear", this.onClear, this);
34091         }
34092         if(ds){
34093             ds.on("load", this.onLoad, this);
34094             ds.on("datachanged", this.onDataChange, this);
34095             ds.on("add", this.onAdd, this);
34096             ds.on("remove", this.onRemove, this);
34097             ds.on("update", this.onUpdate, this);
34098             ds.on("clear", this.onClear, this);
34099         }
34100         this.ds = ds;
34101
34102         if(this.cm){
34103             this.cm.un("widthchange", this.onColWidthChange, this);
34104             this.cm.un("headerchange", this.onHeaderChange, this);
34105             this.cm.un("hiddenchange", this.onHiddenChange, this);
34106             this.cm.un("columnmoved", this.onColumnMove, this);
34107             this.cm.un("columnlockchange", this.onColumnLock, this);
34108         }
34109         if(cm){
34110             this.generateRules(cm);
34111             cm.on("widthchange", this.onColWidthChange, this);
34112             cm.on("headerchange", this.onHeaderChange, this);
34113             cm.on("hiddenchange", this.onHiddenChange, this);
34114             cm.on("columnmoved", this.onColumnMove, this);
34115             cm.on("columnlockchange", this.onColumnLock, this);
34116         }
34117         this.cm = cm;
34118     },
34119
34120     init: function(grid){
34121         Roo.grid.GridView.superclass.init.call(this, grid);
34122
34123         this.bind(grid.dataSource, grid.colModel);
34124
34125         grid.on("headerclick", this.handleHeaderClick, this);
34126
34127         if(grid.trackMouseOver){
34128             grid.on("mouseover", this.onRowOver, this);
34129             grid.on("mouseout", this.onRowOut, this);
34130         }
34131         grid.cancelTextSelection = function(){};
34132         this.gridId = grid.id;
34133
34134         var tpls = this.templates || {};
34135
34136         if(!tpls.master){
34137             tpls.master = new Roo.Template(
34138                '<div class="x-grid" hidefocus="true">',
34139                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34140                   '<div class="x-grid-topbar"></div>',
34141                   '<div class="x-grid-scroller"><div></div></div>',
34142                   '<div class="x-grid-locked">',
34143                       '<div class="x-grid-header">{lockedHeader}</div>',
34144                       '<div class="x-grid-body">{lockedBody}</div>',
34145                   "</div>",
34146                   '<div class="x-grid-viewport">',
34147                       '<div class="x-grid-header">{header}</div>',
34148                       '<div class="x-grid-body">{body}</div>',
34149                   "</div>",
34150                   '<div class="x-grid-bottombar"></div>',
34151                  
34152                   '<div class="x-grid-resize-proxy">&#160;</div>',
34153                "</div>"
34154             );
34155             tpls.master.disableformats = true;
34156         }
34157
34158         if(!tpls.header){
34159             tpls.header = new Roo.Template(
34160                '<table border="0" cellspacing="0" cellpadding="0">',
34161                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34162                "</table>{splits}"
34163             );
34164             tpls.header.disableformats = true;
34165         }
34166         tpls.header.compile();
34167
34168         if(!tpls.hcell){
34169             tpls.hcell = new Roo.Template(
34170                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34171                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34172                 "</div></td>"
34173              );
34174              tpls.hcell.disableFormats = true;
34175         }
34176         tpls.hcell.compile();
34177
34178         if(!tpls.hsplit){
34179             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34180             tpls.hsplit.disableFormats = true;
34181         }
34182         tpls.hsplit.compile();
34183
34184         if(!tpls.body){
34185             tpls.body = new Roo.Template(
34186                '<table border="0" cellspacing="0" cellpadding="0">',
34187                "<tbody>{rows}</tbody>",
34188                "</table>"
34189             );
34190             tpls.body.disableFormats = true;
34191         }
34192         tpls.body.compile();
34193
34194         if(!tpls.row){
34195             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34196             tpls.row.disableFormats = true;
34197         }
34198         tpls.row.compile();
34199
34200         if(!tpls.cell){
34201             tpls.cell = new Roo.Template(
34202                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34203                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34204                 "</td>"
34205             );
34206             tpls.cell.disableFormats = true;
34207         }
34208         tpls.cell.compile();
34209
34210         this.templates = tpls;
34211     },
34212
34213     // remap these for backwards compat
34214     onColWidthChange : function(){
34215         this.updateColumns.apply(this, arguments);
34216     },
34217     onHeaderChange : function(){
34218         this.updateHeaders.apply(this, arguments);
34219     }, 
34220     onHiddenChange : function(){
34221         this.handleHiddenChange.apply(this, arguments);
34222     },
34223     onColumnMove : function(){
34224         this.handleColumnMove.apply(this, arguments);
34225     },
34226     onColumnLock : function(){
34227         this.handleLockChange.apply(this, arguments);
34228     },
34229
34230     onDataChange : function(){
34231         this.refresh();
34232         this.updateHeaderSortState();
34233     },
34234
34235     onClear : function(){
34236         this.refresh();
34237     },
34238
34239     onUpdate : function(ds, record){
34240         this.refreshRow(record);
34241     },
34242
34243     refreshRow : function(record){
34244         var ds = this.ds, index;
34245         if(typeof record == 'number'){
34246             index = record;
34247             record = ds.getAt(index);
34248         }else{
34249             index = ds.indexOf(record);
34250         }
34251         this.insertRows(ds, index, index, true);
34252         this.onRemove(ds, record, index+1, true);
34253         this.syncRowHeights(index, index);
34254         this.layout();
34255         this.fireEvent("rowupdated", this, index, record);
34256     },
34257
34258     onAdd : function(ds, records, index){
34259         this.insertRows(ds, index, index + (records.length-1));
34260     },
34261
34262     onRemove : function(ds, record, index, isUpdate){
34263         if(isUpdate !== true){
34264             this.fireEvent("beforerowremoved", this, index, record);
34265         }
34266         var bt = this.getBodyTable(), lt = this.getLockedTable();
34267         if(bt.rows[index]){
34268             bt.firstChild.removeChild(bt.rows[index]);
34269         }
34270         if(lt.rows[index]){
34271             lt.firstChild.removeChild(lt.rows[index]);
34272         }
34273         if(isUpdate !== true){
34274             this.stripeRows(index);
34275             this.syncRowHeights(index, index);
34276             this.layout();
34277             this.fireEvent("rowremoved", this, index, record);
34278         }
34279     },
34280
34281     onLoad : function(){
34282         this.scrollToTop();
34283     },
34284
34285     /**
34286      * Scrolls the grid to the top
34287      */
34288     scrollToTop : function(){
34289         if(this.scroller){
34290             this.scroller.dom.scrollTop = 0;
34291             this.syncScroll();
34292         }
34293     },
34294
34295     /**
34296      * Gets a panel in the header of the grid that can be used for toolbars etc.
34297      * After modifying the contents of this panel a call to grid.autoSize() may be
34298      * required to register any changes in size.
34299      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34300      * @return Roo.Element
34301      */
34302     getHeaderPanel : function(doShow){
34303         if(doShow){
34304             this.headerPanel.show();
34305         }
34306         return this.headerPanel;
34307     },
34308
34309     /**
34310      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34311      * After modifying the contents of this panel a call to grid.autoSize() may be
34312      * required to register any changes in size.
34313      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34314      * @return Roo.Element
34315      */
34316     getFooterPanel : function(doShow){
34317         if(doShow){
34318             this.footerPanel.show();
34319         }
34320         return this.footerPanel;
34321     },
34322
34323     initElements : function(){
34324         var E = Roo.Element;
34325         var el = this.grid.getGridEl().dom.firstChild;
34326         var cs = el.childNodes;
34327
34328         this.el = new E(el);
34329         
34330          this.focusEl = new E(el.firstChild);
34331         this.focusEl.swallowEvent("click", true);
34332         
34333         this.headerPanel = new E(cs[1]);
34334         this.headerPanel.enableDisplayMode("block");
34335
34336         this.scroller = new E(cs[2]);
34337         this.scrollSizer = new E(this.scroller.dom.firstChild);
34338
34339         this.lockedWrap = new E(cs[3]);
34340         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34341         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34342
34343         this.mainWrap = new E(cs[4]);
34344         this.mainHd = new E(this.mainWrap.dom.firstChild);
34345         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34346
34347         this.footerPanel = new E(cs[5]);
34348         this.footerPanel.enableDisplayMode("block");
34349
34350         this.resizeProxy = new E(cs[6]);
34351
34352         this.headerSelector = String.format(
34353            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34354            this.lockedHd.id, this.mainHd.id
34355         );
34356
34357         this.splitterSelector = String.format(
34358            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34359            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34360         );
34361     },
34362     idToCssName : function(s)
34363     {
34364         return s.replace(/[^a-z0-9]+/ig, '-');
34365     },
34366
34367     getHeaderCell : function(index){
34368         return Roo.DomQuery.select(this.headerSelector)[index];
34369     },
34370
34371     getHeaderCellMeasure : function(index){
34372         return this.getHeaderCell(index).firstChild;
34373     },
34374
34375     getHeaderCellText : function(index){
34376         return this.getHeaderCell(index).firstChild.firstChild;
34377     },
34378
34379     getLockedTable : function(){
34380         return this.lockedBody.dom.firstChild;
34381     },
34382
34383     getBodyTable : function(){
34384         return this.mainBody.dom.firstChild;
34385     },
34386
34387     getLockedRow : function(index){
34388         return this.getLockedTable().rows[index];
34389     },
34390
34391     getRow : function(index){
34392         return this.getBodyTable().rows[index];
34393     },
34394
34395     getRowComposite : function(index){
34396         if(!this.rowEl){
34397             this.rowEl = new Roo.CompositeElementLite();
34398         }
34399         var els = [], lrow, mrow;
34400         if(lrow = this.getLockedRow(index)){
34401             els.push(lrow);
34402         }
34403         if(mrow = this.getRow(index)){
34404             els.push(mrow);
34405         }
34406         this.rowEl.elements = els;
34407         return this.rowEl;
34408     },
34409     /**
34410      * Gets the 'td' of the cell
34411      * 
34412      * @param {Integer} rowIndex row to select
34413      * @param {Integer} colIndex column to select
34414      * 
34415      * @return {Object} 
34416      */
34417     getCell : function(rowIndex, colIndex){
34418         var locked = this.cm.getLockedCount();
34419         var source;
34420         if(colIndex < locked){
34421             source = this.lockedBody.dom.firstChild;
34422         }else{
34423             source = this.mainBody.dom.firstChild;
34424             colIndex -= locked;
34425         }
34426         return source.rows[rowIndex].childNodes[colIndex];
34427     },
34428
34429     getCellText : function(rowIndex, colIndex){
34430         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34431     },
34432
34433     getCellBox : function(cell){
34434         var b = this.fly(cell).getBox();
34435         if(Roo.isOpera){ // opera fails to report the Y
34436             b.y = cell.offsetTop + this.mainBody.getY();
34437         }
34438         return b;
34439     },
34440
34441     getCellIndex : function(cell){
34442         var id = String(cell.className).match(this.cellRE);
34443         if(id){
34444             return parseInt(id[1], 10);
34445         }
34446         return 0;
34447     },
34448
34449     findHeaderIndex : function(n){
34450         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34451         return r ? this.getCellIndex(r) : false;
34452     },
34453
34454     findHeaderCell : function(n){
34455         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34456         return r ? r : false;
34457     },
34458
34459     findRowIndex : function(n){
34460         if(!n){
34461             return false;
34462         }
34463         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34464         return r ? r.rowIndex : false;
34465     },
34466
34467     findCellIndex : function(node){
34468         var stop = this.el.dom;
34469         while(node && node != stop){
34470             if(this.findRE.test(node.className)){
34471                 return this.getCellIndex(node);
34472             }
34473             node = node.parentNode;
34474         }
34475         return false;
34476     },
34477
34478     getColumnId : function(index){
34479         return this.cm.getColumnId(index);
34480     },
34481
34482     getSplitters : function()
34483     {
34484         if(this.splitterSelector){
34485            return Roo.DomQuery.select(this.splitterSelector);
34486         }else{
34487             return null;
34488       }
34489     },
34490
34491     getSplitter : function(index){
34492         return this.getSplitters()[index];
34493     },
34494
34495     onRowOver : function(e, t){
34496         var row;
34497         if((row = this.findRowIndex(t)) !== false){
34498             this.getRowComposite(row).addClass("x-grid-row-over");
34499         }
34500     },
34501
34502     onRowOut : function(e, t){
34503         var row;
34504         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34505             this.getRowComposite(row).removeClass("x-grid-row-over");
34506         }
34507     },
34508
34509     renderHeaders : function(){
34510         var cm = this.cm;
34511         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
34512         var cb = [], lb = [], sb = [], lsb = [], p = {};
34513         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34514             p.cellId = "x-grid-hd-0-" + i;
34515             p.splitId = "x-grid-csplit-0-" + i;
34516             p.id = cm.getColumnId(i);
34517             p.title = cm.getColumnTooltip(i) || "";
34518             p.value = cm.getColumnHeader(i) || "";
34519             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
34520             if(!cm.isLocked(i)){
34521                 cb[cb.length] = ct.apply(p);
34522                 sb[sb.length] = st.apply(p);
34523             }else{
34524                 lb[lb.length] = ct.apply(p);
34525                 lsb[lsb.length] = st.apply(p);
34526             }
34527         }
34528         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
34529                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
34530     },
34531
34532     updateHeaders : function(){
34533         var html = this.renderHeaders();
34534         this.lockedHd.update(html[0]);
34535         this.mainHd.update(html[1]);
34536     },
34537
34538     /**
34539      * Focuses the specified row.
34540      * @param {Number} row The row index
34541      */
34542     focusRow : function(row)
34543     {
34544         //Roo.log('GridView.focusRow');
34545         var x = this.scroller.dom.scrollLeft;
34546         this.focusCell(row, 0, false);
34547         this.scroller.dom.scrollLeft = x;
34548     },
34549
34550     /**
34551      * Focuses the specified cell.
34552      * @param {Number} row The row index
34553      * @param {Number} col The column index
34554      * @param {Boolean} hscroll false to disable horizontal scrolling
34555      */
34556     focusCell : function(row, col, hscroll)
34557     {
34558         //Roo.log('GridView.focusCell');
34559         var el = this.ensureVisible(row, col, hscroll);
34560         this.focusEl.alignTo(el, "tl-tl");
34561         if(Roo.isGecko){
34562             this.focusEl.focus();
34563         }else{
34564             this.focusEl.focus.defer(1, this.focusEl);
34565         }
34566     },
34567
34568     /**
34569      * Scrolls the specified cell into view
34570      * @param {Number} row The row index
34571      * @param {Number} col The column index
34572      * @param {Boolean} hscroll false to disable horizontal scrolling
34573      */
34574     ensureVisible : function(row, col, hscroll)
34575     {
34576         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34577         //return null; //disable for testing.
34578         if(typeof row != "number"){
34579             row = row.rowIndex;
34580         }
34581         if(row < 0 && row >= this.ds.getCount()){
34582             return  null;
34583         }
34584         col = (col !== undefined ? col : 0);
34585         var cm = this.grid.colModel;
34586         while(cm.isHidden(col)){
34587             col++;
34588         }
34589
34590         var el = this.getCell(row, col);
34591         if(!el){
34592             return null;
34593         }
34594         var c = this.scroller.dom;
34595
34596         var ctop = parseInt(el.offsetTop, 10);
34597         var cleft = parseInt(el.offsetLeft, 10);
34598         var cbot = ctop + el.offsetHeight;
34599         var cright = cleft + el.offsetWidth;
34600         
34601         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34602         var stop = parseInt(c.scrollTop, 10);
34603         var sleft = parseInt(c.scrollLeft, 10);
34604         var sbot = stop + ch;
34605         var sright = sleft + c.clientWidth;
34606         /*
34607         Roo.log('GridView.ensureVisible:' +
34608                 ' ctop:' + ctop +
34609                 ' c.clientHeight:' + c.clientHeight +
34610                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34611                 ' stop:' + stop +
34612                 ' cbot:' + cbot +
34613                 ' sbot:' + sbot +
34614                 ' ch:' + ch  
34615                 );
34616         */
34617         if(ctop < stop){
34618              c.scrollTop = ctop;
34619             //Roo.log("set scrolltop to ctop DISABLE?");
34620         }else if(cbot > sbot){
34621             //Roo.log("set scrolltop to cbot-ch");
34622             c.scrollTop = cbot-ch;
34623         }
34624         
34625         if(hscroll !== false){
34626             if(cleft < sleft){
34627                 c.scrollLeft = cleft;
34628             }else if(cright > sright){
34629                 c.scrollLeft = cright-c.clientWidth;
34630             }
34631         }
34632          
34633         return el;
34634     },
34635
34636     updateColumns : function(){
34637         this.grid.stopEditing();
34638         var cm = this.grid.colModel, colIds = this.getColumnIds();
34639         //var totalWidth = cm.getTotalWidth();
34640         var pos = 0;
34641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34642             //if(cm.isHidden(i)) continue;
34643             var w = cm.getColumnWidth(i);
34644             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34645             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34646         }
34647         this.updateSplitters();
34648     },
34649
34650     generateRules : function(cm){
34651         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34652         Roo.util.CSS.removeStyleSheet(rulesId);
34653         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34654             var cid = cm.getColumnId(i);
34655             var align = '';
34656             if(cm.config[i].align){
34657                 align = 'text-align:'+cm.config[i].align+';';
34658             }
34659             var hidden = '';
34660             if(cm.isHidden(i)){
34661                 hidden = 'display:none;';
34662             }
34663             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34664             ruleBuf.push(
34665                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34666                     this.hdSelector, cid, " {\n", align, width, "}\n",
34667                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34668                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34669         }
34670         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34671     },
34672
34673     updateSplitters : function(){
34674         var cm = this.cm, s = this.getSplitters();
34675         if(s){ // splitters not created yet
34676             var pos = 0, locked = true;
34677             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34678                 if(cm.isHidden(i)) continue;
34679                 var w = cm.getColumnWidth(i); // make sure it's a number
34680                 if(!cm.isLocked(i) && locked){
34681                     pos = 0;
34682                     locked = false;
34683                 }
34684                 pos += w;
34685                 s[i].style.left = (pos-this.splitOffset) + "px";
34686             }
34687         }
34688     },
34689
34690     handleHiddenChange : function(colModel, colIndex, hidden){
34691         if(hidden){
34692             this.hideColumn(colIndex);
34693         }else{
34694             this.unhideColumn(colIndex);
34695         }
34696     },
34697
34698     hideColumn : function(colIndex){
34699         var cid = this.getColumnId(colIndex);
34700         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34701         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34702         if(Roo.isSafari){
34703             this.updateHeaders();
34704         }
34705         this.updateSplitters();
34706         this.layout();
34707     },
34708
34709     unhideColumn : function(colIndex){
34710         var cid = this.getColumnId(colIndex);
34711         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34712         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34713
34714         if(Roo.isSafari){
34715             this.updateHeaders();
34716         }
34717         this.updateSplitters();
34718         this.layout();
34719     },
34720
34721     insertRows : function(dm, firstRow, lastRow, isUpdate){
34722         if(firstRow == 0 && lastRow == dm.getCount()-1){
34723             this.refresh();
34724         }else{
34725             if(!isUpdate){
34726                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34727             }
34728             var s = this.getScrollState();
34729             var markup = this.renderRows(firstRow, lastRow);
34730             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34731             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34732             this.restoreScroll(s);
34733             if(!isUpdate){
34734                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34735                 this.syncRowHeights(firstRow, lastRow);
34736                 this.stripeRows(firstRow);
34737                 this.layout();
34738             }
34739         }
34740     },
34741
34742     bufferRows : function(markup, target, index){
34743         var before = null, trows = target.rows, tbody = target.tBodies[0];
34744         if(index < trows.length){
34745             before = trows[index];
34746         }
34747         var b = document.createElement("div");
34748         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34749         var rows = b.firstChild.rows;
34750         for(var i = 0, len = rows.length; i < len; i++){
34751             if(before){
34752                 tbody.insertBefore(rows[0], before);
34753             }else{
34754                 tbody.appendChild(rows[0]);
34755             }
34756         }
34757         b.innerHTML = "";
34758         b = null;
34759     },
34760
34761     deleteRows : function(dm, firstRow, lastRow){
34762         if(dm.getRowCount()<1){
34763             this.fireEvent("beforerefresh", this);
34764             this.mainBody.update("");
34765             this.lockedBody.update("");
34766             this.fireEvent("refresh", this);
34767         }else{
34768             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34769             var bt = this.getBodyTable();
34770             var tbody = bt.firstChild;
34771             var rows = bt.rows;
34772             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34773                 tbody.removeChild(rows[firstRow]);
34774             }
34775             this.stripeRows(firstRow);
34776             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34777         }
34778     },
34779
34780     updateRows : function(dataSource, firstRow, lastRow){
34781         var s = this.getScrollState();
34782         this.refresh();
34783         this.restoreScroll(s);
34784     },
34785
34786     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34787         if(!noRefresh){
34788            this.refresh();
34789         }
34790         this.updateHeaderSortState();
34791     },
34792
34793     getScrollState : function(){
34794         
34795         var sb = this.scroller.dom;
34796         return {left: sb.scrollLeft, top: sb.scrollTop};
34797     },
34798
34799     stripeRows : function(startRow){
34800         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34801             return;
34802         }
34803         startRow = startRow || 0;
34804         var rows = this.getBodyTable().rows;
34805         var lrows = this.getLockedTable().rows;
34806         var cls = ' x-grid-row-alt ';
34807         for(var i = startRow, len = rows.length; i < len; i++){
34808             var row = rows[i], lrow = lrows[i];
34809             var isAlt = ((i+1) % 2 == 0);
34810             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34811             if(isAlt == hasAlt){
34812                 continue;
34813             }
34814             if(isAlt){
34815                 row.className += " x-grid-row-alt";
34816             }else{
34817                 row.className = row.className.replace("x-grid-row-alt", "");
34818             }
34819             if(lrow){
34820                 lrow.className = row.className;
34821             }
34822         }
34823     },
34824
34825     restoreScroll : function(state){
34826         //Roo.log('GridView.restoreScroll');
34827         var sb = this.scroller.dom;
34828         sb.scrollLeft = state.left;
34829         sb.scrollTop = state.top;
34830         this.syncScroll();
34831     },
34832
34833     syncScroll : function(){
34834         //Roo.log('GridView.syncScroll');
34835         var sb = this.scroller.dom;
34836         var sh = this.mainHd.dom;
34837         var bs = this.mainBody.dom;
34838         var lv = this.lockedBody.dom;
34839         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34840         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34841     },
34842
34843     handleScroll : function(e){
34844         this.syncScroll();
34845         var sb = this.scroller.dom;
34846         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34847         e.stopEvent();
34848     },
34849
34850     handleWheel : function(e){
34851         var d = e.getWheelDelta();
34852         this.scroller.dom.scrollTop -= d*22;
34853         // set this here to prevent jumpy scrolling on large tables
34854         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34855         e.stopEvent();
34856     },
34857
34858     renderRows : function(startRow, endRow){
34859         // pull in all the crap needed to render rows
34860         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34861         var colCount = cm.getColumnCount();
34862
34863         if(ds.getCount() < 1){
34864             return ["", ""];
34865         }
34866
34867         // build a map for all the columns
34868         var cs = [];
34869         for(var i = 0; i < colCount; i++){
34870             var name = cm.getDataIndex(i);
34871             cs[i] = {
34872                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34873                 renderer : cm.getRenderer(i),
34874                 id : cm.getColumnId(i),
34875                 locked : cm.isLocked(i)
34876             };
34877         }
34878
34879         startRow = startRow || 0;
34880         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34881
34882         // records to render
34883         var rs = ds.getRange(startRow, endRow);
34884
34885         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34886     },
34887
34888     // As much as I hate to duplicate code, this was branched because FireFox really hates
34889     // [].join("") on strings. The performance difference was substantial enough to
34890     // branch this function
34891     doRender : Roo.isGecko ?
34892             function(cs, rs, ds, startRow, colCount, stripe){
34893                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34894                 // buffers
34895                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34896                 
34897                 var hasListener = this.grid.hasListener('rowclass');
34898                 var rowcfg = {};
34899                 for(var j = 0, len = rs.length; j < len; j++){
34900                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34901                     for(var i = 0; i < colCount; i++){
34902                         c = cs[i];
34903                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34904                         p.id = c.id;
34905                         p.css = p.attr = "";
34906                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34907                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34908                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34909                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34910                         }
34911                         var markup = ct.apply(p);
34912                         if(!c.locked){
34913                             cb+= markup;
34914                         }else{
34915                             lcb+= markup;
34916                         }
34917                     }
34918                     var alt = [];
34919                     if(stripe && ((rowIndex+1) % 2 == 0)){
34920                         alt.push("x-grid-row-alt")
34921                     }
34922                     if(r.dirty){
34923                         alt.push(  " x-grid-dirty-row");
34924                     }
34925                     rp.cells = lcb;
34926                     if(this.getRowClass){
34927                         alt.push(this.getRowClass(r, rowIndex));
34928                     }
34929                     if (hasListener) {
34930                         rowcfg = {
34931                              
34932                             record: r,
34933                             rowIndex : rowIndex,
34934                             rowClass : ''
34935                         }
34936                         this.grid.fireEvent('rowclass', this, rowcfg);
34937                         alt.push(rowcfg.rowClass);
34938                     }
34939                     rp.alt = alt.join(" ");
34940                     lbuf+= rt.apply(rp);
34941                     rp.cells = cb;
34942                     buf+=  rt.apply(rp);
34943                 }
34944                 return [lbuf, buf];
34945             } :
34946             function(cs, rs, ds, startRow, colCount, stripe){
34947                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34948                 // buffers
34949                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34950                 var hasListener = this.grid.hasListener('rowclass');
34951  
34952                 var rowcfg = {};
34953                 for(var j = 0, len = rs.length; j < len; j++){
34954                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34955                     for(var i = 0; i < colCount; i++){
34956                         c = cs[i];
34957                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34958                         p.id = c.id;
34959                         p.css = p.attr = "";
34960                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34961                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34962                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34963                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34964                         }
34965                         
34966                         var markup = ct.apply(p);
34967                         if(!c.locked){
34968                             cb[cb.length] = markup;
34969                         }else{
34970                             lcb[lcb.length] = markup;
34971                         }
34972                     }
34973                     var alt = [];
34974                     if(stripe && ((rowIndex+1) % 2 == 0)){
34975                         alt.push( "x-grid-row-alt");
34976                     }
34977                     if(r.dirty){
34978                         alt.push(" x-grid-dirty-row");
34979                     }
34980                     rp.cells = lcb;
34981                     if(this.getRowClass){
34982                         alt.push( this.getRowClass(r, rowIndex));
34983                     }
34984                     if (hasListener) {
34985                         rowcfg = {
34986                              
34987                             record: r,
34988                             rowIndex : rowIndex,
34989                             rowClass : ''
34990                         }
34991                         this.grid.fireEvent('rowclass', this, rowcfg);
34992                         alt.push(rowcfg.rowClass);
34993                     }
34994                     rp.alt = alt.join(" ");
34995                     rp.cells = lcb.join("");
34996                     lbuf[lbuf.length] = rt.apply(rp);
34997                     rp.cells = cb.join("");
34998                     buf[buf.length] =  rt.apply(rp);
34999                 }
35000                 return [lbuf.join(""), buf.join("")];
35001             },
35002
35003     renderBody : function(){
35004         var markup = this.renderRows();
35005         var bt = this.templates.body;
35006         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35007     },
35008
35009     /**
35010      * Refreshes the grid
35011      * @param {Boolean} headersToo
35012      */
35013     refresh : function(headersToo){
35014         this.fireEvent("beforerefresh", this);
35015         this.grid.stopEditing();
35016         var result = this.renderBody();
35017         this.lockedBody.update(result[0]);
35018         this.mainBody.update(result[1]);
35019         if(headersToo === true){
35020             this.updateHeaders();
35021             this.updateColumns();
35022             this.updateSplitters();
35023             this.updateHeaderSortState();
35024         }
35025         this.syncRowHeights();
35026         this.layout();
35027         this.fireEvent("refresh", this);
35028     },
35029
35030     handleColumnMove : function(cm, oldIndex, newIndex){
35031         this.indexMap = null;
35032         var s = this.getScrollState();
35033         this.refresh(true);
35034         this.restoreScroll(s);
35035         this.afterMove(newIndex);
35036     },
35037
35038     afterMove : function(colIndex){
35039         if(this.enableMoveAnim && Roo.enableFx){
35040             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35041         }
35042         // if multisort - fix sortOrder, and reload..
35043         if (this.grid.dataSource.multiSort) {
35044             // the we can call sort again..
35045             var dm = this.grid.dataSource;
35046             var cm = this.grid.colModel;
35047             var so = [];
35048             for(var i = 0; i < cm.config.length; i++ ) {
35049                 
35050                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35051                     continue; // dont' bother, it's not in sort list or being set.
35052                 }
35053                 
35054                 so.push(cm.config[i].dataIndex);
35055             };
35056             dm.sortOrder = so;
35057             dm.load(dm.lastOptions);
35058             
35059             
35060         }
35061         
35062     },
35063
35064     updateCell : function(dm, rowIndex, dataIndex){
35065         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35066         if(typeof colIndex == "undefined"){ // not present in grid
35067             return;
35068         }
35069         var cm = this.grid.colModel;
35070         var cell = this.getCell(rowIndex, colIndex);
35071         var cellText = this.getCellText(rowIndex, colIndex);
35072
35073         var p = {
35074             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35075             id : cm.getColumnId(colIndex),
35076             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35077         };
35078         var renderer = cm.getRenderer(colIndex);
35079         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35080         if(typeof val == "undefined" || val === "") val = "&#160;";
35081         cellText.innerHTML = val;
35082         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35083         this.syncRowHeights(rowIndex, rowIndex);
35084     },
35085
35086     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35087         var maxWidth = 0;
35088         if(this.grid.autoSizeHeaders){
35089             var h = this.getHeaderCellMeasure(colIndex);
35090             maxWidth = Math.max(maxWidth, h.scrollWidth);
35091         }
35092         var tb, index;
35093         if(this.cm.isLocked(colIndex)){
35094             tb = this.getLockedTable();
35095             index = colIndex;
35096         }else{
35097             tb = this.getBodyTable();
35098             index = colIndex - this.cm.getLockedCount();
35099         }
35100         if(tb && tb.rows){
35101             var rows = tb.rows;
35102             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35103             for(var i = 0; i < stopIndex; i++){
35104                 var cell = rows[i].childNodes[index].firstChild;
35105                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35106             }
35107         }
35108         return maxWidth + /*margin for error in IE*/ 5;
35109     },
35110     /**
35111      * Autofit a column to its content.
35112      * @param {Number} colIndex
35113      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35114      */
35115      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35116          if(this.cm.isHidden(colIndex)){
35117              return; // can't calc a hidden column
35118          }
35119         if(forceMinSize){
35120             var cid = this.cm.getColumnId(colIndex);
35121             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35122            if(this.grid.autoSizeHeaders){
35123                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35124            }
35125         }
35126         var newWidth = this.calcColumnWidth(colIndex);
35127         this.cm.setColumnWidth(colIndex,
35128             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35129         if(!suppressEvent){
35130             this.grid.fireEvent("columnresize", colIndex, newWidth);
35131         }
35132     },
35133
35134     /**
35135      * Autofits all columns to their content and then expands to fit any extra space in the grid
35136      */
35137      autoSizeColumns : function(){
35138         var cm = this.grid.colModel;
35139         var colCount = cm.getColumnCount();
35140         for(var i = 0; i < colCount; i++){
35141             this.autoSizeColumn(i, true, true);
35142         }
35143         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35144             this.fitColumns();
35145         }else{
35146             this.updateColumns();
35147             this.layout();
35148         }
35149     },
35150
35151     /**
35152      * Autofits all columns to the grid's width proportionate with their current size
35153      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35154      */
35155     fitColumns : function(reserveScrollSpace){
35156         var cm = this.grid.colModel;
35157         var colCount = cm.getColumnCount();
35158         var cols = [];
35159         var width = 0;
35160         var i, w;
35161         for (i = 0; i < colCount; i++){
35162             if(!cm.isHidden(i) && !cm.isFixed(i)){
35163                 w = cm.getColumnWidth(i);
35164                 cols.push(i);
35165                 cols.push(w);
35166                 width += w;
35167             }
35168         }
35169         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35170         if(reserveScrollSpace){
35171             avail -= 17;
35172         }
35173         var frac = (avail - cm.getTotalWidth())/width;
35174         while (cols.length){
35175             w = cols.pop();
35176             i = cols.pop();
35177             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35178         }
35179         this.updateColumns();
35180         this.layout();
35181     },
35182
35183     onRowSelect : function(rowIndex){
35184         var row = this.getRowComposite(rowIndex);
35185         row.addClass("x-grid-row-selected");
35186     },
35187
35188     onRowDeselect : function(rowIndex){
35189         var row = this.getRowComposite(rowIndex);
35190         row.removeClass("x-grid-row-selected");
35191     },
35192
35193     onCellSelect : function(row, col){
35194         var cell = this.getCell(row, col);
35195         if(cell){
35196             Roo.fly(cell).addClass("x-grid-cell-selected");
35197         }
35198     },
35199
35200     onCellDeselect : function(row, col){
35201         var cell = this.getCell(row, col);
35202         if(cell){
35203             Roo.fly(cell).removeClass("x-grid-cell-selected");
35204         }
35205     },
35206
35207     updateHeaderSortState : function(){
35208         
35209         // sort state can be single { field: xxx, direction : yyy}
35210         // or   { xxx=>ASC , yyy : DESC ..... }
35211         
35212         var mstate = {};
35213         if (!this.ds.multiSort) { 
35214             var state = this.ds.getSortState();
35215             if(!state){
35216                 return;
35217             }
35218             mstate[state.field] = state.direction;
35219             // FIXME... - this is not used here.. but might be elsewhere..
35220             this.sortState = state;
35221             
35222         } else {
35223             mstate = this.ds.sortToggle;
35224         }
35225         //remove existing sort classes..
35226         
35227         var sc = this.sortClasses;
35228         var hds = this.el.select(this.headerSelector).removeClass(sc);
35229         
35230         for(var f in mstate) {
35231         
35232             var sortColumn = this.cm.findColumnIndex(f);
35233             
35234             if(sortColumn != -1){
35235                 var sortDir = mstate[f];        
35236                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35237             }
35238         }
35239         
35240          
35241         
35242     },
35243
35244
35245     handleHeaderClick : function(g, index){
35246         if(this.headersDisabled){
35247             return;
35248         }
35249         var dm = g.dataSource, cm = g.colModel;
35250         if(!cm.isSortable(index)){
35251             return;
35252         }
35253         g.stopEditing();
35254         
35255         if (dm.multiSort) {
35256             // update the sortOrder
35257             var so = [];
35258             for(var i = 0; i < cm.config.length; i++ ) {
35259                 
35260                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35261                     continue; // dont' bother, it's not in sort list or being set.
35262                 }
35263                 
35264                 so.push(cm.config[i].dataIndex);
35265             };
35266             dm.sortOrder = so;
35267         }
35268         
35269         
35270         dm.sort(cm.getDataIndex(index));
35271     },
35272
35273
35274     destroy : function(){
35275         if(this.colMenu){
35276             this.colMenu.removeAll();
35277             Roo.menu.MenuMgr.unregister(this.colMenu);
35278             this.colMenu.getEl().remove();
35279             delete this.colMenu;
35280         }
35281         if(this.hmenu){
35282             this.hmenu.removeAll();
35283             Roo.menu.MenuMgr.unregister(this.hmenu);
35284             this.hmenu.getEl().remove();
35285             delete this.hmenu;
35286         }
35287         if(this.grid.enableColumnMove){
35288             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35289             if(dds){
35290                 for(var dd in dds){
35291                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35292                         var elid = dds[dd].dragElId;
35293                         dds[dd].unreg();
35294                         Roo.get(elid).remove();
35295                     } else if(dds[dd].config.isTarget){
35296                         dds[dd].proxyTop.remove();
35297                         dds[dd].proxyBottom.remove();
35298                         dds[dd].unreg();
35299                     }
35300                     if(Roo.dd.DDM.locationCache[dd]){
35301                         delete Roo.dd.DDM.locationCache[dd];
35302                     }
35303                 }
35304                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35305             }
35306         }
35307         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35308         this.bind(null, null);
35309         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35310     },
35311
35312     handleLockChange : function(){
35313         this.refresh(true);
35314     },
35315
35316     onDenyColumnLock : function(){
35317
35318     },
35319
35320     onDenyColumnHide : function(){
35321
35322     },
35323
35324     handleHdMenuClick : function(item){
35325         var index = this.hdCtxIndex;
35326         var cm = this.cm, ds = this.ds;
35327         switch(item.id){
35328             case "asc":
35329                 ds.sort(cm.getDataIndex(index), "ASC");
35330                 break;
35331             case "desc":
35332                 ds.sort(cm.getDataIndex(index), "DESC");
35333                 break;
35334             case "lock":
35335                 var lc = cm.getLockedCount();
35336                 if(cm.getColumnCount(true) <= lc+1){
35337                     this.onDenyColumnLock();
35338                     return;
35339                 }
35340                 if(lc != index){
35341                     cm.setLocked(index, true, true);
35342                     cm.moveColumn(index, lc);
35343                     this.grid.fireEvent("columnmove", index, lc);
35344                 }else{
35345                     cm.setLocked(index, true);
35346                 }
35347             break;
35348             case "unlock":
35349                 var lc = cm.getLockedCount();
35350                 if((lc-1) != index){
35351                     cm.setLocked(index, false, true);
35352                     cm.moveColumn(index, lc-1);
35353                     this.grid.fireEvent("columnmove", index, lc-1);
35354                 }else{
35355                     cm.setLocked(index, false);
35356                 }
35357             break;
35358             default:
35359                 index = cm.getIndexById(item.id.substr(4));
35360                 if(index != -1){
35361                     if(item.checked && cm.getColumnCount(true) <= 1){
35362                         this.onDenyColumnHide();
35363                         return false;
35364                     }
35365                     cm.setHidden(index, item.checked);
35366                 }
35367         }
35368         return true;
35369     },
35370
35371     beforeColMenuShow : function(){
35372         var cm = this.cm,  colCount = cm.getColumnCount();
35373         this.colMenu.removeAll();
35374         for(var i = 0; i < colCount; i++){
35375             this.colMenu.add(new Roo.menu.CheckItem({
35376                 id: "col-"+cm.getColumnId(i),
35377                 text: cm.getColumnHeader(i),
35378                 checked: !cm.isHidden(i),
35379                 hideOnClick:false
35380             }));
35381         }
35382     },
35383
35384     handleHdCtx : function(g, index, e){
35385         e.stopEvent();
35386         var hd = this.getHeaderCell(index);
35387         this.hdCtxIndex = index;
35388         var ms = this.hmenu.items, cm = this.cm;
35389         ms.get("asc").setDisabled(!cm.isSortable(index));
35390         ms.get("desc").setDisabled(!cm.isSortable(index));
35391         if(this.grid.enableColLock !== false){
35392             ms.get("lock").setDisabled(cm.isLocked(index));
35393             ms.get("unlock").setDisabled(!cm.isLocked(index));
35394         }
35395         this.hmenu.show(hd, "tl-bl");
35396     },
35397
35398     handleHdOver : function(e){
35399         var hd = this.findHeaderCell(e.getTarget());
35400         if(hd && !this.headersDisabled){
35401             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35402                this.fly(hd).addClass("x-grid-hd-over");
35403             }
35404         }
35405     },
35406
35407     handleHdOut : function(e){
35408         var hd = this.findHeaderCell(e.getTarget());
35409         if(hd){
35410             this.fly(hd).removeClass("x-grid-hd-over");
35411         }
35412     },
35413
35414     handleSplitDblClick : function(e, t){
35415         var i = this.getCellIndex(t);
35416         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35417             this.autoSizeColumn(i, true);
35418             this.layout();
35419         }
35420     },
35421
35422     render : function(){
35423
35424         var cm = this.cm;
35425         var colCount = cm.getColumnCount();
35426
35427         if(this.grid.monitorWindowResize === true){
35428             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35429         }
35430         var header = this.renderHeaders();
35431         var body = this.templates.body.apply({rows:""});
35432         var html = this.templates.master.apply({
35433             lockedBody: body,
35434             body: body,
35435             lockedHeader: header[0],
35436             header: header[1]
35437         });
35438
35439         //this.updateColumns();
35440
35441         this.grid.getGridEl().dom.innerHTML = html;
35442
35443         this.initElements();
35444         
35445         // a kludge to fix the random scolling effect in webkit
35446         this.el.on("scroll", function() {
35447             this.el.dom.scrollTop=0; // hopefully not recursive..
35448         },this);
35449
35450         this.scroller.on("scroll", this.handleScroll, this);
35451         this.lockedBody.on("mousewheel", this.handleWheel, this);
35452         this.mainBody.on("mousewheel", this.handleWheel, this);
35453
35454         this.mainHd.on("mouseover", this.handleHdOver, this);
35455         this.mainHd.on("mouseout", this.handleHdOut, this);
35456         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35457                 {delegate: "."+this.splitClass});
35458
35459         this.lockedHd.on("mouseover", this.handleHdOver, this);
35460         this.lockedHd.on("mouseout", this.handleHdOut, this);
35461         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35462                 {delegate: "."+this.splitClass});
35463
35464         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35465             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35466         }
35467
35468         this.updateSplitters();
35469
35470         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35471             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35472             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35473         }
35474
35475         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35476             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35477             this.hmenu.add(
35478                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35479                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35480             );
35481             if(this.grid.enableColLock !== false){
35482                 this.hmenu.add('-',
35483                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35484                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35485                 );
35486             }
35487             if(this.grid.enableColumnHide !== false){
35488
35489                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35490                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35491                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35492
35493                 this.hmenu.add('-',
35494                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35495                 );
35496             }
35497             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35498
35499             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35500         }
35501
35502         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35503             this.dd = new Roo.grid.GridDragZone(this.grid, {
35504                 ddGroup : this.grid.ddGroup || 'GridDD'
35505             });
35506         }
35507
35508         /*
35509         for(var i = 0; i < colCount; i++){
35510             if(cm.isHidden(i)){
35511                 this.hideColumn(i);
35512             }
35513             if(cm.config[i].align){
35514                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35515                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35516             }
35517         }*/
35518         
35519         this.updateHeaderSortState();
35520
35521         this.beforeInitialResize();
35522         this.layout(true);
35523
35524         // two part rendering gives faster view to the user
35525         this.renderPhase2.defer(1, this);
35526     },
35527
35528     renderPhase2 : function(){
35529         // render the rows now
35530         this.refresh();
35531         if(this.grid.autoSizeColumns){
35532             this.autoSizeColumns();
35533         }
35534     },
35535
35536     beforeInitialResize : function(){
35537
35538     },
35539
35540     onColumnSplitterMoved : function(i, w){
35541         this.userResized = true;
35542         var cm = this.grid.colModel;
35543         cm.setColumnWidth(i, w, true);
35544         var cid = cm.getColumnId(i);
35545         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35546         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35547         this.updateSplitters();
35548         this.layout();
35549         this.grid.fireEvent("columnresize", i, w);
35550     },
35551
35552     syncRowHeights : function(startIndex, endIndex){
35553         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35554             startIndex = startIndex || 0;
35555             var mrows = this.getBodyTable().rows;
35556             var lrows = this.getLockedTable().rows;
35557             var len = mrows.length-1;
35558             endIndex = Math.min(endIndex || len, len);
35559             for(var i = startIndex; i <= endIndex; i++){
35560                 var m = mrows[i], l = lrows[i];
35561                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35562                 m.style.height = l.style.height = h + "px";
35563             }
35564         }
35565     },
35566
35567     layout : function(initialRender, is2ndPass){
35568         var g = this.grid;
35569         var auto = g.autoHeight;
35570         var scrollOffset = 16;
35571         var c = g.getGridEl(), cm = this.cm,
35572                 expandCol = g.autoExpandColumn,
35573                 gv = this;
35574         //c.beginMeasure();
35575
35576         if(!c.dom.offsetWidth){ // display:none?
35577             if(initialRender){
35578                 this.lockedWrap.show();
35579                 this.mainWrap.show();
35580             }
35581             return;
35582         }
35583
35584         var hasLock = this.cm.isLocked(0);
35585
35586         var tbh = this.headerPanel.getHeight();
35587         var bbh = this.footerPanel.getHeight();
35588
35589         if(auto){
35590             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35591             var newHeight = ch + c.getBorderWidth("tb");
35592             if(g.maxHeight){
35593                 newHeight = Math.min(g.maxHeight, newHeight);
35594             }
35595             c.setHeight(newHeight);
35596         }
35597
35598         if(g.autoWidth){
35599             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35600         }
35601
35602         var s = this.scroller;
35603
35604         var csize = c.getSize(true);
35605
35606         this.el.setSize(csize.width, csize.height);
35607
35608         this.headerPanel.setWidth(csize.width);
35609         this.footerPanel.setWidth(csize.width);
35610
35611         var hdHeight = this.mainHd.getHeight();
35612         var vw = csize.width;
35613         var vh = csize.height - (tbh + bbh);
35614
35615         s.setSize(vw, vh);
35616
35617         var bt = this.getBodyTable();
35618         var ltWidth = hasLock ?
35619                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35620
35621         var scrollHeight = bt.offsetHeight;
35622         var scrollWidth = ltWidth + bt.offsetWidth;
35623         var vscroll = false, hscroll = false;
35624
35625         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35626
35627         var lw = this.lockedWrap, mw = this.mainWrap;
35628         var lb = this.lockedBody, mb = this.mainBody;
35629
35630         setTimeout(function(){
35631             var t = s.dom.offsetTop;
35632             var w = s.dom.clientWidth,
35633                 h = s.dom.clientHeight;
35634
35635             lw.setTop(t);
35636             lw.setSize(ltWidth, h);
35637
35638             mw.setLeftTop(ltWidth, t);
35639             mw.setSize(w-ltWidth, h);
35640
35641             lb.setHeight(h-hdHeight);
35642             mb.setHeight(h-hdHeight);
35643
35644             if(is2ndPass !== true && !gv.userResized && expandCol){
35645                 // high speed resize without full column calculation
35646                 
35647                 var ci = cm.getIndexById(expandCol);
35648                 if (ci < 0) {
35649                     ci = cm.findColumnIndex(expandCol);
35650                 }
35651                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35652                 var expandId = cm.getColumnId(ci);
35653                 var  tw = cm.getTotalWidth(false);
35654                 var currentWidth = cm.getColumnWidth(ci);
35655                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35656                 if(currentWidth != cw){
35657                     cm.setColumnWidth(ci, cw, true);
35658                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35659                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35660                     gv.updateSplitters();
35661                     gv.layout(false, true);
35662                 }
35663             }
35664
35665             if(initialRender){
35666                 lw.show();
35667                 mw.show();
35668             }
35669             //c.endMeasure();
35670         }, 10);
35671     },
35672
35673     onWindowResize : function(){
35674         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35675             return;
35676         }
35677         this.layout();
35678     },
35679
35680     appendFooter : function(parentEl){
35681         return null;
35682     },
35683
35684     sortAscText : "Sort Ascending",
35685     sortDescText : "Sort Descending",
35686     lockText : "Lock Column",
35687     unlockText : "Unlock Column",
35688     columnsText : "Columns"
35689 });
35690
35691
35692 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35693     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35694     this.proxy.el.addClass('x-grid3-col-dd');
35695 };
35696
35697 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35698     handleMouseDown : function(e){
35699
35700     },
35701
35702     callHandleMouseDown : function(e){
35703         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35704     }
35705 });
35706 /*
35707  * Based on:
35708  * Ext JS Library 1.1.1
35709  * Copyright(c) 2006-2007, Ext JS, LLC.
35710  *
35711  * Originally Released Under LGPL - original licence link has changed is not relivant.
35712  *
35713  * Fork - LGPL
35714  * <script type="text/javascript">
35715  */
35716  
35717 // private
35718 // This is a support class used internally by the Grid components
35719 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35720     this.grid = grid;
35721     this.view = grid.getView();
35722     this.proxy = this.view.resizeProxy;
35723     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35724         "gridSplitters" + this.grid.getGridEl().id, {
35725         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35726     });
35727     this.setHandleElId(Roo.id(hd));
35728     this.setOuterHandleElId(Roo.id(hd2));
35729     this.scroll = false;
35730 };
35731 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35732     fly: Roo.Element.fly,
35733
35734     b4StartDrag : function(x, y){
35735         this.view.headersDisabled = true;
35736         this.proxy.setHeight(this.view.mainWrap.getHeight());
35737         var w = this.cm.getColumnWidth(this.cellIndex);
35738         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35739         this.resetConstraints();
35740         this.setXConstraint(minw, 1000);
35741         this.setYConstraint(0, 0);
35742         this.minX = x - minw;
35743         this.maxX = x + 1000;
35744         this.startPos = x;
35745         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35746     },
35747
35748
35749     handleMouseDown : function(e){
35750         ev = Roo.EventObject.setEvent(e);
35751         var t = this.fly(ev.getTarget());
35752         if(t.hasClass("x-grid-split")){
35753             this.cellIndex = this.view.getCellIndex(t.dom);
35754             this.split = t.dom;
35755             this.cm = this.grid.colModel;
35756             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35757                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35758             }
35759         }
35760     },
35761
35762     endDrag : function(e){
35763         this.view.headersDisabled = false;
35764         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35765         var diff = endX - this.startPos;
35766         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35767     },
35768
35769     autoOffset : function(){
35770         this.setDelta(0,0);
35771     }
35772 });/*
35773  * Based on:
35774  * Ext JS Library 1.1.1
35775  * Copyright(c) 2006-2007, Ext JS, LLC.
35776  *
35777  * Originally Released Under LGPL - original licence link has changed is not relivant.
35778  *
35779  * Fork - LGPL
35780  * <script type="text/javascript">
35781  */
35782  
35783 // private
35784 // This is a support class used internally by the Grid components
35785 Roo.grid.GridDragZone = function(grid, config){
35786     this.view = grid.getView();
35787     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35788     if(this.view.lockedBody){
35789         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35790         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35791     }
35792     this.scroll = false;
35793     this.grid = grid;
35794     this.ddel = document.createElement('div');
35795     this.ddel.className = 'x-grid-dd-wrap';
35796 };
35797
35798 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35799     ddGroup : "GridDD",
35800
35801     getDragData : function(e){
35802         var t = Roo.lib.Event.getTarget(e);
35803         var rowIndex = this.view.findRowIndex(t);
35804         if(rowIndex !== false){
35805             var sm = this.grid.selModel;
35806             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35807               //  sm.mouseDown(e, t);
35808             //}
35809             if (e.hasModifier()){
35810                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35811             }
35812             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
35813         }
35814         return false;
35815     },
35816
35817     onInitDrag : function(e){
35818         var data = this.dragData;
35819         this.ddel.innerHTML = this.grid.getDragDropText();
35820         this.proxy.update(this.ddel);
35821         // fire start drag?
35822     },
35823
35824     afterRepair : function(){
35825         this.dragging = false;
35826     },
35827
35828     getRepairXY : function(e, data){
35829         return false;
35830     },
35831
35832     onEndDrag : function(data, e){
35833         // fire end drag?
35834     },
35835
35836     onValidDrop : function(dd, e, id){
35837         // fire drag drop?
35838         this.hideProxy();
35839     },
35840
35841     beforeInvalidDrop : function(e, id){
35842
35843     }
35844 });/*
35845  * Based on:
35846  * Ext JS Library 1.1.1
35847  * Copyright(c) 2006-2007, Ext JS, LLC.
35848  *
35849  * Originally Released Under LGPL - original licence link has changed is not relivant.
35850  *
35851  * Fork - LGPL
35852  * <script type="text/javascript">
35853  */
35854  
35855
35856 /**
35857  * @class Roo.grid.ColumnModel
35858  * @extends Roo.util.Observable
35859  * This is the default implementation of a ColumnModel used by the Grid. It defines
35860  * the columns in the grid.
35861  * <br>Usage:<br>
35862  <pre><code>
35863  var colModel = new Roo.grid.ColumnModel([
35864         {header: "Ticker", width: 60, sortable: true, locked: true},
35865         {header: "Company Name", width: 150, sortable: true},
35866         {header: "Market Cap.", width: 100, sortable: true},
35867         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35868         {header: "Employees", width: 100, sortable: true, resizable: false}
35869  ]);
35870  </code></pre>
35871  * <p>
35872  
35873  * The config options listed for this class are options which may appear in each
35874  * individual column definition.
35875  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35876  * @constructor
35877  * @param {Object} config An Array of column config objects. See this class's
35878  * config objects for details.
35879 */
35880 Roo.grid.ColumnModel = function(config){
35881         /**
35882      * The config passed into the constructor
35883      */
35884     this.config = config;
35885     this.lookup = {};
35886
35887     // if no id, create one
35888     // if the column does not have a dataIndex mapping,
35889     // map it to the order it is in the config
35890     for(var i = 0, len = config.length; i < len; i++){
35891         var c = config[i];
35892         if(typeof c.dataIndex == "undefined"){
35893             c.dataIndex = i;
35894         }
35895         if(typeof c.renderer == "string"){
35896             c.renderer = Roo.util.Format[c.renderer];
35897         }
35898         if(typeof c.id == "undefined"){
35899             c.id = Roo.id();
35900         }
35901         if(c.editor && c.editor.xtype){
35902             c.editor  = Roo.factory(c.editor, Roo.grid);
35903         }
35904         if(c.editor && c.editor.isFormField){
35905             c.editor = new Roo.grid.GridEditor(c.editor);
35906         }
35907         this.lookup[c.id] = c;
35908     }
35909
35910     /**
35911      * The width of columns which have no width specified (defaults to 100)
35912      * @type Number
35913      */
35914     this.defaultWidth = 100;
35915
35916     /**
35917      * Default sortable of columns which have no sortable specified (defaults to false)
35918      * @type Boolean
35919      */
35920     this.defaultSortable = false;
35921
35922     this.addEvents({
35923         /**
35924              * @event widthchange
35925              * Fires when the width of a column changes.
35926              * @param {ColumnModel} this
35927              * @param {Number} columnIndex The column index
35928              * @param {Number} newWidth The new width
35929              */
35930             "widthchange": true,
35931         /**
35932              * @event headerchange
35933              * Fires when the text of a header changes.
35934              * @param {ColumnModel} this
35935              * @param {Number} columnIndex The column index
35936              * @param {Number} newText The new header text
35937              */
35938             "headerchange": true,
35939         /**
35940              * @event hiddenchange
35941              * Fires when a column is hidden or "unhidden".
35942              * @param {ColumnModel} this
35943              * @param {Number} columnIndex The column index
35944              * @param {Boolean} hidden true if hidden, false otherwise
35945              */
35946             "hiddenchange": true,
35947             /**
35948          * @event columnmoved
35949          * Fires when a column is moved.
35950          * @param {ColumnModel} this
35951          * @param {Number} oldIndex
35952          * @param {Number} newIndex
35953          */
35954         "columnmoved" : true,
35955         /**
35956          * @event columlockchange
35957          * Fires when a column's locked state is changed
35958          * @param {ColumnModel} this
35959          * @param {Number} colIndex
35960          * @param {Boolean} locked true if locked
35961          */
35962         "columnlockchange" : true
35963     });
35964     Roo.grid.ColumnModel.superclass.constructor.call(this);
35965 };
35966 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35967     /**
35968      * @cfg {String} header The header text to display in the Grid view.
35969      */
35970     /**
35971      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35972      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35973      * specified, the column's index is used as an index into the Record's data Array.
35974      */
35975     /**
35976      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35977      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35978      */
35979     /**
35980      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35981      * Defaults to the value of the {@link #defaultSortable} property.
35982      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35983      */
35984     /**
35985      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35986      */
35987     /**
35988      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35989      */
35990     /**
35991      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35992      */
35993     /**
35994      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35995      */
35996     /**
35997      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35998      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35999      * default renderer uses the raw data value.
36000      */
36001        /**
36002      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36003      */
36004     /**
36005      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36006      */
36007
36008     /**
36009      * Returns the id of the column at the specified index.
36010      * @param {Number} index The column index
36011      * @return {String} the id
36012      */
36013     getColumnId : function(index){
36014         return this.config[index].id;
36015     },
36016
36017     /**
36018      * Returns the column for a specified id.
36019      * @param {String} id The column id
36020      * @return {Object} the column
36021      */
36022     getColumnById : function(id){
36023         return this.lookup[id];
36024     },
36025
36026     
36027     /**
36028      * Returns the column for a specified dataIndex.
36029      * @param {String} dataIndex The column dataIndex
36030      * @return {Object|Boolean} the column or false if not found
36031      */
36032     getColumnByDataIndex: function(dataIndex){
36033         var index = this.findColumnIndex(dataIndex);
36034         return index > -1 ? this.config[index] : false;
36035     },
36036     
36037     /**
36038      * Returns the index for a specified column id.
36039      * @param {String} id The column id
36040      * @return {Number} the index, or -1 if not found
36041      */
36042     getIndexById : function(id){
36043         for(var i = 0, len = this.config.length; i < len; i++){
36044             if(this.config[i].id == id){
36045                 return i;
36046             }
36047         }
36048         return -1;
36049     },
36050     
36051     /**
36052      * Returns the index for a specified column dataIndex.
36053      * @param {String} dataIndex The column dataIndex
36054      * @return {Number} the index, or -1 if not found
36055      */
36056     
36057     findColumnIndex : function(dataIndex){
36058         for(var i = 0, len = this.config.length; i < len; i++){
36059             if(this.config[i].dataIndex == dataIndex){
36060                 return i;
36061             }
36062         }
36063         return -1;
36064     },
36065     
36066     
36067     moveColumn : function(oldIndex, newIndex){
36068         var c = this.config[oldIndex];
36069         this.config.splice(oldIndex, 1);
36070         this.config.splice(newIndex, 0, c);
36071         this.dataMap = null;
36072         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36073     },
36074
36075     isLocked : function(colIndex){
36076         return this.config[colIndex].locked === true;
36077     },
36078
36079     setLocked : function(colIndex, value, suppressEvent){
36080         if(this.isLocked(colIndex) == value){
36081             return;
36082         }
36083         this.config[colIndex].locked = value;
36084         if(!suppressEvent){
36085             this.fireEvent("columnlockchange", this, colIndex, value);
36086         }
36087     },
36088
36089     getTotalLockedWidth : function(){
36090         var totalWidth = 0;
36091         for(var i = 0; i < this.config.length; i++){
36092             if(this.isLocked(i) && !this.isHidden(i)){
36093                 this.totalWidth += this.getColumnWidth(i);
36094             }
36095         }
36096         return totalWidth;
36097     },
36098
36099     getLockedCount : function(){
36100         for(var i = 0, len = this.config.length; i < len; i++){
36101             if(!this.isLocked(i)){
36102                 return i;
36103             }
36104         }
36105     },
36106
36107     /**
36108      * Returns the number of columns.
36109      * @return {Number}
36110      */
36111     getColumnCount : function(visibleOnly){
36112         if(visibleOnly === true){
36113             var c = 0;
36114             for(var i = 0, len = this.config.length; i < len; i++){
36115                 if(!this.isHidden(i)){
36116                     c++;
36117                 }
36118             }
36119             return c;
36120         }
36121         return this.config.length;
36122     },
36123
36124     /**
36125      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36126      * @param {Function} fn
36127      * @param {Object} scope (optional)
36128      * @return {Array} result
36129      */
36130     getColumnsBy : function(fn, scope){
36131         var r = [];
36132         for(var i = 0, len = this.config.length; i < len; i++){
36133             var c = this.config[i];
36134             if(fn.call(scope||this, c, i) === true){
36135                 r[r.length] = c;
36136             }
36137         }
36138         return r;
36139     },
36140
36141     /**
36142      * Returns true if the specified column is sortable.
36143      * @param {Number} col The column index
36144      * @return {Boolean}
36145      */
36146     isSortable : function(col){
36147         if(typeof this.config[col].sortable == "undefined"){
36148             return this.defaultSortable;
36149         }
36150         return this.config[col].sortable;
36151     },
36152
36153     /**
36154      * Returns the rendering (formatting) function defined for the column.
36155      * @param {Number} col The column index.
36156      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36157      */
36158     getRenderer : function(col){
36159         if(!this.config[col].renderer){
36160             return Roo.grid.ColumnModel.defaultRenderer;
36161         }
36162         return this.config[col].renderer;
36163     },
36164
36165     /**
36166      * Sets the rendering (formatting) function for a column.
36167      * @param {Number} col The column index
36168      * @param {Function} fn The function to use to process the cell's raw data
36169      * to return HTML markup for the grid view. The render function is called with
36170      * the following parameters:<ul>
36171      * <li>Data value.</li>
36172      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36173      * <li>css A CSS style string to apply to the table cell.</li>
36174      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36175      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36176      * <li>Row index</li>
36177      * <li>Column index</li>
36178      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36179      */
36180     setRenderer : function(col, fn){
36181         this.config[col].renderer = fn;
36182     },
36183
36184     /**
36185      * Returns the width for the specified column.
36186      * @param {Number} col The column index
36187      * @return {Number}
36188      */
36189     getColumnWidth : function(col){
36190         return this.config[col].width * 1 || this.defaultWidth;
36191     },
36192
36193     /**
36194      * Sets the width for a column.
36195      * @param {Number} col The column index
36196      * @param {Number} width The new width
36197      */
36198     setColumnWidth : function(col, width, suppressEvent){
36199         this.config[col].width = width;
36200         this.totalWidth = null;
36201         if(!suppressEvent){
36202              this.fireEvent("widthchange", this, col, width);
36203         }
36204     },
36205
36206     /**
36207      * Returns the total width of all columns.
36208      * @param {Boolean} includeHidden True to include hidden column widths
36209      * @return {Number}
36210      */
36211     getTotalWidth : function(includeHidden){
36212         if(!this.totalWidth){
36213             this.totalWidth = 0;
36214             for(var i = 0, len = this.config.length; i < len; i++){
36215                 if(includeHidden || !this.isHidden(i)){
36216                     this.totalWidth += this.getColumnWidth(i);
36217                 }
36218             }
36219         }
36220         return this.totalWidth;
36221     },
36222
36223     /**
36224      * Returns the header for the specified column.
36225      * @param {Number} col The column index
36226      * @return {String}
36227      */
36228     getColumnHeader : function(col){
36229         return this.config[col].header;
36230     },
36231
36232     /**
36233      * Sets the header for a column.
36234      * @param {Number} col The column index
36235      * @param {String} header The new header
36236      */
36237     setColumnHeader : function(col, header){
36238         this.config[col].header = header;
36239         this.fireEvent("headerchange", this, col, header);
36240     },
36241
36242     /**
36243      * Returns the tooltip for the specified column.
36244      * @param {Number} col The column index
36245      * @return {String}
36246      */
36247     getColumnTooltip : function(col){
36248             return this.config[col].tooltip;
36249     },
36250     /**
36251      * Sets the tooltip for a column.
36252      * @param {Number} col The column index
36253      * @param {String} tooltip The new tooltip
36254      */
36255     setColumnTooltip : function(col, tooltip){
36256             this.config[col].tooltip = tooltip;
36257     },
36258
36259     /**
36260      * Returns the dataIndex for the specified column.
36261      * @param {Number} col The column index
36262      * @return {Number}
36263      */
36264     getDataIndex : function(col){
36265         return this.config[col].dataIndex;
36266     },
36267
36268     /**
36269      * Sets the dataIndex for a column.
36270      * @param {Number} col The column index
36271      * @param {Number} dataIndex The new dataIndex
36272      */
36273     setDataIndex : function(col, dataIndex){
36274         this.config[col].dataIndex = dataIndex;
36275     },
36276
36277     
36278     
36279     /**
36280      * Returns true if the cell is editable.
36281      * @param {Number} colIndex The column index
36282      * @param {Number} rowIndex The row index
36283      * @return {Boolean}
36284      */
36285     isCellEditable : function(colIndex, rowIndex){
36286         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36287     },
36288
36289     /**
36290      * Returns the editor defined for the cell/column.
36291      * return false or null to disable editing.
36292      * @param {Number} colIndex The column index
36293      * @param {Number} rowIndex The row index
36294      * @return {Object}
36295      */
36296     getCellEditor : function(colIndex, rowIndex){
36297         return this.config[colIndex].editor;
36298     },
36299
36300     /**
36301      * Sets if a column is editable.
36302      * @param {Number} col The column index
36303      * @param {Boolean} editable True if the column is editable
36304      */
36305     setEditable : function(col, editable){
36306         this.config[col].editable = editable;
36307     },
36308
36309
36310     /**
36311      * Returns true if the column is hidden.
36312      * @param {Number} colIndex The column index
36313      * @return {Boolean}
36314      */
36315     isHidden : function(colIndex){
36316         return this.config[colIndex].hidden;
36317     },
36318
36319
36320     /**
36321      * Returns true if the column width cannot be changed
36322      */
36323     isFixed : function(colIndex){
36324         return this.config[colIndex].fixed;
36325     },
36326
36327     /**
36328      * Returns true if the column can be resized
36329      * @return {Boolean}
36330      */
36331     isResizable : function(colIndex){
36332         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36333     },
36334     /**
36335      * Sets if a column is hidden.
36336      * @param {Number} colIndex The column index
36337      * @param {Boolean} hidden True if the column is hidden
36338      */
36339     setHidden : function(colIndex, hidden){
36340         this.config[colIndex].hidden = hidden;
36341         this.totalWidth = null;
36342         this.fireEvent("hiddenchange", this, colIndex, hidden);
36343     },
36344
36345     /**
36346      * Sets the editor for a column.
36347      * @param {Number} col The column index
36348      * @param {Object} editor The editor object
36349      */
36350     setEditor : function(col, editor){
36351         this.config[col].editor = editor;
36352     }
36353 });
36354
36355 Roo.grid.ColumnModel.defaultRenderer = function(value){
36356         if(typeof value == "string" && value.length < 1){
36357             return "&#160;";
36358         }
36359         return value;
36360 };
36361
36362 // Alias for backwards compatibility
36363 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36364 /*
36365  * Based on:
36366  * Ext JS Library 1.1.1
36367  * Copyright(c) 2006-2007, Ext JS, LLC.
36368  *
36369  * Originally Released Under LGPL - original licence link has changed is not relivant.
36370  *
36371  * Fork - LGPL
36372  * <script type="text/javascript">
36373  */
36374
36375 /**
36376  * @class Roo.grid.AbstractSelectionModel
36377  * @extends Roo.util.Observable
36378  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36379  * implemented by descendant classes.  This class should not be directly instantiated.
36380  * @constructor
36381  */
36382 Roo.grid.AbstractSelectionModel = function(){
36383     this.locked = false;
36384     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36385 };
36386
36387 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36388     /** @ignore Called by the grid automatically. Do not call directly. */
36389     init : function(grid){
36390         this.grid = grid;
36391         this.initEvents();
36392     },
36393
36394     /**
36395      * Locks the selections.
36396      */
36397     lock : function(){
36398         this.locked = true;
36399     },
36400
36401     /**
36402      * Unlocks the selections.
36403      */
36404     unlock : function(){
36405         this.locked = false;
36406     },
36407
36408     /**
36409      * Returns true if the selections are locked.
36410      * @return {Boolean}
36411      */
36412     isLocked : function(){
36413         return this.locked;
36414     }
36415 });/*
36416  * Based on:
36417  * Ext JS Library 1.1.1
36418  * Copyright(c) 2006-2007, Ext JS, LLC.
36419  *
36420  * Originally Released Under LGPL - original licence link has changed is not relivant.
36421  *
36422  * Fork - LGPL
36423  * <script type="text/javascript">
36424  */
36425 /**
36426  * @extends Roo.grid.AbstractSelectionModel
36427  * @class Roo.grid.RowSelectionModel
36428  * The default SelectionModel used by {@link Roo.grid.Grid}.
36429  * It supports multiple selections and keyboard selection/navigation. 
36430  * @constructor
36431  * @param {Object} config
36432  */
36433 Roo.grid.RowSelectionModel = function(config){
36434     Roo.apply(this, config);
36435     this.selections = new Roo.util.MixedCollection(false, function(o){
36436         return o.id;
36437     });
36438
36439     this.last = false;
36440     this.lastActive = false;
36441
36442     this.addEvents({
36443         /**
36444              * @event selectionchange
36445              * Fires when the selection changes
36446              * @param {SelectionModel} this
36447              */
36448             "selectionchange" : true,
36449         /**
36450              * @event afterselectionchange
36451              * Fires after the selection changes (eg. by key press or clicking)
36452              * @param {SelectionModel} this
36453              */
36454             "afterselectionchange" : true,
36455         /**
36456              * @event beforerowselect
36457              * Fires when a row is selected being selected, return false to cancel.
36458              * @param {SelectionModel} this
36459              * @param {Number} rowIndex The selected index
36460              * @param {Boolean} keepExisting False if other selections will be cleared
36461              */
36462             "beforerowselect" : true,
36463         /**
36464              * @event rowselect
36465              * Fires when a row is selected.
36466              * @param {SelectionModel} this
36467              * @param {Number} rowIndex The selected index
36468              * @param {Roo.data.Record} r The record
36469              */
36470             "rowselect" : true,
36471         /**
36472              * @event rowdeselect
36473              * Fires when a row is deselected.
36474              * @param {SelectionModel} this
36475              * @param {Number} rowIndex The selected index
36476              */
36477         "rowdeselect" : true
36478     });
36479     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36480     this.locked = false;
36481 };
36482
36483 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36484     /**
36485      * @cfg {Boolean} singleSelect
36486      * True to allow selection of only one row at a time (defaults to false)
36487      */
36488     singleSelect : false,
36489
36490     // private
36491     initEvents : function(){
36492
36493         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36494             this.grid.on("mousedown", this.handleMouseDown, this);
36495         }else{ // allow click to work like normal
36496             this.grid.on("rowclick", this.handleDragableRowClick, this);
36497         }
36498
36499         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36500             "up" : function(e){
36501                 if(!e.shiftKey){
36502                     this.selectPrevious(e.shiftKey);
36503                 }else if(this.last !== false && this.lastActive !== false){
36504                     var last = this.last;
36505                     this.selectRange(this.last,  this.lastActive-1);
36506                     this.grid.getView().focusRow(this.lastActive);
36507                     if(last !== false){
36508                         this.last = last;
36509                     }
36510                 }else{
36511                     this.selectFirstRow();
36512                 }
36513                 this.fireEvent("afterselectionchange", this);
36514             },
36515             "down" : function(e){
36516                 if(!e.shiftKey){
36517                     this.selectNext(e.shiftKey);
36518                 }else if(this.last !== false && this.lastActive !== false){
36519                     var last = this.last;
36520                     this.selectRange(this.last,  this.lastActive+1);
36521                     this.grid.getView().focusRow(this.lastActive);
36522                     if(last !== false){
36523                         this.last = last;
36524                     }
36525                 }else{
36526                     this.selectFirstRow();
36527                 }
36528                 this.fireEvent("afterselectionchange", this);
36529             },
36530             scope: this
36531         });
36532
36533         var view = this.grid.view;
36534         view.on("refresh", this.onRefresh, this);
36535         view.on("rowupdated", this.onRowUpdated, this);
36536         view.on("rowremoved", this.onRemove, this);
36537     },
36538
36539     // private
36540     onRefresh : function(){
36541         var ds = this.grid.dataSource, i, v = this.grid.view;
36542         var s = this.selections;
36543         s.each(function(r){
36544             if((i = ds.indexOfId(r.id)) != -1){
36545                 v.onRowSelect(i);
36546             }else{
36547                 s.remove(r);
36548             }
36549         });
36550     },
36551
36552     // private
36553     onRemove : function(v, index, r){
36554         this.selections.remove(r);
36555     },
36556
36557     // private
36558     onRowUpdated : function(v, index, r){
36559         if(this.isSelected(r)){
36560             v.onRowSelect(index);
36561         }
36562     },
36563
36564     /**
36565      * Select records.
36566      * @param {Array} records The records to select
36567      * @param {Boolean} keepExisting (optional) True to keep existing selections
36568      */
36569     selectRecords : function(records, keepExisting){
36570         if(!keepExisting){
36571             this.clearSelections();
36572         }
36573         var ds = this.grid.dataSource;
36574         for(var i = 0, len = records.length; i < len; i++){
36575             this.selectRow(ds.indexOf(records[i]), true);
36576         }
36577     },
36578
36579     /**
36580      * Gets the number of selected rows.
36581      * @return {Number}
36582      */
36583     getCount : function(){
36584         return this.selections.length;
36585     },
36586
36587     /**
36588      * Selects the first row in the grid.
36589      */
36590     selectFirstRow : function(){
36591         this.selectRow(0);
36592     },
36593
36594     /**
36595      * Select the last row.
36596      * @param {Boolean} keepExisting (optional) True to keep existing selections
36597      */
36598     selectLastRow : function(keepExisting){
36599         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36600     },
36601
36602     /**
36603      * Selects the row immediately following the last selected row.
36604      * @param {Boolean} keepExisting (optional) True to keep existing selections
36605      */
36606     selectNext : function(keepExisting){
36607         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36608             this.selectRow(this.last+1, keepExisting);
36609             this.grid.getView().focusRow(this.last);
36610         }
36611     },
36612
36613     /**
36614      * Selects the row that precedes the last selected row.
36615      * @param {Boolean} keepExisting (optional) True to keep existing selections
36616      */
36617     selectPrevious : function(keepExisting){
36618         if(this.last){
36619             this.selectRow(this.last-1, keepExisting);
36620             this.grid.getView().focusRow(this.last);
36621         }
36622     },
36623
36624     /**
36625      * Returns the selected records
36626      * @return {Array} Array of selected records
36627      */
36628     getSelections : function(){
36629         return [].concat(this.selections.items);
36630     },
36631
36632     /**
36633      * Returns the first selected record.
36634      * @return {Record}
36635      */
36636     getSelected : function(){
36637         return this.selections.itemAt(0);
36638     },
36639
36640
36641     /**
36642      * Clears all selections.
36643      */
36644     clearSelections : function(fast){
36645         if(this.locked) return;
36646         if(fast !== true){
36647             var ds = this.grid.dataSource;
36648             var s = this.selections;
36649             s.each(function(r){
36650                 this.deselectRow(ds.indexOfId(r.id));
36651             }, this);
36652             s.clear();
36653         }else{
36654             this.selections.clear();
36655         }
36656         this.last = false;
36657     },
36658
36659
36660     /**
36661      * Selects all rows.
36662      */
36663     selectAll : function(){
36664         if(this.locked) return;
36665         this.selections.clear();
36666         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36667             this.selectRow(i, true);
36668         }
36669     },
36670
36671     /**
36672      * Returns True if there is a selection.
36673      * @return {Boolean}
36674      */
36675     hasSelection : function(){
36676         return this.selections.length > 0;
36677     },
36678
36679     /**
36680      * Returns True if the specified row is selected.
36681      * @param {Number/Record} record The record or index of the record to check
36682      * @return {Boolean}
36683      */
36684     isSelected : function(index){
36685         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36686         return (r && this.selections.key(r.id) ? true : false);
36687     },
36688
36689     /**
36690      * Returns True if the specified record id is selected.
36691      * @param {String} id The id of record to check
36692      * @return {Boolean}
36693      */
36694     isIdSelected : function(id){
36695         return (this.selections.key(id) ? true : false);
36696     },
36697
36698     // private
36699     handleMouseDown : function(e, t){
36700         var view = this.grid.getView(), rowIndex;
36701         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36702             return;
36703         };
36704         if(e.shiftKey && this.last !== false){
36705             var last = this.last;
36706             this.selectRange(last, rowIndex, e.ctrlKey);
36707             this.last = last; // reset the last
36708             view.focusRow(rowIndex);
36709         }else{
36710             var isSelected = this.isSelected(rowIndex);
36711             if(e.button !== 0 && isSelected){
36712                 view.focusRow(rowIndex);
36713             }else if(e.ctrlKey && isSelected){
36714                 this.deselectRow(rowIndex);
36715             }else if(!isSelected){
36716                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36717                 view.focusRow(rowIndex);
36718             }
36719         }
36720         this.fireEvent("afterselectionchange", this);
36721     },
36722     // private
36723     handleDragableRowClick :  function(grid, rowIndex, e) 
36724     {
36725         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36726             this.selectRow(rowIndex, false);
36727             grid.view.focusRow(rowIndex);
36728              this.fireEvent("afterselectionchange", this);
36729         }
36730     },
36731     
36732     /**
36733      * Selects multiple rows.
36734      * @param {Array} rows Array of the indexes of the row to select
36735      * @param {Boolean} keepExisting (optional) True to keep existing selections
36736      */
36737     selectRows : function(rows, keepExisting){
36738         if(!keepExisting){
36739             this.clearSelections();
36740         }
36741         for(var i = 0, len = rows.length; i < len; i++){
36742             this.selectRow(rows[i], true);
36743         }
36744     },
36745
36746     /**
36747      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36748      * @param {Number} startRow The index of the first row in the range
36749      * @param {Number} endRow The index of the last row in the range
36750      * @param {Boolean} keepExisting (optional) True to retain existing selections
36751      */
36752     selectRange : function(startRow, endRow, keepExisting){
36753         if(this.locked) return;
36754         if(!keepExisting){
36755             this.clearSelections();
36756         }
36757         if(startRow <= endRow){
36758             for(var i = startRow; i <= endRow; i++){
36759                 this.selectRow(i, true);
36760             }
36761         }else{
36762             for(var i = startRow; i >= endRow; i--){
36763                 this.selectRow(i, true);
36764             }
36765         }
36766     },
36767
36768     /**
36769      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36770      * @param {Number} startRow The index of the first row in the range
36771      * @param {Number} endRow The index of the last row in the range
36772      */
36773     deselectRange : function(startRow, endRow, preventViewNotify){
36774         if(this.locked) return;
36775         for(var i = startRow; i <= endRow; i++){
36776             this.deselectRow(i, preventViewNotify);
36777         }
36778     },
36779
36780     /**
36781      * Selects a row.
36782      * @param {Number} row The index of the row to select
36783      * @param {Boolean} keepExisting (optional) True to keep existing selections
36784      */
36785     selectRow : function(index, keepExisting, preventViewNotify){
36786         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
36787         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36788             if(!keepExisting || this.singleSelect){
36789                 this.clearSelections();
36790             }
36791             var r = this.grid.dataSource.getAt(index);
36792             this.selections.add(r);
36793             this.last = this.lastActive = index;
36794             if(!preventViewNotify){
36795                 this.grid.getView().onRowSelect(index);
36796             }
36797             this.fireEvent("rowselect", this, index, r);
36798             this.fireEvent("selectionchange", this);
36799         }
36800     },
36801
36802     /**
36803      * Deselects a row.
36804      * @param {Number} row The index of the row to deselect
36805      */
36806     deselectRow : function(index, preventViewNotify){
36807         if(this.locked) return;
36808         if(this.last == index){
36809             this.last = false;
36810         }
36811         if(this.lastActive == index){
36812             this.lastActive = false;
36813         }
36814         var r = this.grid.dataSource.getAt(index);
36815         this.selections.remove(r);
36816         if(!preventViewNotify){
36817             this.grid.getView().onRowDeselect(index);
36818         }
36819         this.fireEvent("rowdeselect", this, index);
36820         this.fireEvent("selectionchange", this);
36821     },
36822
36823     // private
36824     restoreLast : function(){
36825         if(this._last){
36826             this.last = this._last;
36827         }
36828     },
36829
36830     // private
36831     acceptsNav : function(row, col, cm){
36832         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36833     },
36834
36835     // private
36836     onEditorKey : function(field, e){
36837         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36838         if(k == e.TAB){
36839             e.stopEvent();
36840             ed.completeEdit();
36841             if(e.shiftKey){
36842                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36843             }else{
36844                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36845             }
36846         }else if(k == e.ENTER && !e.ctrlKey){
36847             e.stopEvent();
36848             ed.completeEdit();
36849             if(e.shiftKey){
36850                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36851             }else{
36852                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36853             }
36854         }else if(k == e.ESC){
36855             ed.cancelEdit();
36856         }
36857         if(newCell){
36858             g.startEditing(newCell[0], newCell[1]);
36859         }
36860     }
36861 });/*
36862  * Based on:
36863  * Ext JS Library 1.1.1
36864  * Copyright(c) 2006-2007, Ext JS, LLC.
36865  *
36866  * Originally Released Under LGPL - original licence link has changed is not relivant.
36867  *
36868  * Fork - LGPL
36869  * <script type="text/javascript">
36870  */
36871 /**
36872  * @class Roo.grid.CellSelectionModel
36873  * @extends Roo.grid.AbstractSelectionModel
36874  * This class provides the basic implementation for cell selection in a grid.
36875  * @constructor
36876  * @param {Object} config The object containing the configuration of this model.
36877  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36878  */
36879 Roo.grid.CellSelectionModel = function(config){
36880     Roo.apply(this, config);
36881
36882     this.selection = null;
36883
36884     this.addEvents({
36885         /**
36886              * @event beforerowselect
36887              * Fires before a cell is selected.
36888              * @param {SelectionModel} this
36889              * @param {Number} rowIndex The selected row index
36890              * @param {Number} colIndex The selected cell index
36891              */
36892             "beforecellselect" : true,
36893         /**
36894              * @event cellselect
36895              * Fires when a cell is selected.
36896              * @param {SelectionModel} this
36897              * @param {Number} rowIndex The selected row index
36898              * @param {Number} colIndex The selected cell index
36899              */
36900             "cellselect" : true,
36901         /**
36902              * @event selectionchange
36903              * Fires when the active selection changes.
36904              * @param {SelectionModel} this
36905              * @param {Object} selection null for no selection or an object (o) with two properties
36906                 <ul>
36907                 <li>o.record: the record object for the row the selection is in</li>
36908                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36909                 </ul>
36910              */
36911             "selectionchange" : true,
36912         /**
36913              * @event tabend
36914              * Fires when the tab (or enter) was pressed on the last editable cell
36915              * You can use this to trigger add new row.
36916              * @param {SelectionModel} this
36917              */
36918             "tabend" : true
36919     });
36920     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36921 };
36922
36923 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36924     
36925     enter_is_tab: false,
36926
36927     /** @ignore */
36928     initEvents : function(){
36929         this.grid.on("mousedown", this.handleMouseDown, this);
36930         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36931         var view = this.grid.view;
36932         view.on("refresh", this.onViewChange, this);
36933         view.on("rowupdated", this.onRowUpdated, this);
36934         view.on("beforerowremoved", this.clearSelections, this);
36935         view.on("beforerowsinserted", this.clearSelections, this);
36936         if(this.grid.isEditor){
36937             this.grid.on("beforeedit", this.beforeEdit,  this);
36938         }
36939     },
36940
36941         //private
36942     beforeEdit : function(e){
36943         this.select(e.row, e.column, false, true, e.record);
36944     },
36945
36946         //private
36947     onRowUpdated : function(v, index, r){
36948         if(this.selection && this.selection.record == r){
36949             v.onCellSelect(index, this.selection.cell[1]);
36950         }
36951     },
36952
36953         //private
36954     onViewChange : function(){
36955         this.clearSelections(true);
36956     },
36957
36958         /**
36959          * Returns the currently selected cell,.
36960          * @return {Array} The selected cell (row, column) or null if none selected.
36961          */
36962     getSelectedCell : function(){
36963         return this.selection ? this.selection.cell : null;
36964     },
36965
36966     /**
36967      * Clears all selections.
36968      * @param {Boolean} true to prevent the gridview from being notified about the change.
36969      */
36970     clearSelections : function(preventNotify){
36971         var s = this.selection;
36972         if(s){
36973             if(preventNotify !== true){
36974                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36975             }
36976             this.selection = null;
36977             this.fireEvent("selectionchange", this, null);
36978         }
36979     },
36980
36981     /**
36982      * Returns true if there is a selection.
36983      * @return {Boolean}
36984      */
36985     hasSelection : function(){
36986         return this.selection ? true : false;
36987     },
36988
36989     /** @ignore */
36990     handleMouseDown : function(e, t){
36991         var v = this.grid.getView();
36992         if(this.isLocked()){
36993             return;
36994         };
36995         var row = v.findRowIndex(t);
36996         var cell = v.findCellIndex(t);
36997         if(row !== false && cell !== false){
36998             this.select(row, cell);
36999         }
37000     },
37001
37002     /**
37003      * Selects a cell.
37004      * @param {Number} rowIndex
37005      * @param {Number} collIndex
37006      */
37007     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37008         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37009             this.clearSelections();
37010             r = r || this.grid.dataSource.getAt(rowIndex);
37011             this.selection = {
37012                 record : r,
37013                 cell : [rowIndex, colIndex]
37014             };
37015             if(!preventViewNotify){
37016                 var v = this.grid.getView();
37017                 v.onCellSelect(rowIndex, colIndex);
37018                 if(preventFocus !== true){
37019                     v.focusCell(rowIndex, colIndex);
37020                 }
37021             }
37022             this.fireEvent("cellselect", this, rowIndex, colIndex);
37023             this.fireEvent("selectionchange", this, this.selection);
37024         }
37025     },
37026
37027         //private
37028     isSelectable : function(rowIndex, colIndex, cm){
37029         return !cm.isHidden(colIndex);
37030     },
37031
37032     /** @ignore */
37033     handleKeyDown : function(e){
37034         //Roo.log('Cell Sel Model handleKeyDown');
37035         if(!e.isNavKeyPress()){
37036             return;
37037         }
37038         var g = this.grid, s = this.selection;
37039         if(!s){
37040             e.stopEvent();
37041             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
37042             if(cell){
37043                 this.select(cell[0], cell[1]);
37044             }
37045             return;
37046         }
37047         var sm = this;
37048         var walk = function(row, col, step){
37049             return g.walkCells(row, col, step, sm.isSelectable,  sm);
37050         };
37051         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37052         var newCell;
37053
37054       
37055
37056         switch(k){
37057             case e.TAB:
37058                 // handled by onEditorKey
37059                 if (g.isEditor && g.editing) {
37060                     return;
37061                 }
37062                 if(e.shiftKey) {
37063                     newCell = walk(r, c-1, -1);
37064                 } else {
37065                     newCell = walk(r, c+1, 1);
37066                 }
37067                 break;
37068             
37069             case e.DOWN:
37070                newCell = walk(r+1, c, 1);
37071                 break;
37072             
37073             case e.UP:
37074                 newCell = walk(r-1, c, -1);
37075                 break;
37076             
37077             case e.RIGHT:
37078                 newCell = walk(r, c+1, 1);
37079                 break;
37080             
37081             case e.LEFT:
37082                 newCell = walk(r, c-1, -1);
37083                 break;
37084             
37085             case e.ENTER:
37086                 
37087                 if(g.isEditor && !g.editing){
37088                    g.startEditing(r, c);
37089                    e.stopEvent();
37090                    return;
37091                 }
37092                 
37093                 
37094              break;
37095         };
37096         if(newCell){
37097             this.select(newCell[0], newCell[1]);
37098             e.stopEvent();
37099             
37100         }
37101     },
37102
37103     acceptsNav : function(row, col, cm){
37104         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37105     },
37106     /**
37107      * Selects a cell.
37108      * @param {Number} field (not used) - as it's normally used as a listener
37109      * @param {Number} e - event - fake it by using
37110      *
37111      * var e = Roo.EventObjectImpl.prototype;
37112      * e.keyCode = e.TAB
37113      *
37114      * 
37115      */
37116     onEditorKey : function(field, e){
37117         
37118         var k = e.getKey(),
37119             newCell,
37120             g = this.grid,
37121             ed = g.activeEditor,
37122             forward = false;
37123         ///Roo.log('onEditorKey' + k);
37124         
37125         
37126         if (this.enter_is_tab && k == e.ENTER) {
37127             k = e.TAB;
37128         }
37129         
37130         if(k == e.TAB){
37131             if(e.shiftKey){
37132                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37133             }else{
37134                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37135                 forward = true;
37136             }
37137             
37138             e.stopEvent();
37139             
37140         }else if(k == e.ENTER &&  !e.ctrlKey){
37141             ed.completeEdit();
37142             e.stopEvent();
37143             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37144         }else if(k == e.ESC){
37145             ed.cancelEdit();
37146         }
37147         
37148         
37149         if(newCell){
37150             //Roo.log('next cell after edit');
37151             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37152         } else if (forward) {
37153             // tabbed past last
37154             this.fireEvent.defer(100, this, ['tabend',this]);
37155         }
37156     }
37157 });/*
37158  * Based on:
37159  * Ext JS Library 1.1.1
37160  * Copyright(c) 2006-2007, Ext JS, LLC.
37161  *
37162  * Originally Released Under LGPL - original licence link has changed is not relivant.
37163  *
37164  * Fork - LGPL
37165  * <script type="text/javascript">
37166  */
37167  
37168 /**
37169  * @class Roo.grid.EditorGrid
37170  * @extends Roo.grid.Grid
37171  * Class for creating and editable grid.
37172  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37173  * The container MUST have some type of size defined for the grid to fill. The container will be 
37174  * automatically set to position relative if it isn't already.
37175  * @param {Object} dataSource The data model to bind to
37176  * @param {Object} colModel The column model with info about this grid's columns
37177  */
37178 Roo.grid.EditorGrid = function(container, config){
37179     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37180     this.getGridEl().addClass("xedit-grid");
37181
37182     if(!this.selModel){
37183         this.selModel = new Roo.grid.CellSelectionModel();
37184     }
37185
37186     this.activeEditor = null;
37187
37188         this.addEvents({
37189             /**
37190              * @event beforeedit
37191              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37192              * <ul style="padding:5px;padding-left:16px;">
37193              * <li>grid - This grid</li>
37194              * <li>record - The record being edited</li>
37195              * <li>field - The field name being edited</li>
37196              * <li>value - The value for the field being edited.</li>
37197              * <li>row - The grid row index</li>
37198              * <li>column - The grid column index</li>
37199              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37200              * </ul>
37201              * @param {Object} e An edit event (see above for description)
37202              */
37203             "beforeedit" : true,
37204             /**
37205              * @event afteredit
37206              * Fires after a cell is edited. <br />
37207              * <ul style="padding:5px;padding-left:16px;">
37208              * <li>grid - This grid</li>
37209              * <li>record - The record being edited</li>
37210              * <li>field - The field name being edited</li>
37211              * <li>value - The value being set</li>
37212              * <li>originalValue - The original value for the field, before the edit.</li>
37213              * <li>row - The grid row index</li>
37214              * <li>column - The grid column index</li>
37215              * </ul>
37216              * @param {Object} e An edit event (see above for description)
37217              */
37218             "afteredit" : true,
37219             /**
37220              * @event validateedit
37221              * Fires after a cell is edited, but before the value is set in the record. 
37222          * You can use this to modify the value being set in the field, Return false
37223              * to cancel the change. The edit event object has the following properties <br />
37224              * <ul style="padding:5px;padding-left:16px;">
37225          * <li>editor - This editor</li>
37226              * <li>grid - This grid</li>
37227              * <li>record - The record being edited</li>
37228              * <li>field - The field name being edited</li>
37229              * <li>value - The value being set</li>
37230              * <li>originalValue - The original value for the field, before the edit.</li>
37231              * <li>row - The grid row index</li>
37232              * <li>column - The grid column index</li>
37233              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37234              * </ul>
37235              * @param {Object} e An edit event (see above for description)
37236              */
37237             "validateedit" : true
37238         });
37239     this.on("bodyscroll", this.stopEditing,  this);
37240     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37241 };
37242
37243 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37244     /**
37245      * @cfg {Number} clicksToEdit
37246      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37247      */
37248     clicksToEdit: 2,
37249
37250     // private
37251     isEditor : true,
37252     // private
37253     trackMouseOver: false, // causes very odd FF errors
37254
37255     onCellDblClick : function(g, row, col){
37256         this.startEditing(row, col);
37257     },
37258
37259     onEditComplete : function(ed, value, startValue){
37260         this.editing = false;
37261         this.activeEditor = null;
37262         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37263         var r = ed.record;
37264         var field = this.colModel.getDataIndex(ed.col);
37265         var e = {
37266             grid: this,
37267             record: r,
37268             field: field,
37269             originalValue: startValue,
37270             value: value,
37271             row: ed.row,
37272             column: ed.col,
37273             cancel:false,
37274             editor: ed
37275         };
37276         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37277         cell.show();
37278           
37279         if(String(value) !== String(startValue)){
37280             
37281             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37282                 r.set(field, e.value);
37283                 // if we are dealing with a combo box..
37284                 // then we also set the 'name' colum to be the displayField
37285                 if (ed.field.displayField && ed.field.name) {
37286                     r.set(ed.field.name, ed.field.el.dom.value);
37287                 }
37288                 
37289                 delete e.cancel; //?? why!!!
37290                 this.fireEvent("afteredit", e);
37291             }
37292         } else {
37293             this.fireEvent("afteredit", e); // always fire it!
37294         }
37295         this.view.focusCell(ed.row, ed.col);
37296     },
37297
37298     /**
37299      * Starts editing the specified for the specified row/column
37300      * @param {Number} rowIndex
37301      * @param {Number} colIndex
37302      */
37303     startEditing : function(row, col){
37304         this.stopEditing();
37305         if(this.colModel.isCellEditable(col, row)){
37306             this.view.ensureVisible(row, col, true);
37307           
37308             var r = this.dataSource.getAt(row);
37309             var field = this.colModel.getDataIndex(col);
37310             var cell = Roo.get(this.view.getCell(row,col));
37311             var e = {
37312                 grid: this,
37313                 record: r,
37314                 field: field,
37315                 value: r.data[field],
37316                 row: row,
37317                 column: col,
37318                 cancel:false 
37319             };
37320             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37321                 this.editing = true;
37322                 var ed = this.colModel.getCellEditor(col, row);
37323                 
37324                 if (!ed) {
37325                     return;
37326                 }
37327                 if(!ed.rendered){
37328                     ed.render(ed.parentEl || document.body);
37329                 }
37330                 ed.field.reset();
37331                
37332                 cell.hide();
37333                 
37334                 (function(){ // complex but required for focus issues in safari, ie and opera
37335                     ed.row = row;
37336                     ed.col = col;
37337                     ed.record = r;
37338                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37339                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37340                     this.activeEditor = ed;
37341                     var v = r.data[field];
37342                     ed.startEdit(this.view.getCell(row, col), v);
37343                     // combo's with 'displayField and name set
37344                     if (ed.field.displayField && ed.field.name) {
37345                         ed.field.el.dom.value = r.data[ed.field.name];
37346                     }
37347                     
37348                     
37349                 }).defer(50, this);
37350             }
37351         }
37352     },
37353         
37354     /**
37355      * Stops any active editing
37356      */
37357     stopEditing : function(){
37358         if(this.activeEditor){
37359             this.activeEditor.completeEdit();
37360         }
37361         this.activeEditor = null;
37362     }
37363 });/*
37364  * Based on:
37365  * Ext JS Library 1.1.1
37366  * Copyright(c) 2006-2007, Ext JS, LLC.
37367  *
37368  * Originally Released Under LGPL - original licence link has changed is not relivant.
37369  *
37370  * Fork - LGPL
37371  * <script type="text/javascript">
37372  */
37373
37374 // private - not really -- you end up using it !
37375 // This is a support class used internally by the Grid components
37376
37377 /**
37378  * @class Roo.grid.GridEditor
37379  * @extends Roo.Editor
37380  * Class for creating and editable grid elements.
37381  * @param {Object} config any settings (must include field)
37382  */
37383 Roo.grid.GridEditor = function(field, config){
37384     if (!config && field.field) {
37385         config = field;
37386         field = Roo.factory(config.field, Roo.form);
37387     }
37388     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37389     field.monitorTab = false;
37390 };
37391
37392 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37393     
37394     /**
37395      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37396      */
37397     
37398     alignment: "tl-tl",
37399     autoSize: "width",
37400     hideEl : false,
37401     cls: "x-small-editor x-grid-editor",
37402     shim:false,
37403     shadow:"frame"
37404 });/*
37405  * Based on:
37406  * Ext JS Library 1.1.1
37407  * Copyright(c) 2006-2007, Ext JS, LLC.
37408  *
37409  * Originally Released Under LGPL - original licence link has changed is not relivant.
37410  *
37411  * Fork - LGPL
37412  * <script type="text/javascript">
37413  */
37414   
37415
37416   
37417 Roo.grid.PropertyRecord = Roo.data.Record.create([
37418     {name:'name',type:'string'},  'value'
37419 ]);
37420
37421
37422 Roo.grid.PropertyStore = function(grid, source){
37423     this.grid = grid;
37424     this.store = new Roo.data.Store({
37425         recordType : Roo.grid.PropertyRecord
37426     });
37427     this.store.on('update', this.onUpdate,  this);
37428     if(source){
37429         this.setSource(source);
37430     }
37431     Roo.grid.PropertyStore.superclass.constructor.call(this);
37432 };
37433
37434
37435
37436 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37437     setSource : function(o){
37438         this.source = o;
37439         this.store.removeAll();
37440         var data = [];
37441         for(var k in o){
37442             if(this.isEditableValue(o[k])){
37443                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37444             }
37445         }
37446         this.store.loadRecords({records: data}, {}, true);
37447     },
37448
37449     onUpdate : function(ds, record, type){
37450         if(type == Roo.data.Record.EDIT){
37451             var v = record.data['value'];
37452             var oldValue = record.modified['value'];
37453             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37454                 this.source[record.id] = v;
37455                 record.commit();
37456                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37457             }else{
37458                 record.reject();
37459             }
37460         }
37461     },
37462
37463     getProperty : function(row){
37464        return this.store.getAt(row);
37465     },
37466
37467     isEditableValue: function(val){
37468         if(val && val instanceof Date){
37469             return true;
37470         }else if(typeof val == 'object' || typeof val == 'function'){
37471             return false;
37472         }
37473         return true;
37474     },
37475
37476     setValue : function(prop, value){
37477         this.source[prop] = value;
37478         this.store.getById(prop).set('value', value);
37479     },
37480
37481     getSource : function(){
37482         return this.source;
37483     }
37484 });
37485
37486 Roo.grid.PropertyColumnModel = function(grid, store){
37487     this.grid = grid;
37488     var g = Roo.grid;
37489     g.PropertyColumnModel.superclass.constructor.call(this, [
37490         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37491         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37492     ]);
37493     this.store = store;
37494     this.bselect = Roo.DomHelper.append(document.body, {
37495         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37496             {tag: 'option', value: 'true', html: 'true'},
37497             {tag: 'option', value: 'false', html: 'false'}
37498         ]
37499     });
37500     Roo.id(this.bselect);
37501     var f = Roo.form;
37502     this.editors = {
37503         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37504         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37505         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37506         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37507         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37508     };
37509     this.renderCellDelegate = this.renderCell.createDelegate(this);
37510     this.renderPropDelegate = this.renderProp.createDelegate(this);
37511 };
37512
37513 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37514     
37515     
37516     nameText : 'Name',
37517     valueText : 'Value',
37518     
37519     dateFormat : 'm/j/Y',
37520     
37521     
37522     renderDate : function(dateVal){
37523         return dateVal.dateFormat(this.dateFormat);
37524     },
37525
37526     renderBool : function(bVal){
37527         return bVal ? 'true' : 'false';
37528     },
37529
37530     isCellEditable : function(colIndex, rowIndex){
37531         return colIndex == 1;
37532     },
37533
37534     getRenderer : function(col){
37535         return col == 1 ?
37536             this.renderCellDelegate : this.renderPropDelegate;
37537     },
37538
37539     renderProp : function(v){
37540         return this.getPropertyName(v);
37541     },
37542
37543     renderCell : function(val){
37544         var rv = val;
37545         if(val instanceof Date){
37546             rv = this.renderDate(val);
37547         }else if(typeof val == 'boolean'){
37548             rv = this.renderBool(val);
37549         }
37550         return Roo.util.Format.htmlEncode(rv);
37551     },
37552
37553     getPropertyName : function(name){
37554         var pn = this.grid.propertyNames;
37555         return pn && pn[name] ? pn[name] : name;
37556     },
37557
37558     getCellEditor : function(colIndex, rowIndex){
37559         var p = this.store.getProperty(rowIndex);
37560         var n = p.data['name'], val = p.data['value'];
37561         
37562         if(typeof(this.grid.customEditors[n]) == 'string'){
37563             return this.editors[this.grid.customEditors[n]];
37564         }
37565         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37566             return this.grid.customEditors[n];
37567         }
37568         if(val instanceof Date){
37569             return this.editors['date'];
37570         }else if(typeof val == 'number'){
37571             return this.editors['number'];
37572         }else if(typeof val == 'boolean'){
37573             return this.editors['boolean'];
37574         }else{
37575             return this.editors['string'];
37576         }
37577     }
37578 });
37579
37580 /**
37581  * @class Roo.grid.PropertyGrid
37582  * @extends Roo.grid.EditorGrid
37583  * This class represents the  interface of a component based property grid control.
37584  * <br><br>Usage:<pre><code>
37585  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37586       
37587  });
37588  // set any options
37589  grid.render();
37590  * </code></pre>
37591   
37592  * @constructor
37593  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37594  * The container MUST have some type of size defined for the grid to fill. The container will be
37595  * automatically set to position relative if it isn't already.
37596  * @param {Object} config A config object that sets properties on this grid.
37597  */
37598 Roo.grid.PropertyGrid = function(container, config){
37599     config = config || {};
37600     var store = new Roo.grid.PropertyStore(this);
37601     this.store = store;
37602     var cm = new Roo.grid.PropertyColumnModel(this, store);
37603     store.store.sort('name', 'ASC');
37604     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37605         ds: store.store,
37606         cm: cm,
37607         enableColLock:false,
37608         enableColumnMove:false,
37609         stripeRows:false,
37610         trackMouseOver: false,
37611         clicksToEdit:1
37612     }, config));
37613     this.getGridEl().addClass('x-props-grid');
37614     this.lastEditRow = null;
37615     this.on('columnresize', this.onColumnResize, this);
37616     this.addEvents({
37617          /**
37618              * @event beforepropertychange
37619              * Fires before a property changes (return false to stop?)
37620              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37621              * @param {String} id Record Id
37622              * @param {String} newval New Value
37623          * @param {String} oldval Old Value
37624              */
37625         "beforepropertychange": true,
37626         /**
37627              * @event propertychange
37628              * Fires after a property changes
37629              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37630              * @param {String} id Record Id
37631              * @param {String} newval New Value
37632          * @param {String} oldval Old Value
37633              */
37634         "propertychange": true
37635     });
37636     this.customEditors = this.customEditors || {};
37637 };
37638 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37639     
37640      /**
37641      * @cfg {Object} customEditors map of colnames=> custom editors.
37642      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37643      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37644      * false disables editing of the field.
37645          */
37646     
37647       /**
37648      * @cfg {Object} propertyNames map of property Names to their displayed value
37649          */
37650     
37651     render : function(){
37652         Roo.grid.PropertyGrid.superclass.render.call(this);
37653         this.autoSize.defer(100, this);
37654     },
37655
37656     autoSize : function(){
37657         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37658         if(this.view){
37659             this.view.fitColumns();
37660         }
37661     },
37662
37663     onColumnResize : function(){
37664         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37665         this.autoSize();
37666     },
37667     /**
37668      * Sets the data for the Grid
37669      * accepts a Key => Value object of all the elements avaiable.
37670      * @param {Object} data  to appear in grid.
37671      */
37672     setSource : function(source){
37673         this.store.setSource(source);
37674         //this.autoSize();
37675     },
37676     /**
37677      * Gets all the data from the grid.
37678      * @return {Object} data  data stored in grid
37679      */
37680     getSource : function(){
37681         return this.store.getSource();
37682     }
37683 });/*
37684  * Based on:
37685  * Ext JS Library 1.1.1
37686  * Copyright(c) 2006-2007, Ext JS, LLC.
37687  *
37688  * Originally Released Under LGPL - original licence link has changed is not relivant.
37689  *
37690  * Fork - LGPL
37691  * <script type="text/javascript">
37692  */
37693  
37694 /**
37695  * @class Roo.LoadMask
37696  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37697  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37698  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37699  * element's UpdateManager load indicator and will be destroyed after the initial load.
37700  * @constructor
37701  * Create a new LoadMask
37702  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37703  * @param {Object} config The config object
37704  */
37705 Roo.LoadMask = function(el, config){
37706     this.el = Roo.get(el);
37707     Roo.apply(this, config);
37708     if(this.store){
37709         this.store.on('beforeload', this.onBeforeLoad, this);
37710         this.store.on('load', this.onLoad, this);
37711         this.store.on('loadexception', this.onLoadException, this);
37712         this.removeMask = false;
37713     }else{
37714         var um = this.el.getUpdateManager();
37715         um.showLoadIndicator = false; // disable the default indicator
37716         um.on('beforeupdate', this.onBeforeLoad, this);
37717         um.on('update', this.onLoad, this);
37718         um.on('failure', this.onLoad, this);
37719         this.removeMask = true;
37720     }
37721 };
37722
37723 Roo.LoadMask.prototype = {
37724     /**
37725      * @cfg {Boolean} removeMask
37726      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37727      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37728      */
37729     /**
37730      * @cfg {String} msg
37731      * The text to display in a centered loading message box (defaults to 'Loading...')
37732      */
37733     msg : 'Loading...',
37734     /**
37735      * @cfg {String} msgCls
37736      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37737      */
37738     msgCls : 'x-mask-loading',
37739
37740     /**
37741      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37742      * @type Boolean
37743      */
37744     disabled: false,
37745
37746     /**
37747      * Disables the mask to prevent it from being displayed
37748      */
37749     disable : function(){
37750        this.disabled = true;
37751     },
37752
37753     /**
37754      * Enables the mask so that it can be displayed
37755      */
37756     enable : function(){
37757         this.disabled = false;
37758     },
37759     
37760     onLoadException : function()
37761     {
37762         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37763             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37764         }
37765         this.el.unmask(this.removeMask);
37766     },
37767     // private
37768     onLoad : function()
37769     {
37770         this.el.unmask(this.removeMask);
37771     },
37772
37773     // private
37774     onBeforeLoad : function(){
37775         if(!this.disabled){
37776             this.el.mask(this.msg, this.msgCls);
37777         }
37778     },
37779
37780     // private
37781     destroy : function(){
37782         if(this.store){
37783             this.store.un('beforeload', this.onBeforeLoad, this);
37784             this.store.un('load', this.onLoad, this);
37785             this.store.un('loadexception', this.onLoadException, this);
37786         }else{
37787             var um = this.el.getUpdateManager();
37788             um.un('beforeupdate', this.onBeforeLoad, this);
37789             um.un('update', this.onLoad, this);
37790             um.un('failure', this.onLoad, this);
37791         }
37792     }
37793 };/*
37794  * Based on:
37795  * Ext JS Library 1.1.1
37796  * Copyright(c) 2006-2007, Ext JS, LLC.
37797  *
37798  * Originally Released Under LGPL - original licence link has changed is not relivant.
37799  *
37800  * Fork - LGPL
37801  * <script type="text/javascript">
37802  */
37803 Roo.XTemplate = function(){
37804     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37805     var s = this.html;
37806
37807     s = ['<tpl>', s, '</tpl>'].join('');
37808
37809     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
37810
37811     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
37812     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
37813     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
37814     var m, id = 0;
37815     var tpls = [];
37816
37817     while(m = s.match(re)){
37818        var m2 = m[0].match(nameRe);
37819        var m3 = m[0].match(ifRe);
37820        var m4 = m[0].match(execRe);
37821        var exp = null, fn = null, exec = null;
37822        var name = m2 && m2[1] ? m2[1] : '';
37823        if(m3){
37824            exp = m3 && m3[1] ? m3[1] : null;
37825            if(exp){
37826                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
37827            }
37828        }
37829        if(m4){
37830            exp = m4 && m4[1] ? m4[1] : null;
37831            if(exp){
37832                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
37833            }
37834        }
37835        if(name){
37836            switch(name){
37837                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
37838                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
37839                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
37840            }
37841        }
37842        tpls.push({
37843             id: id,
37844             target: name,
37845             exec: exec,
37846             test: fn,
37847             body: m[1]||''
37848         });
37849        s = s.replace(m[0], '{xtpl'+ id + '}');
37850        ++id;
37851     }
37852     for(var i = tpls.length-1; i >= 0; --i){
37853         this.compileTpl(tpls[i]);
37854     }
37855     this.master = tpls[tpls.length-1];
37856     this.tpls = tpls;
37857 };
37858 Roo.extend(Roo.XTemplate, Roo.Template, {
37859
37860     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37861
37862     applySubTemplate : function(id, values, parent){
37863         var t = this.tpls[id];
37864         if(t.test && !t.test.call(this, values, parent)){
37865             return '';
37866         }
37867         if(t.exec && t.exec.call(this, values, parent)){
37868             return '';
37869         }
37870         var vs = t.target ? t.target.call(this, values, parent) : values;
37871         parent = t.target ? values : parent;
37872         if(t.target && vs instanceof Array){
37873             var buf = [];
37874             for(var i = 0, len = vs.length; i < len; i++){
37875                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
37876             }
37877             return buf.join('');
37878         }
37879         return t.compiled.call(this, vs, parent);
37880     },
37881
37882     compileTpl : function(tpl){
37883         var fm = Roo.util.Format;
37884         var useF = this.disableFormats !== true;
37885         var sep = Roo.isGecko ? "+" : ",";
37886         var fn = function(m, name, format, args){
37887             if(name.substr(0, 4) == 'xtpl'){
37888                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
37889             }
37890             var v;
37891             if(name.indexOf('.') != -1){
37892                 v = name;
37893             }else{
37894                 v = "values['" + name + "']";
37895             }
37896             if(format && useF){
37897                 args = args ? ',' + args : "";
37898                 if(format.substr(0, 5) != "this."){
37899                     format = "fm." + format + '(';
37900                 }else{
37901                     format = 'this.call("'+ format.substr(5) + '", ';
37902                     args = ", values";
37903                 }
37904             }else{
37905                 args= ''; format = "("+v+" === undefined ? '' : ";
37906             }
37907             return "'"+ sep + format + v + args + ")"+sep+"'";
37908         };
37909         var body;
37910         // branched to use + in gecko and [].join() in others
37911         if(Roo.isGecko){
37912             body = "tpl.compiled = function(values, parent){ return '" +
37913                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
37914                     "';};";
37915         }else{
37916             body = ["tpl.compiled = function(values, parent){ return ['"];
37917             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
37918             body.push("'].join('');};");
37919             body = body.join('');
37920         }
37921         /** eval:var:zzzzzzz */
37922         eval(body);
37923         return this;
37924     },
37925
37926     applyTemplate : function(values){
37927         return this.master.compiled.call(this, values, {});
37928         var s = this.subs;
37929     },
37930
37931     apply : function(){
37932         return this.applyTemplate.apply(this, arguments);
37933     },
37934
37935     compile : function(){return this;}
37936 });
37937
37938 Roo.XTemplate.from = function(el){
37939     el = Roo.getDom(el);
37940     return new Roo.XTemplate(el.value || el.innerHTML);
37941 };/*
37942  * Original code for Roojs - LGPL
37943  * <script type="text/javascript">
37944  */
37945  
37946 /**
37947  * @class Roo.XComponent
37948  * A delayed Element creator...
37949  * Or a way to group chunks of interface together.
37950  * 
37951  * Mypart.xyx = new Roo.XComponent({
37952
37953     parent : 'Mypart.xyz', // empty == document.element.!!
37954     order : '001',
37955     name : 'xxxx'
37956     region : 'xxxx'
37957     disabled : function() {} 
37958      
37959     tree : function() { // return an tree of xtype declared components
37960         var MODULE = this;
37961         return 
37962         {
37963             xtype : 'NestedLayoutPanel',
37964             // technicall
37965         }
37966      ]
37967  *})
37968  *
37969  *
37970  * It can be used to build a big heiracy, with parent etc.
37971  * or you can just use this to render a single compoent to a dom element
37972  * MYPART.render(Roo.Element | String(id) | dom_element )
37973  * 
37974  * @extends Roo.util.Observable
37975  * @constructor
37976  * @param cfg {Object} configuration of component
37977  * 
37978  */
37979 Roo.XComponent = function(cfg) {
37980     Roo.apply(this, cfg);
37981     this.addEvents({ 
37982         /**
37983              * @event built
37984              * Fires when this the componnt is built
37985              * @param {Roo.XComponent} c the component
37986              */
37987         'built' : true,
37988         /**
37989              * @event buildcomplete
37990              * Fires on the top level element when all elements have been built
37991              * @param {Roo.XComponent} c the top level component.
37992          */
37993         'buildcomplete' : true
37994         
37995     });
37996     this.region = this.region || 'center'; // default..
37997     Roo.XComponent.register(this);
37998     this.modules = false;
37999     this.el = false; // where the layout goes..
38000     
38001     
38002 }
38003 Roo.extend(Roo.XComponent, Roo.util.Observable, {
38004     /**
38005      * @property el
38006      * The created element (with Roo.factory())
38007      * @type {Roo.Layout}
38008      */
38009     el  : false,
38010     
38011     /**
38012      * @property el
38013      * for BC  - use el in new code
38014      * @type {Roo.Layout}
38015      */
38016     panel : false,
38017     
38018     /**
38019      * @property layout
38020      * for BC  - use el in new code
38021      * @type {Roo.Layout}
38022      */
38023     layout : false,
38024     
38025      /**
38026      * @cfg {Function|boolean} disabled
38027      * If this module is disabled by some rule, return true from the funtion
38028      */
38029     disabled : false,
38030     
38031     /**
38032      * @cfg {String} parent 
38033      * Name of parent element which it get xtype added to..
38034      */
38035     parent: false,
38036     
38037     /**
38038      * @cfg {String} order
38039      * Used to set the order in which elements are created (usefull for multiple tabs)
38040      */
38041     
38042     order : false,
38043     /**
38044      * @cfg {String} name
38045      * String to display while loading.
38046      */
38047     name : false,
38048     /**
38049      * @cfg {String} region
38050      * Region to render component to (defaults to center)
38051      */
38052     region : 'center',
38053     
38054     /**
38055      * @cfg {Array} items
38056      * A single item array - the first element is the root of the tree..
38057      * It's done this way to stay compatible with the Xtype system...
38058      */
38059     items : false,
38060     
38061     
38062      /**
38063      * render
38064      * render element to dom or tree
38065      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
38066      */
38067     
38068     render : function(el)
38069     {
38070         
38071         el = el || false;
38072         var hp = this.parent ? 1 : 0;
38073         
38074         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
38075             // if parent is a '#.....' string, then let's use that..
38076             var ename = this.parent.substr(1)
38077             this.parent = false;
38078             el = Roo.get(ename);
38079             if (!el) {
38080                 Roo.log("Warning - element can not be found :#" + ename );
38081                 return;
38082             }
38083         }
38084         
38085         
38086         if (!this.parent) {
38087             
38088             el = el ? Roo.get(el) : false;
38089             
38090             // it's a top level one..
38091             this.parent =  {
38092                 el : new Roo.BorderLayout(el || document.body, {
38093                 
38094                      center: {
38095                          titlebar: false,
38096                          autoScroll:false,
38097                          closeOnTab: true,
38098                          tabPosition: 'top',
38099                           //resizeTabs: true,
38100                          alwaysShowTabs: el && hp? false :  true,
38101                          hideTabs: el || !hp ? true :  false,
38102                          minTabWidth: 140
38103                      }
38104                  })
38105             }
38106         }
38107         
38108         
38109             
38110         var tree = this.tree();
38111         tree.region = tree.region || this.region;
38112         this.el = this.parent.el.addxtype(tree);
38113         this.fireEvent('built', this);
38114         
38115         this.panel = this.el;
38116         this.layout = this.panel.layout;    
38117          
38118     }
38119     
38120 });
38121
38122 Roo.apply(Roo.XComponent, {
38123     
38124     /**
38125      * @property  buildCompleted
38126      * True when the builder has completed building the interface.
38127      * @type Boolean
38128      */
38129     buildCompleted : false,
38130      
38131     /**
38132      * @property  topModule
38133      * the upper most module - uses document.element as it's constructor.
38134      * @type Object
38135      */
38136      
38137     topModule  : false,
38138       
38139     /**
38140      * @property  modules
38141      * array of modules to be created by registration system.
38142      * @type {Array} of Roo.XComponent
38143      */
38144     
38145     modules : [],
38146     /**
38147      * @property  elmodules
38148      * array of modules to be created by which use #ID 
38149      * @type {Array} of Roo.XComponent
38150      */
38151      
38152     elmodules : [],
38153
38154     
38155     /**
38156      * Register components to be built later.
38157      *
38158      * This solves the following issues
38159      * - Building is not done on page load, but after an authentication process has occured.
38160      * - Interface elements are registered on page load
38161      * - Parent Interface elements may not be loaded before child, so this handles that..
38162      * 
38163      *
38164      * example:
38165      * 
38166      * MyApp.register({
38167           order : '000001',
38168           module : 'Pman.Tab.projectMgr',
38169           region : 'center',
38170           parent : 'Pman.layout',
38171           disabled : false,  // or use a function..
38172         })
38173      
38174      * * @param {Object} details about module
38175      */
38176     register : function(obj) {
38177         this.modules.push(obj);
38178          
38179     },
38180     /**
38181      * convert a string to an object..
38182      * eg. 'AAA.BBB' -> finds AAA.BBB
38183
38184      */
38185     
38186     toObject : function(str)
38187     {
38188         if (!str || typeof(str) == 'object') {
38189             return str;
38190         }
38191         if (str.substring(0,1) == '#') {
38192             return str;
38193         }
38194
38195         var ar = str.split('.');
38196         var rt, o;
38197         rt = ar.shift();
38198             /** eval:var:o */
38199         try {
38200             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38201         } catch (e) {
38202             throw "Module not found : " + str;
38203         }
38204         
38205         if (o === false) {
38206             throw "Module not found : " + str;
38207         }
38208         Roo.each(ar, function(e) {
38209             if (typeof(o[e]) == 'undefined') {
38210                 throw "Module not found : " + str;
38211             }
38212             o = o[e];
38213         });
38214         
38215         return o;
38216         
38217     },
38218     
38219     
38220     /**
38221      * move modules into their correct place in the tree..
38222      * 
38223      */
38224     preBuild : function ()
38225     {
38226         var _t = this;
38227         Roo.each(this.modules , function (obj)
38228         {
38229             var opar = obj.parent;
38230             try { 
38231                 obj.parent = this.toObject(opar);
38232             } catch(e) {
38233                 Roo.log(e.toString());
38234                 return;
38235             }
38236             
38237             if (!obj.parent) {
38238                 this.topModule = obj;
38239                 return;
38240             }
38241             if (typeof(obj.parent) == 'string') {
38242                 this.elmodules.push(obj);
38243                 return;
38244             }
38245             if (obj.parent.constructor != Roo.XComponent) {
38246                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
38247             }
38248             if (!obj.parent.modules) {
38249                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38250                     function(o) { return o.order + '' }
38251                 );
38252             }
38253             
38254             obj.parent.modules.add(obj);
38255         }, this);
38256     },
38257     
38258      /**
38259      * make a list of modules to build.
38260      * @return {Array} list of modules. 
38261      */ 
38262     
38263     buildOrder : function()
38264     {
38265         var _this = this;
38266         var cmp = function(a,b) {   
38267             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38268         };
38269         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38270             throw "No top level modules to build";
38271         }
38272         
38273         // make a flat list in order of modules to build.
38274         var mods = this.topModule ? [ this.topModule ] : [];
38275         Roo.each(this.elmodules,function(e) { mods.push(e) });
38276
38277         
38278         // add modules to their parents..
38279         var addMod = function(m) {
38280            // Roo.debug && Roo.log(m.modKey);
38281             
38282             mods.push(m);
38283             if (m.modules) {
38284                 m.modules.keySort('ASC',  cmp );
38285                 m.modules.each(addMod);
38286             }
38287             // not sure if this is used any more..
38288             if (m.finalize) {
38289                 m.finalize.name = m.name + " (clean up) ";
38290                 mods.push(m.finalize);
38291             }
38292             
38293         }
38294         if (this.topModule) { 
38295             this.topModule.modules.keySort('ASC',  cmp );
38296             this.topModule.modules.each(addMod);
38297         }
38298         return mods;
38299     },
38300     
38301      /**
38302      * Build the registered modules.
38303      * @param {Object} parent element.
38304      * @param {Function} optional method to call after module has been added.
38305      * 
38306      */ 
38307    
38308     build : function() 
38309     {
38310         
38311         this.preBuild();
38312         var mods = this.buildOrder();
38313       
38314         //this.allmods = mods;
38315         //Roo.debug && Roo.log(mods);
38316         //return;
38317         if (!mods.length) { // should not happen
38318             throw "NO modules!!!";
38319         }
38320         
38321         
38322         
38323         // flash it up as modal - so we store the mask!?
38324         Roo.MessageBox.show({ title: 'loading' });
38325         Roo.MessageBox.show({
38326            title: "Please wait...",
38327            msg: "Building Interface...",
38328            width:450,
38329            progress:true,
38330            closable:false,
38331            modal: false
38332           
38333         });
38334         var total = mods.length;
38335         
38336         var _this = this;
38337         var progressRun = function() {
38338             if (!mods.length) {
38339                 Roo.debug && Roo.log('hide?');
38340                 Roo.MessageBox.hide();
38341                 if (_this.topModule) { 
38342                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
38343                 }
38344                 // THE END...
38345                 return false;   
38346             }
38347             
38348             var m = mods.shift();
38349             
38350             
38351             Roo.debug && Roo.log(m);
38352             // not sure if this is supported any more.. - modules that are are just function
38353             if (typeof(m) == 'function') { 
38354                 m.call(this);
38355                 return progressRun.defer(10, _this);
38356             } 
38357             
38358             
38359             
38360             Roo.MessageBox.updateProgress(
38361                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
38362                     " of " + total + 
38363                     (m.name ? (' - ' + m.name) : '')
38364                     );
38365             
38366          
38367             // is the module disabled?
38368             var disabled = (typeof(m.disabled) == 'function') ?
38369                 m.disabled.call(m.module.disabled) : m.disabled;    
38370             
38371             
38372             if (disabled) {
38373                 return progressRun(); // we do not update the display!
38374             }
38375             
38376             // now build 
38377             
38378             m.render();
38379             // it's 10 on top level, and 1 on others??? why...
38380             return progressRun.defer(10, _this);
38381              
38382         }
38383         progressRun.defer(1, _this);
38384      
38385         
38386         
38387     }
38388     
38389      
38390    
38391     
38392     
38393 });
38394  //<script type="text/javascript">
38395
38396
38397 /**
38398  * @class Roo.Login
38399  * @extends Roo.LayoutDialog
38400  * A generic Login Dialog..... - only one needed in theory!?!?
38401  *
38402  * Fires XComponent builder on success...
38403  * 
38404  * Sends 
38405  *    username,password, lang = for login actions.
38406  *    check = 1 for periodic checking that sesion is valid.
38407  *    passwordRequest = email request password
38408  *    logout = 1 = to logout
38409  * 
38410  * Affects: (this id="????" elements)
38411  *   loading  (removed) (used to indicate application is loading)
38412  *   loading-mask (hides) (used to hide application when it's building loading)
38413  *   
38414  * 
38415  * Usage: 
38416  *    
38417  * 
38418  * Myapp.login = Roo.Login({
38419      url: xxxx,
38420    
38421      realm : 'Myapp', 
38422      
38423      
38424      method : 'POST',
38425      
38426      
38427      * 
38428  })
38429  * 
38430  * 
38431  * 
38432  **/
38433  
38434 Roo.Login = function(cfg)
38435 {
38436     this.addEvents({
38437         'refreshed' : true
38438     });
38439     
38440     Roo.apply(this,cfg);
38441     
38442     Roo.onReady(function() {
38443         this.onLoad();
38444     }, this);
38445     // call parent..
38446     
38447    
38448     Roo.Login.superclass.constructor.call(this, this);
38449     //this.addxtype(this.items[0]);
38450     
38451     
38452 }
38453
38454
38455 Roo.extend(Roo.Login, Roo.LayoutDialog, {
38456     
38457     /**
38458      * @cfg {String} method
38459      * Method used to query for login details.
38460      */
38461     
38462     method : 'POST',
38463     /**
38464      * @cfg {String} url
38465      * URL to query login data. - eg. baseURL + '/Login.php'
38466      */
38467     url : '',
38468     
38469     /**
38470      * @property user
38471      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
38472      * @type {Object} 
38473      */
38474     user : false,
38475     /**
38476      * @property checkFails
38477      * Number of times we have attempted to get authentication check, and failed.
38478      * @type {Number} 
38479      */
38480     checkFails : 0,
38481       /**
38482      * @property intervalID
38483      * The window interval that does the constant login checking.
38484      * @type {Number} 
38485      */
38486     intervalID : 0,
38487     
38488     
38489     onLoad : function() // called on page load...
38490     {
38491         // load 
38492          
38493         if (Roo.get('loading')) { // clear any loading indicator..
38494             Roo.get('loading').remove();
38495         }
38496         
38497         //this.switchLang('en'); // set the language to english..
38498        
38499         this.check({
38500             success:  function(response, opts)  {  // check successfull...
38501             
38502                 var res = this.processResponse(response);
38503                 this.checkFails =0;
38504                 if (!res.success) { // error!
38505                     this.checkFails = 5;
38506                     //console.log('call failure');
38507                     return this.failure(response,opts);
38508                 }
38509                 
38510                 if (!res.data.id) { // id=0 == login failure.
38511                     return this.show();
38512                 }
38513                 
38514                               
38515                         //console.log(success);
38516                 this.fillAuth(res.data);   
38517                 this.checkFails =0;
38518                 Roo.XComponent.build();
38519             },
38520             failure : this.show
38521         });
38522         
38523     }, 
38524     
38525     
38526     check: function(cfg) // called every so often to refresh cookie etc..
38527     {
38528         if (cfg.again) { // could be undefined..
38529             this.checkFails++;
38530         } else {
38531             this.checkFails = 0;
38532         }
38533         var _this = this;
38534         if (this.sending) {
38535             if ( this.checkFails > 4) {
38536                 Roo.MessageBox.alert("Error",  
38537                     "Error getting authentication status. - try reloading, or wait a while", function() {
38538                         _this.sending = false;
38539                     }); 
38540                 return;
38541             }
38542             cfg.again = true;
38543             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
38544             return;
38545         }
38546         this.sending = true;
38547         
38548         Roo.Ajax.request({  
38549             url: this.url,
38550             params: {
38551                 getAuthUser: true
38552             },  
38553             method: this.method,
38554             success:  cfg.success || this.success,
38555             failure : cfg.failure || this.failure,
38556             scope : this,
38557             callCfg : cfg
38558               
38559         });  
38560     }, 
38561     
38562     
38563     logout: function()
38564     {
38565         window.onbeforeunload = function() { }; // false does not work for IE..
38566         this.user = false;
38567         var _this = this;
38568         
38569         Roo.Ajax.request({  
38570             url: this.url,
38571             params: {
38572                 logout: 1
38573             },  
38574             method: 'GET',
38575             failure : function() {
38576                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
38577                     document.location = document.location.toString() + '?ts=' + Math.random();
38578                 });
38579                 
38580             },
38581             success : function() {
38582                 _this.user = false;
38583                 this.checkFails =0;
38584                 // fixme..
38585                 document.location = document.location.toString() + '?ts=' + Math.random();
38586             }
38587               
38588               
38589         }); 
38590     },
38591     
38592     processResponse : function (response)
38593     {
38594         var res = '';
38595         try {
38596             res = Roo.decode(response.responseText);
38597             // oops...
38598             if (typeof(res) != 'object') {
38599                 res = { success : false, errorMsg : res, errors : true };
38600             }
38601             if (typeof(res.success) == 'undefined') {
38602                 res.success = false;
38603             }
38604             
38605         } catch(e) {
38606             res = { success : false,  errorMsg : response.responseText, errors : true };
38607         }
38608         return res;
38609     },
38610     
38611     success : function(response, opts)  // check successfull...
38612     {  
38613         this.sending = false;
38614         var res = this.processResponse(response);
38615         if (!res.success) {
38616             return this.failure(response, opts);
38617         }
38618         if (!res.data || !res.data.id) {
38619             return this.failure(response,opts);
38620         }
38621         //console.log(res);
38622         this.fillAuth(res.data);
38623         
38624         this.checkFails =0;
38625         
38626     },
38627     
38628     
38629     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
38630     {
38631         this.authUser = -1;
38632         this.sending = false;
38633         var res = this.processResponse(response);
38634         //console.log(res);
38635         if ( this.checkFails > 2) {
38636         
38637             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
38638                 "Error getting authentication status. - try reloading"); 
38639             return;
38640         }
38641         opts.callCfg.again = true;
38642         this.check.defer(1000, this, [ opts.callCfg ]);
38643         return;  
38644     },
38645     
38646     
38647     
38648     fillAuth: function(au) {
38649         this.startAuthCheck();
38650         this.authUserId = au.id;
38651         this.authUser = au;
38652         this.lastChecked = new Date();
38653         this.fireEvent('refreshed', au);
38654         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
38655         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
38656         au.lang = au.lang || 'en';
38657         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
38658         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
38659         this.switchLang(au.lang );
38660         
38661      
38662         // open system... - -on setyp..
38663         if (this.authUserId  < 0) {
38664             Roo.MessageBox.alert("Warning", 
38665                 "This is an open system - please set up a admin user with a password.");  
38666         }
38667          
38668         //Pman.onload(); // which should do nothing if it's a re-auth result...
38669         
38670              
38671     },
38672     
38673     startAuthCheck : function() // starter for timeout checking..
38674     {
38675         if (this.intervalID) { // timer already in place...
38676             return false;
38677         }
38678         var _this = this;
38679         this.intervalID =  window.setInterval(function() {
38680               _this.check(false);
38681             }, 120000); // every 120 secs = 2mins..
38682         
38683         
38684     },
38685          
38686     
38687     switchLang : function (lang) 
38688     {
38689         _T = typeof(_T) == 'undefined' ? false : _T;
38690           if (!_T || !lang.length) {
38691             return;
38692         }
38693         
38694         if (!_T && lang != 'en') {
38695             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38696             return;
38697         }
38698         
38699         if (typeof(_T.en) == 'undefined') {
38700             _T.en = {};
38701             Roo.apply(_T.en, _T);
38702         }
38703         
38704         if (typeof(_T[lang]) == 'undefined') {
38705             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38706             return;
38707         }
38708         
38709         
38710         Roo.apply(_T, _T[lang]);
38711         // just need to set the text values for everything...
38712         var _this = this;
38713         /* this will not work ...
38714         if (this.form) { 
38715             
38716                
38717             function formLabel(name, val) {
38718                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
38719             }
38720             
38721             formLabel('password', "Password"+':');
38722             formLabel('username', "Email Address"+':');
38723             formLabel('lang', "Language"+':');
38724             this.dialog.setTitle("Login");
38725             this.dialog.buttons[0].setText("Forgot Password");
38726             this.dialog.buttons[1].setText("Login");
38727         }
38728         */
38729         
38730         
38731     },
38732     
38733     
38734     title: "Login",
38735     modal: true,
38736     width:  350,
38737     //height: 230,
38738     height: 180,
38739     shadow: true,
38740     minWidth:200,
38741     minHeight:180,
38742     //proxyDrag: true,
38743     closable: false,
38744     draggable: false,
38745     collapsible: false,
38746     resizable: false,
38747     center: {  // needed??
38748         autoScroll:false,
38749         titlebar: false,
38750        // tabPosition: 'top',
38751         hideTabs: true,
38752         closeOnTab: true,
38753         alwaysShowTabs: false
38754     } ,
38755     listeners : {
38756         
38757         show  : function(dlg)
38758         {
38759             //console.log(this);
38760             this.form = this.layout.getRegion('center').activePanel.form;
38761             this.form.dialog = dlg;
38762             this.buttons[0].form = this.form;
38763             this.buttons[0].dialog = dlg;
38764             this.buttons[1].form = this.form;
38765             this.buttons[1].dialog = dlg;
38766            
38767            //this.resizeToLogo.defer(1000,this);
38768             // this is all related to resizing for logos..
38769             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
38770            //// if (!sz) {
38771              //   this.resizeToLogo.defer(1000,this);
38772              //   return;
38773            // }
38774             //var w = Ext.lib.Dom.getViewWidth() - 100;
38775             //var h = Ext.lib.Dom.getViewHeight() - 100;
38776             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
38777             //this.center();
38778             if (this.disabled) {
38779                 this.hide();
38780                 return;
38781             }
38782             
38783             if (this.user.id < 0) { // used for inital setup situations.
38784                 return;
38785             }
38786             
38787             if (this.intervalID) {
38788                 // remove the timer
38789                 window.clearInterval(this.intervalID);
38790                 this.intervalID = false;
38791             }
38792             
38793             
38794             if (Roo.get('loading')) {
38795                 Roo.get('loading').remove();
38796             }
38797             if (Roo.get('loading-mask')) {
38798                 Roo.get('loading-mask').hide();
38799             }
38800             
38801             //incomming._node = tnode;
38802             this.form.reset();
38803             //this.dialog.modal = !modal;
38804             //this.dialog.show();
38805             this.el.unmask(); 
38806             
38807             
38808             this.form.setValues({
38809                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
38810                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
38811             });
38812             
38813             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
38814             if (this.form.findField('username').getValue().length > 0 ){
38815                 this.form.findField('password').focus();
38816             } else {
38817                this.form.findField('username').focus();
38818             }
38819     
38820         }
38821     },
38822     items : [
38823          {
38824        
38825             xtype : 'ContentPanel',
38826             xns : Roo,
38827             region: 'center',
38828             fitToFrame : true,
38829             
38830             items : [
38831     
38832                 {
38833                
38834                     xtype : 'Form',
38835                     xns : Roo.form,
38836                     labelWidth: 100,
38837                     style : 'margin: 10px;',
38838                     
38839                     listeners : {
38840                         actionfailed : function(f, act) {
38841                             // form can return { errors: .... }
38842                                 
38843                             //act.result.errors // invalid form element list...
38844                             //act.result.errorMsg// invalid form element list...
38845                             
38846                             this.dialog.el.unmask();
38847                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
38848                                         "Login failed - communication error - try again.");
38849                                       
38850                         },
38851                         actioncomplete: function(re, act) {
38852                              
38853                             Roo.state.Manager.set(
38854                                 this.dialog.realm + '.username',  
38855                                     this.findField('username').getValue()
38856                             );
38857                             Roo.state.Manager.set(
38858                                 this.dialog.realm + '.lang',  
38859                                 this.findField('lang').getValue() 
38860                             );
38861                             
38862                             this.dialog.fillAuth(act.result.data);
38863                               
38864                             this.dialog.hide();
38865                             
38866                             if (Roo.get('loading-mask')) {
38867                                 Roo.get('loading-mask').show();
38868                             }
38869                             Roo.XComponent.build();
38870                             
38871                              
38872                             
38873                         }
38874                     },
38875                     items : [
38876                         {
38877                             xtype : 'TextField',
38878                             xns : Roo.form,
38879                             fieldLabel: "Email Address",
38880                             name: 'username',
38881                             width:200,
38882                             autoCreate : {tag: "input", type: "text", size: "20"}
38883                         },
38884                         {
38885                             xtype : 'TextField',
38886                             xns : Roo.form,
38887                             fieldLabel: "Password",
38888                             inputType: 'password',
38889                             name: 'password',
38890                             width:200,
38891                             autoCreate : {tag: "input", type: "text", size: "20"},
38892                             listeners : {
38893                                 specialkey : function(e,ev) {
38894                                     if (ev.keyCode == 13) {
38895                                         this.form.dialog.el.mask("Logging in");
38896                                         this.form.doAction('submit', {
38897                                             url: this.form.dialog.url,
38898                                             method: this.form.dialog.method
38899                                         });
38900                                     }
38901                                 }
38902                             }  
38903                         },
38904                         {
38905                             xtype : 'ComboBox',
38906                             xns : Roo.form,
38907                             fieldLabel: "Language",
38908                             name : 'langdisp',
38909                             store: {
38910                                 xtype : 'SimpleStore',
38911                                 fields: ['lang', 'ldisp'],
38912                                 data : [
38913                                     [ 'en', 'English' ],
38914                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
38915                                     [ 'zh_CN', '\u7C21\u4E2D' ]
38916                                 ]
38917                             },
38918                             
38919                             valueField : 'lang',
38920                             hiddenName:  'lang',
38921                             width: 200,
38922                             displayField:'ldisp',
38923                             typeAhead: false,
38924                             editable: false,
38925                             mode: 'local',
38926                             triggerAction: 'all',
38927                             emptyText:'Select a Language...',
38928                             selectOnFocus:true,
38929                             listeners : {
38930                                 select :  function(cb, rec, ix) {
38931                                     this.form.switchLang(rec.data.lang);
38932                                 }
38933                             }
38934                         
38935                         }
38936                     ]
38937                 }
38938                   
38939                 
38940             ]
38941         }
38942     ],
38943     buttons : [
38944         {
38945             xtype : 'Button',
38946             xns : 'Roo',
38947             text : "Forgot Password",
38948             listeners : {
38949                 click : function() {
38950                     //console.log(this);
38951                     var n = this.form.findField('username').getValue();
38952                     if (!n.length) {
38953                         Roo.MessageBox.alert("Error", "Fill in your email address");
38954                         return;
38955                     }
38956                     Roo.Ajax.request({
38957                         url: this.dialog.url,
38958                         params: {
38959                             passwordRequest: n
38960                         },
38961                         method: this.dialog.method,
38962                         success:  function(response, opts)  {  // check successfull...
38963                         
38964                             var res = this.dialog.processResponse(response);
38965                             if (!res.success) { // error!
38966                                Roo.MessageBox.alert("Error" ,
38967                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
38968                                return;
38969                             }
38970                             Roo.MessageBox.alert("Notice" ,
38971                                 "Please check you email for the Password Reset message");
38972                         },
38973                         failure : function() {
38974                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
38975                         }
38976                         
38977                     });
38978                 }
38979             }
38980         },
38981         {
38982             xtype : 'Button',
38983             xns : 'Roo',
38984             text : "Login",
38985             listeners : {
38986                 
38987                 click : function () {
38988                         
38989                     this.dialog.el.mask("Logging in");
38990                     this.form.doAction('submit', {
38991                             url: this.dialog.url,
38992                             method: this.dialog.method
38993                     });
38994                 }
38995             }
38996         }
38997     ]
38998   
38999   
39000 })
39001  
39002
39003
39004