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("Old Dialog Message:" +  msgEl.innerHTML )
15787                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15788                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15789                 
15790             }
15791             var d = this.getDialog();
15792             opt = options;
15793             d.setTitle(opt.title || "&#160;");
15794             d.close.setDisplayed(opt.closable !== false);
15795             activeTextEl = textboxEl;
15796             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15797             if(opt.prompt){
15798                 if(opt.multiline){
15799                     textboxEl.hide();
15800                     textareaEl.show();
15801                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15802                         opt.multiline : this.defaultTextHeight);
15803                     activeTextEl = textareaEl;
15804                 }else{
15805                     textboxEl.show();
15806                     textareaEl.hide();
15807                 }
15808             }else{
15809                 textboxEl.hide();
15810                 textareaEl.hide();
15811             }
15812             progressEl.setDisplayed(opt.progress === true);
15813             this.updateProgress(0);
15814             activeTextEl.dom.value = opt.value || "";
15815             if(opt.prompt){
15816                 dlg.setDefaultButton(activeTextEl);
15817             }else{
15818                 var bs = opt.buttons;
15819                 var db = null;
15820                 if(bs && bs.ok){
15821                     db = buttons["ok"];
15822                 }else if(bs && bs.yes){
15823                     db = buttons["yes"];
15824                 }
15825                 dlg.setDefaultButton(db);
15826             }
15827             bwidth = updateButtons(opt.buttons);
15828             this.updateText(opt.msg);
15829             if(opt.cls){
15830                 d.el.addClass(opt.cls);
15831             }
15832             d.proxyDrag = opt.proxyDrag === true;
15833             d.modal = opt.modal !== false;
15834             d.mask = opt.modal !== false ? mask : false;
15835             if(!d.isVisible()){
15836                 // force it to the end of the z-index stack so it gets a cursor in FF
15837                 document.body.appendChild(dlg.el.dom);
15838                 d.animateTarget = null;
15839                 d.show(options.animEl);
15840             }
15841             return this;
15842         },
15843
15844         /**
15845          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15846          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15847          * and closing the message box when the process is complete.
15848          * @param {String} title The title bar text
15849          * @param {String} msg The message box body text
15850          * @return {Roo.MessageBox} This message box
15851          */
15852         progress : function(title, msg){
15853             this.show({
15854                 title : title,
15855                 msg : msg,
15856                 buttons: false,
15857                 progress:true,
15858                 closable:false,
15859                 minWidth: this.minProgressWidth,
15860                 modal : true
15861             });
15862             return this;
15863         },
15864
15865         /**
15866          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15867          * If a callback function is passed it will be called after the user clicks the button, and the
15868          * id of the button that was clicked will be passed as the only parameter to the callback
15869          * (could also be the top-right close button).
15870          * @param {String} title The title bar text
15871          * @param {String} msg The message box body text
15872          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15873          * @param {Object} scope (optional) The scope of the callback function
15874          * @return {Roo.MessageBox} This message box
15875          */
15876         alert : function(title, msg, fn, scope){
15877             this.show({
15878                 title : title,
15879                 msg : msg,
15880                 buttons: this.OK,
15881                 fn: fn,
15882                 scope : scope,
15883                 modal : true
15884             });
15885             return this;
15886         },
15887
15888         /**
15889          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15890          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15891          * You are responsible for closing the message box when the process is complete.
15892          * @param {String} msg The message box body text
15893          * @param {String} title (optional) The title bar text
15894          * @return {Roo.MessageBox} This message box
15895          */
15896         wait : function(msg, title){
15897             this.show({
15898                 title : title,
15899                 msg : msg,
15900                 buttons: false,
15901                 closable:false,
15902                 progress:true,
15903                 modal:true,
15904                 width:300,
15905                 wait:true
15906             });
15907             waitTimer = Roo.TaskMgr.start({
15908                 run: function(i){
15909                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15910                 },
15911                 interval: 1000
15912             });
15913             return this;
15914         },
15915
15916         /**
15917          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15918          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15919          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15920          * @param {String} title The title bar text
15921          * @param {String} msg The message box body text
15922          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15923          * @param {Object} scope (optional) The scope of the callback function
15924          * @return {Roo.MessageBox} This message box
15925          */
15926         confirm : function(title, msg, fn, scope){
15927             this.show({
15928                 title : title,
15929                 msg : msg,
15930                 buttons: this.YESNO,
15931                 fn: fn,
15932                 scope : scope,
15933                 modal : true
15934             });
15935             return this;
15936         },
15937
15938         /**
15939          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15940          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15941          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15942          * (could also be the top-right close button) and the text that was entered will be passed as the two
15943          * parameters to the callback.
15944          * @param {String} title The title bar text
15945          * @param {String} msg The message box body text
15946          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15947          * @param {Object} scope (optional) The scope of the callback function
15948          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15949          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15950          * @return {Roo.MessageBox} This message box
15951          */
15952         prompt : function(title, msg, fn, scope, multiline){
15953             this.show({
15954                 title : title,
15955                 msg : msg,
15956                 buttons: this.OKCANCEL,
15957                 fn: fn,
15958                 minWidth:250,
15959                 scope : scope,
15960                 prompt:true,
15961                 multiline: multiline,
15962                 modal : true
15963             });
15964             return this;
15965         },
15966
15967         /**
15968          * Button config that displays a single OK button
15969          * @type Object
15970          */
15971         OK : {ok:true},
15972         /**
15973          * Button config that displays Yes and No buttons
15974          * @type Object
15975          */
15976         YESNO : {yes:true, no:true},
15977         /**
15978          * Button config that displays OK and Cancel buttons
15979          * @type Object
15980          */
15981         OKCANCEL : {ok:true, cancel:true},
15982         /**
15983          * Button config that displays Yes, No and Cancel buttons
15984          * @type Object
15985          */
15986         YESNOCANCEL : {yes:true, no:true, cancel:true},
15987
15988         /**
15989          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15990          * @type Number
15991          */
15992         defaultTextHeight : 75,
15993         /**
15994          * The maximum width in pixels of the message box (defaults to 600)
15995          * @type Number
15996          */
15997         maxWidth : 600,
15998         /**
15999          * The minimum width in pixels of the message box (defaults to 100)
16000          * @type Number
16001          */
16002         minWidth : 100,
16003         /**
16004          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16005          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16006          * @type Number
16007          */
16008         minProgressWidth : 250,
16009         /**
16010          * An object containing the default button text strings that can be overriden for localized language support.
16011          * Supported properties are: ok, cancel, yes and no.
16012          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16013          * @type Object
16014          */
16015         buttonText : {
16016             ok : "OK",
16017             cancel : "Cancel",
16018             yes : "Yes",
16019             no : "No"
16020         }
16021     };
16022 }();
16023
16024 /**
16025  * Shorthand for {@link Roo.MessageBox}
16026  */
16027 Roo.Msg = Roo.MessageBox;/*
16028  * Based on:
16029  * Ext JS Library 1.1.1
16030  * Copyright(c) 2006-2007, Ext JS, LLC.
16031  *
16032  * Originally Released Under LGPL - original licence link has changed is not relivant.
16033  *
16034  * Fork - LGPL
16035  * <script type="text/javascript">
16036  */
16037 /**
16038  * @class Roo.QuickTips
16039  * Provides attractive and customizable tooltips for any element.
16040  * @singleton
16041  */
16042 Roo.QuickTips = function(){
16043     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16044     var ce, bd, xy, dd;
16045     var visible = false, disabled = true, inited = false;
16046     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16047     
16048     var onOver = function(e){
16049         if(disabled){
16050             return;
16051         }
16052         var t = e.getTarget();
16053         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16054             return;
16055         }
16056         if(ce && t == ce.el){
16057             clearTimeout(hideProc);
16058             return;
16059         }
16060         if(t && tagEls[t.id]){
16061             tagEls[t.id].el = t;
16062             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16063             return;
16064         }
16065         var ttp, et = Roo.fly(t);
16066         var ns = cfg.namespace;
16067         if(tm.interceptTitles && t.title){
16068             ttp = t.title;
16069             t.qtip = ttp;
16070             t.removeAttribute("title");
16071             e.preventDefault();
16072         }else{
16073             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16074         }
16075         if(ttp){
16076             showProc = show.defer(tm.showDelay, tm, [{
16077                 el: t, 
16078                 text: ttp, 
16079                 width: et.getAttributeNS(ns, cfg.width),
16080                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16081                 title: et.getAttributeNS(ns, cfg.title),
16082                     cls: et.getAttributeNS(ns, cfg.cls)
16083             }]);
16084         }
16085     };
16086     
16087     var onOut = function(e){
16088         clearTimeout(showProc);
16089         var t = e.getTarget();
16090         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16091             hideProc = setTimeout(hide, tm.hideDelay);
16092         }
16093     };
16094     
16095     var onMove = function(e){
16096         if(disabled){
16097             return;
16098         }
16099         xy = e.getXY();
16100         xy[1] += 18;
16101         if(tm.trackMouse && ce){
16102             el.setXY(xy);
16103         }
16104     };
16105     
16106     var onDown = function(e){
16107         clearTimeout(showProc);
16108         clearTimeout(hideProc);
16109         if(!e.within(el)){
16110             if(tm.hideOnClick){
16111                 hide();
16112                 tm.disable();
16113                 tm.enable.defer(100, tm);
16114             }
16115         }
16116     };
16117     
16118     var getPad = function(){
16119         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16120     };
16121
16122     var show = function(o){
16123         if(disabled){
16124             return;
16125         }
16126         clearTimeout(dismissProc);
16127         ce = o;
16128         if(removeCls){ // in case manually hidden
16129             el.removeClass(removeCls);
16130             removeCls = null;
16131         }
16132         if(ce.cls){
16133             el.addClass(ce.cls);
16134             removeCls = ce.cls;
16135         }
16136         if(ce.title){
16137             tipTitle.update(ce.title);
16138             tipTitle.show();
16139         }else{
16140             tipTitle.update('');
16141             tipTitle.hide();
16142         }
16143         el.dom.style.width  = tm.maxWidth+'px';
16144         //tipBody.dom.style.width = '';
16145         tipBodyText.update(o.text);
16146         var p = getPad(), w = ce.width;
16147         if(!w){
16148             var td = tipBodyText.dom;
16149             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16150             if(aw > tm.maxWidth){
16151                 w = tm.maxWidth;
16152             }else if(aw < tm.minWidth){
16153                 w = tm.minWidth;
16154             }else{
16155                 w = aw;
16156             }
16157         }
16158         //tipBody.setWidth(w);
16159         el.setWidth(parseInt(w, 10) + p);
16160         if(ce.autoHide === false){
16161             close.setDisplayed(true);
16162             if(dd){
16163                 dd.unlock();
16164             }
16165         }else{
16166             close.setDisplayed(false);
16167             if(dd){
16168                 dd.lock();
16169             }
16170         }
16171         if(xy){
16172             el.avoidY = xy[1]-18;
16173             el.setXY(xy);
16174         }
16175         if(tm.animate){
16176             el.setOpacity(.1);
16177             el.setStyle("visibility", "visible");
16178             el.fadeIn({callback: afterShow});
16179         }else{
16180             afterShow();
16181         }
16182     };
16183     
16184     var afterShow = function(){
16185         if(ce){
16186             el.show();
16187             esc.enable();
16188             if(tm.autoDismiss && ce.autoHide !== false){
16189                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16190             }
16191         }
16192     };
16193     
16194     var hide = function(noanim){
16195         clearTimeout(dismissProc);
16196         clearTimeout(hideProc);
16197         ce = null;
16198         if(el.isVisible()){
16199             esc.disable();
16200             if(noanim !== true && tm.animate){
16201                 el.fadeOut({callback: afterHide});
16202             }else{
16203                 afterHide();
16204             } 
16205         }
16206     };
16207     
16208     var afterHide = function(){
16209         el.hide();
16210         if(removeCls){
16211             el.removeClass(removeCls);
16212             removeCls = null;
16213         }
16214     };
16215     
16216     return {
16217         /**
16218         * @cfg {Number} minWidth
16219         * The minimum width of the quick tip (defaults to 40)
16220         */
16221        minWidth : 40,
16222         /**
16223         * @cfg {Number} maxWidth
16224         * The maximum width of the quick tip (defaults to 300)
16225         */
16226        maxWidth : 300,
16227         /**
16228         * @cfg {Boolean} interceptTitles
16229         * True to automatically use the element's DOM title value if available (defaults to false)
16230         */
16231        interceptTitles : false,
16232         /**
16233         * @cfg {Boolean} trackMouse
16234         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16235         */
16236        trackMouse : false,
16237         /**
16238         * @cfg {Boolean} hideOnClick
16239         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16240         */
16241        hideOnClick : true,
16242         /**
16243         * @cfg {Number} showDelay
16244         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16245         */
16246        showDelay : 500,
16247         /**
16248         * @cfg {Number} hideDelay
16249         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16250         */
16251        hideDelay : 200,
16252         /**
16253         * @cfg {Boolean} autoHide
16254         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16255         * Used in conjunction with hideDelay.
16256         */
16257        autoHide : true,
16258         /**
16259         * @cfg {Boolean}
16260         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16261         * (defaults to true).  Used in conjunction with autoDismissDelay.
16262         */
16263        autoDismiss : true,
16264         /**
16265         * @cfg {Number}
16266         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16267         */
16268        autoDismissDelay : 5000,
16269        /**
16270         * @cfg {Boolean} animate
16271         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16272         */
16273        animate : false,
16274
16275        /**
16276         * @cfg {String} title
16277         * Title text to display (defaults to '').  This can be any valid HTML markup.
16278         */
16279         title: '',
16280        /**
16281         * @cfg {String} text
16282         * Body text to display (defaults to '').  This can be any valid HTML markup.
16283         */
16284         text : '',
16285        /**
16286         * @cfg {String} cls
16287         * A CSS class to apply to the base quick tip element (defaults to '').
16288         */
16289         cls : '',
16290        /**
16291         * @cfg {Number} width
16292         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16293         * minWidth or maxWidth.
16294         */
16295         width : null,
16296
16297     /**
16298      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16299      * or display QuickTips in a page.
16300      */
16301        init : function(){
16302           tm = Roo.QuickTips;
16303           cfg = tm.tagConfig;
16304           if(!inited){
16305               if(!Roo.isReady){ // allow calling of init() before onReady
16306                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16307                   return;
16308               }
16309               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16310               el.fxDefaults = {stopFx: true};
16311               // maximum custom styling
16312               //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>');
16313               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>');              
16314               tipTitle = el.child('h3');
16315               tipTitle.enableDisplayMode("block");
16316               tipBody = el.child('div.x-tip-bd');
16317               tipBodyText = el.child('div.x-tip-bd-inner');
16318               //bdLeft = el.child('div.x-tip-bd-left');
16319               //bdRight = el.child('div.x-tip-bd-right');
16320               close = el.child('div.x-tip-close');
16321               close.enableDisplayMode("block");
16322               close.on("click", hide);
16323               var d = Roo.get(document);
16324               d.on("mousedown", onDown);
16325               d.on("mouseover", onOver);
16326               d.on("mouseout", onOut);
16327               d.on("mousemove", onMove);
16328               esc = d.addKeyListener(27, hide);
16329               esc.disable();
16330               if(Roo.dd.DD){
16331                   dd = el.initDD("default", null, {
16332                       onDrag : function(){
16333                           el.sync();  
16334                       }
16335                   });
16336                   dd.setHandleElId(tipTitle.id);
16337                   dd.lock();
16338               }
16339               inited = true;
16340           }
16341           this.enable(); 
16342        },
16343
16344     /**
16345      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16346      * are supported:
16347      * <pre>
16348 Property    Type                   Description
16349 ----------  ---------------------  ------------------------------------------------------------------------
16350 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16351      * </ul>
16352      * @param {Object} config The config object
16353      */
16354        register : function(config){
16355            var cs = config instanceof Array ? config : arguments;
16356            for(var i = 0, len = cs.length; i < len; i++) {
16357                var c = cs[i];
16358                var target = c.target;
16359                if(target){
16360                    if(target instanceof Array){
16361                        for(var j = 0, jlen = target.length; j < jlen; j++){
16362                            tagEls[target[j]] = c;
16363                        }
16364                    }else{
16365                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16366                    }
16367                }
16368            }
16369        },
16370
16371     /**
16372      * Removes this quick tip from its element and destroys it.
16373      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16374      */
16375        unregister : function(el){
16376            delete tagEls[Roo.id(el)];
16377        },
16378
16379     /**
16380      * Enable this quick tip.
16381      */
16382        enable : function(){
16383            if(inited && disabled){
16384                locks.pop();
16385                if(locks.length < 1){
16386                    disabled = false;
16387                }
16388            }
16389        },
16390
16391     /**
16392      * Disable this quick tip.
16393      */
16394        disable : function(){
16395           disabled = true;
16396           clearTimeout(showProc);
16397           clearTimeout(hideProc);
16398           clearTimeout(dismissProc);
16399           if(ce){
16400               hide(true);
16401           }
16402           locks.push(1);
16403        },
16404
16405     /**
16406      * Returns true if the quick tip is enabled, else false.
16407      */
16408        isEnabled : function(){
16409             return !disabled;
16410        },
16411
16412         // private
16413        tagConfig : {
16414            namespace : "ext",
16415            attribute : "qtip",
16416            width : "width",
16417            target : "target",
16418            title : "qtitle",
16419            hide : "hide",
16420            cls : "qclass"
16421        }
16422    };
16423 }();
16424
16425 // backwards compat
16426 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16427  * Based on:
16428  * Ext JS Library 1.1.1
16429  * Copyright(c) 2006-2007, Ext JS, LLC.
16430  *
16431  * Originally Released Under LGPL - original licence link has changed is not relivant.
16432  *
16433  * Fork - LGPL
16434  * <script type="text/javascript">
16435  */
16436  
16437
16438 /**
16439  * @class Roo.tree.TreePanel
16440  * @extends Roo.data.Tree
16441
16442  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16443  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16444  * @cfg {Boolean} enableDD true to enable drag and drop
16445  * @cfg {Boolean} enableDrag true to enable just drag
16446  * @cfg {Boolean} enableDrop true to enable just drop
16447  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16448  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16449  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16450  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16451  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16452  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16453  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16454  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16455  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16456  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16457  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16458  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16459  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16460  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16461  * @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>
16462  * @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>
16463  * 
16464  * @constructor
16465  * @param {String/HTMLElement/Element} el The container element
16466  * @param {Object} config
16467  */
16468 Roo.tree.TreePanel = function(el, config){
16469     var root = false;
16470     var loader = false;
16471     if (config.root) {
16472         root = config.root;
16473         delete config.root;
16474     }
16475     if (config.loader) {
16476         loader = config.loader;
16477         delete config.loader;
16478     }
16479     
16480     Roo.apply(this, config);
16481     Roo.tree.TreePanel.superclass.constructor.call(this);
16482     this.el = Roo.get(el);
16483     this.el.addClass('x-tree');
16484     //console.log(root);
16485     if (root) {
16486         this.setRootNode( Roo.factory(root, Roo.tree));
16487     }
16488     if (loader) {
16489         this.loader = Roo.factory(loader, Roo.tree);
16490     }
16491    /**
16492     * Read-only. The id of the container element becomes this TreePanel's id.
16493     */
16494     this.id = this.el.id;
16495     this.addEvents({
16496         /**
16497         * @event beforeload
16498         * Fires before a node is loaded, return false to cancel
16499         * @param {Node} node The node being loaded
16500         */
16501         "beforeload" : true,
16502         /**
16503         * @event load
16504         * Fires when a node is loaded
16505         * @param {Node} node The node that was loaded
16506         */
16507         "load" : true,
16508         /**
16509         * @event textchange
16510         * Fires when the text for a node is changed
16511         * @param {Node} node The node
16512         * @param {String} text The new text
16513         * @param {String} oldText The old text
16514         */
16515         "textchange" : true,
16516         /**
16517         * @event beforeexpand
16518         * Fires before a node is expanded, return false to cancel.
16519         * @param {Node} node The node
16520         * @param {Boolean} deep
16521         * @param {Boolean} anim
16522         */
16523         "beforeexpand" : true,
16524         /**
16525         * @event beforecollapse
16526         * Fires before a node is collapsed, return false to cancel.
16527         * @param {Node} node The node
16528         * @param {Boolean} deep
16529         * @param {Boolean} anim
16530         */
16531         "beforecollapse" : true,
16532         /**
16533         * @event expand
16534         * Fires when a node is expanded
16535         * @param {Node} node The node
16536         */
16537         "expand" : true,
16538         /**
16539         * @event disabledchange
16540         * Fires when the disabled status of a node changes
16541         * @param {Node} node The node
16542         * @param {Boolean} disabled
16543         */
16544         "disabledchange" : true,
16545         /**
16546         * @event collapse
16547         * Fires when a node is collapsed
16548         * @param {Node} node The node
16549         */
16550         "collapse" : true,
16551         /**
16552         * @event beforeclick
16553         * Fires before click processing on a node. Return false to cancel the default action.
16554         * @param {Node} node The node
16555         * @param {Roo.EventObject} e The event object
16556         */
16557         "beforeclick":true,
16558         /**
16559         * @event checkchange
16560         * Fires when a node with a checkbox's checked property changes
16561         * @param {Node} this This node
16562         * @param {Boolean} checked
16563         */
16564         "checkchange":true,
16565         /**
16566         * @event click
16567         * Fires when a node is clicked
16568         * @param {Node} node The node
16569         * @param {Roo.EventObject} e The event object
16570         */
16571         "click":true,
16572         /**
16573         * @event dblclick
16574         * Fires when a node is double clicked
16575         * @param {Node} node The node
16576         * @param {Roo.EventObject} e The event object
16577         */
16578         "dblclick":true,
16579         /**
16580         * @event contextmenu
16581         * Fires when a node is right clicked
16582         * @param {Node} node The node
16583         * @param {Roo.EventObject} e The event object
16584         */
16585         "contextmenu":true,
16586         /**
16587         * @event beforechildrenrendered
16588         * Fires right before the child nodes for a node are rendered
16589         * @param {Node} node The node
16590         */
16591         "beforechildrenrendered":true,
16592         /**
16593         * @event startdrag
16594         * Fires when a node starts being dragged
16595         * @param {Roo.tree.TreePanel} this
16596         * @param {Roo.tree.TreeNode} node
16597         * @param {event} e The raw browser event
16598         */ 
16599        "startdrag" : true,
16600        /**
16601         * @event enddrag
16602         * Fires when a drag operation is complete
16603         * @param {Roo.tree.TreePanel} this
16604         * @param {Roo.tree.TreeNode} node
16605         * @param {event} e The raw browser event
16606         */
16607        "enddrag" : true,
16608        /**
16609         * @event dragdrop
16610         * Fires when a dragged node is dropped on a valid DD target
16611         * @param {Roo.tree.TreePanel} this
16612         * @param {Roo.tree.TreeNode} node
16613         * @param {DD} dd The dd it was dropped on
16614         * @param {event} e The raw browser event
16615         */
16616        "dragdrop" : true,
16617        /**
16618         * @event beforenodedrop
16619         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16620         * passed to handlers has the following properties:<br />
16621         * <ul style="padding:5px;padding-left:16px;">
16622         * <li>tree - The TreePanel</li>
16623         * <li>target - The node being targeted for the drop</li>
16624         * <li>data - The drag data from the drag source</li>
16625         * <li>point - The point of the drop - append, above or below</li>
16626         * <li>source - The drag source</li>
16627         * <li>rawEvent - Raw mouse event</li>
16628         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16629         * to be inserted by setting them on this object.</li>
16630         * <li>cancel - Set this to true to cancel the drop.</li>
16631         * </ul>
16632         * @param {Object} dropEvent
16633         */
16634        "beforenodedrop" : true,
16635        /**
16636         * @event nodedrop
16637         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16638         * passed to handlers has the following properties:<br />
16639         * <ul style="padding:5px;padding-left:16px;">
16640         * <li>tree - The TreePanel</li>
16641         * <li>target - The node being targeted for the drop</li>
16642         * <li>data - The drag data from the drag source</li>
16643         * <li>point - The point of the drop - append, above or below</li>
16644         * <li>source - The drag source</li>
16645         * <li>rawEvent - Raw mouse event</li>
16646         * <li>dropNode - Dropped node(s).</li>
16647         * </ul>
16648         * @param {Object} dropEvent
16649         */
16650        "nodedrop" : true,
16651         /**
16652         * @event nodedragover
16653         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16654         * passed to handlers has the following properties:<br />
16655         * <ul style="padding:5px;padding-left:16px;">
16656         * <li>tree - The TreePanel</li>
16657         * <li>target - The node being targeted for the drop</li>
16658         * <li>data - The drag data from the drag source</li>
16659         * <li>point - The point of the drop - append, above or below</li>
16660         * <li>source - The drag source</li>
16661         * <li>rawEvent - Raw mouse event</li>
16662         * <li>dropNode - Drop node(s) provided by the source.</li>
16663         * <li>cancel - Set this to true to signal drop not allowed.</li>
16664         * </ul>
16665         * @param {Object} dragOverEvent
16666         */
16667        "nodedragover" : true
16668         
16669     });
16670     if(this.singleExpand){
16671        this.on("beforeexpand", this.restrictExpand, this);
16672     }
16673     if (this.editor) {
16674         this.editor.tree = this;
16675         this.editor = Roo.factory(this.editor, Roo.tree);
16676     }
16677     
16678     if (this.selModel) {
16679         this.selModel = Roo.factory(this.selModel, Roo.tree);
16680     }
16681    
16682 };
16683 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16684     rootVisible : true,
16685     animate: Roo.enableFx,
16686     lines : true,
16687     enableDD : false,
16688     hlDrop : Roo.enableFx,
16689   
16690     renderer: false,
16691     
16692     rendererTip: false,
16693     // private
16694     restrictExpand : function(node){
16695         var p = node.parentNode;
16696         if(p){
16697             if(p.expandedChild && p.expandedChild.parentNode == p){
16698                 p.expandedChild.collapse();
16699             }
16700             p.expandedChild = node;
16701         }
16702     },
16703
16704     // private override
16705     setRootNode : function(node){
16706         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16707         if(!this.rootVisible){
16708             node.ui = new Roo.tree.RootTreeNodeUI(node);
16709         }
16710         return node;
16711     },
16712
16713     /**
16714      * Returns the container element for this TreePanel
16715      */
16716     getEl : function(){
16717         return this.el;
16718     },
16719
16720     /**
16721      * Returns the default TreeLoader for this TreePanel
16722      */
16723     getLoader : function(){
16724         return this.loader;
16725     },
16726
16727     /**
16728      * Expand all nodes
16729      */
16730     expandAll : function(){
16731         this.root.expand(true);
16732     },
16733
16734     /**
16735      * Collapse all nodes
16736      */
16737     collapseAll : function(){
16738         this.root.collapse(true);
16739     },
16740
16741     /**
16742      * Returns the selection model used by this TreePanel
16743      */
16744     getSelectionModel : function(){
16745         if(!this.selModel){
16746             this.selModel = new Roo.tree.DefaultSelectionModel();
16747         }
16748         return this.selModel;
16749     },
16750
16751     /**
16752      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16753      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16754      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16755      * @return {Array}
16756      */
16757     getChecked : function(a, startNode){
16758         startNode = startNode || this.root;
16759         var r = [];
16760         var f = function(){
16761             if(this.attributes.checked){
16762                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16763             }
16764         }
16765         startNode.cascade(f);
16766         return r;
16767     },
16768
16769     /**
16770      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16771      * @param {String} path
16772      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16773      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16774      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16775      */
16776     expandPath : function(path, attr, callback){
16777         attr = attr || "id";
16778         var keys = path.split(this.pathSeparator);
16779         var curNode = this.root;
16780         if(curNode.attributes[attr] != keys[1]){ // invalid root
16781             if(callback){
16782                 callback(false, null);
16783             }
16784             return;
16785         }
16786         var index = 1;
16787         var f = function(){
16788             if(++index == keys.length){
16789                 if(callback){
16790                     callback(true, curNode);
16791                 }
16792                 return;
16793             }
16794             var c = curNode.findChild(attr, keys[index]);
16795             if(!c){
16796                 if(callback){
16797                     callback(false, curNode);
16798                 }
16799                 return;
16800             }
16801             curNode = c;
16802             c.expand(false, false, f);
16803         };
16804         curNode.expand(false, false, f);
16805     },
16806
16807     /**
16808      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16809      * @param {String} path
16810      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16811      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16812      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16813      */
16814     selectPath : function(path, attr, callback){
16815         attr = attr || "id";
16816         var keys = path.split(this.pathSeparator);
16817         var v = keys.pop();
16818         if(keys.length > 0){
16819             var f = function(success, node){
16820                 if(success && node){
16821                     var n = node.findChild(attr, v);
16822                     if(n){
16823                         n.select();
16824                         if(callback){
16825                             callback(true, n);
16826                         }
16827                     }else if(callback){
16828                         callback(false, n);
16829                     }
16830                 }else{
16831                     if(callback){
16832                         callback(false, n);
16833                     }
16834                 }
16835             };
16836             this.expandPath(keys.join(this.pathSeparator), attr, f);
16837         }else{
16838             this.root.select();
16839             if(callback){
16840                 callback(true, this.root);
16841             }
16842         }
16843     },
16844
16845     getTreeEl : function(){
16846         return this.el;
16847     },
16848
16849     /**
16850      * Trigger rendering of this TreePanel
16851      */
16852     render : function(){
16853         if (this.innerCt) {
16854             return this; // stop it rendering more than once!!
16855         }
16856         
16857         this.innerCt = this.el.createChild({tag:"ul",
16858                cls:"x-tree-root-ct " +
16859                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16860
16861         if(this.containerScroll){
16862             Roo.dd.ScrollManager.register(this.el);
16863         }
16864         if((this.enableDD || this.enableDrop) && !this.dropZone){
16865            /**
16866             * The dropZone used by this tree if drop is enabled
16867             * @type Roo.tree.TreeDropZone
16868             */
16869              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16870                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16871            });
16872         }
16873         if((this.enableDD || this.enableDrag) && !this.dragZone){
16874            /**
16875             * The dragZone used by this tree if drag is enabled
16876             * @type Roo.tree.TreeDragZone
16877             */
16878             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16879                ddGroup: this.ddGroup || "TreeDD",
16880                scroll: this.ddScroll
16881            });
16882         }
16883         this.getSelectionModel().init(this);
16884         if (!this.root) {
16885             Roo.log("ROOT not set in tree");
16886             return this;
16887         }
16888         this.root.render();
16889         if(!this.rootVisible){
16890             this.root.renderChildren();
16891         }
16892         return this;
16893     }
16894 });/*
16895  * Based on:
16896  * Ext JS Library 1.1.1
16897  * Copyright(c) 2006-2007, Ext JS, LLC.
16898  *
16899  * Originally Released Under LGPL - original licence link has changed is not relivant.
16900  *
16901  * Fork - LGPL
16902  * <script type="text/javascript">
16903  */
16904  
16905
16906 /**
16907  * @class Roo.tree.DefaultSelectionModel
16908  * @extends Roo.util.Observable
16909  * The default single selection for a TreePanel.
16910  * @param {Object} cfg Configuration
16911  */
16912 Roo.tree.DefaultSelectionModel = function(cfg){
16913    this.selNode = null;
16914    
16915    
16916    
16917    this.addEvents({
16918        /**
16919         * @event selectionchange
16920         * Fires when the selected node changes
16921         * @param {DefaultSelectionModel} this
16922         * @param {TreeNode} node the new selection
16923         */
16924        "selectionchange" : true,
16925
16926        /**
16927         * @event beforeselect
16928         * Fires before the selected node changes, return false to cancel the change
16929         * @param {DefaultSelectionModel} this
16930         * @param {TreeNode} node the new selection
16931         * @param {TreeNode} node the old selection
16932         */
16933        "beforeselect" : true
16934    });
16935    
16936     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16937 };
16938
16939 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16940     init : function(tree){
16941         this.tree = tree;
16942         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16943         tree.on("click", this.onNodeClick, this);
16944     },
16945     
16946     onNodeClick : function(node, e){
16947         if (e.ctrlKey && this.selNode == node)  {
16948             this.unselect(node);
16949             return;
16950         }
16951         this.select(node);
16952     },
16953     
16954     /**
16955      * Select a node.
16956      * @param {TreeNode} node The node to select
16957      * @return {TreeNode} The selected node
16958      */
16959     select : function(node){
16960         var last = this.selNode;
16961         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16962             if(last){
16963                 last.ui.onSelectedChange(false);
16964             }
16965             this.selNode = node;
16966             node.ui.onSelectedChange(true);
16967             this.fireEvent("selectionchange", this, node, last);
16968         }
16969         return node;
16970     },
16971     
16972     /**
16973      * Deselect a node.
16974      * @param {TreeNode} node The node to unselect
16975      */
16976     unselect : function(node){
16977         if(this.selNode == node){
16978             this.clearSelections();
16979         }    
16980     },
16981     
16982     /**
16983      * Clear all selections
16984      */
16985     clearSelections : function(){
16986         var n = this.selNode;
16987         if(n){
16988             n.ui.onSelectedChange(false);
16989             this.selNode = null;
16990             this.fireEvent("selectionchange", this, null);
16991         }
16992         return n;
16993     },
16994     
16995     /**
16996      * Get the selected node
16997      * @return {TreeNode} The selected node
16998      */
16999     getSelectedNode : function(){
17000         return this.selNode;    
17001     },
17002     
17003     /**
17004      * Returns true if the node is selected
17005      * @param {TreeNode} node The node to check
17006      * @return {Boolean}
17007      */
17008     isSelected : function(node){
17009         return this.selNode == node;  
17010     },
17011
17012     /**
17013      * Selects the node above the selected node in the tree, intelligently walking the nodes
17014      * @return TreeNode The new selection
17015      */
17016     selectPrevious : function(){
17017         var s = this.selNode || this.lastSelNode;
17018         if(!s){
17019             return null;
17020         }
17021         var ps = s.previousSibling;
17022         if(ps){
17023             if(!ps.isExpanded() || ps.childNodes.length < 1){
17024                 return this.select(ps);
17025             } else{
17026                 var lc = ps.lastChild;
17027                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17028                     lc = lc.lastChild;
17029                 }
17030                 return this.select(lc);
17031             }
17032         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17033             return this.select(s.parentNode);
17034         }
17035         return null;
17036     },
17037
17038     /**
17039      * Selects the node above the selected node in the tree, intelligently walking the nodes
17040      * @return TreeNode The new selection
17041      */
17042     selectNext : function(){
17043         var s = this.selNode || this.lastSelNode;
17044         if(!s){
17045             return null;
17046         }
17047         if(s.firstChild && s.isExpanded()){
17048              return this.select(s.firstChild);
17049          }else if(s.nextSibling){
17050              return this.select(s.nextSibling);
17051          }else if(s.parentNode){
17052             var newS = null;
17053             s.parentNode.bubble(function(){
17054                 if(this.nextSibling){
17055                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17056                     return false;
17057                 }
17058             });
17059             return newS;
17060          }
17061         return null;
17062     },
17063
17064     onKeyDown : function(e){
17065         var s = this.selNode || this.lastSelNode;
17066         // undesirable, but required
17067         var sm = this;
17068         if(!s){
17069             return;
17070         }
17071         var k = e.getKey();
17072         switch(k){
17073              case e.DOWN:
17074                  e.stopEvent();
17075                  this.selectNext();
17076              break;
17077              case e.UP:
17078                  e.stopEvent();
17079                  this.selectPrevious();
17080              break;
17081              case e.RIGHT:
17082                  e.preventDefault();
17083                  if(s.hasChildNodes()){
17084                      if(!s.isExpanded()){
17085                          s.expand();
17086                      }else if(s.firstChild){
17087                          this.select(s.firstChild, e);
17088                      }
17089                  }
17090              break;
17091              case e.LEFT:
17092                  e.preventDefault();
17093                  if(s.hasChildNodes() && s.isExpanded()){
17094                      s.collapse();
17095                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17096                      this.select(s.parentNode, e);
17097                  }
17098              break;
17099         };
17100     }
17101 });
17102
17103 /**
17104  * @class Roo.tree.MultiSelectionModel
17105  * @extends Roo.util.Observable
17106  * Multi selection for a TreePanel.
17107  * @param {Object} cfg Configuration
17108  */
17109 Roo.tree.MultiSelectionModel = function(){
17110    this.selNodes = [];
17111    this.selMap = {};
17112    this.addEvents({
17113        /**
17114         * @event selectionchange
17115         * Fires when the selected nodes change
17116         * @param {MultiSelectionModel} this
17117         * @param {Array} nodes Array of the selected nodes
17118         */
17119        "selectionchange" : true
17120    });
17121    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17122    
17123 };
17124
17125 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17126     init : function(tree){
17127         this.tree = tree;
17128         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17129         tree.on("click", this.onNodeClick, this);
17130     },
17131     
17132     onNodeClick : function(node, e){
17133         this.select(node, e, e.ctrlKey);
17134     },
17135     
17136     /**
17137      * Select a node.
17138      * @param {TreeNode} node The node to select
17139      * @param {EventObject} e (optional) An event associated with the selection
17140      * @param {Boolean} keepExisting True to retain existing selections
17141      * @return {TreeNode} The selected node
17142      */
17143     select : function(node, e, keepExisting){
17144         if(keepExisting !== true){
17145             this.clearSelections(true);
17146         }
17147         if(this.isSelected(node)){
17148             this.lastSelNode = node;
17149             return node;
17150         }
17151         this.selNodes.push(node);
17152         this.selMap[node.id] = node;
17153         this.lastSelNode = node;
17154         node.ui.onSelectedChange(true);
17155         this.fireEvent("selectionchange", this, this.selNodes);
17156         return node;
17157     },
17158     
17159     /**
17160      * Deselect a node.
17161      * @param {TreeNode} node The node to unselect
17162      */
17163     unselect : function(node){
17164         if(this.selMap[node.id]){
17165             node.ui.onSelectedChange(false);
17166             var sn = this.selNodes;
17167             var index = -1;
17168             if(sn.indexOf){
17169                 index = sn.indexOf(node);
17170             }else{
17171                 for(var i = 0, len = sn.length; i < len; i++){
17172                     if(sn[i] == node){
17173                         index = i;
17174                         break;
17175                     }
17176                 }
17177             }
17178             if(index != -1){
17179                 this.selNodes.splice(index, 1);
17180             }
17181             delete this.selMap[node.id];
17182             this.fireEvent("selectionchange", this, this.selNodes);
17183         }
17184     },
17185     
17186     /**
17187      * Clear all selections
17188      */
17189     clearSelections : function(suppressEvent){
17190         var sn = this.selNodes;
17191         if(sn.length > 0){
17192             for(var i = 0, len = sn.length; i < len; i++){
17193                 sn[i].ui.onSelectedChange(false);
17194             }
17195             this.selNodes = [];
17196             this.selMap = {};
17197             if(suppressEvent !== true){
17198                 this.fireEvent("selectionchange", this, this.selNodes);
17199             }
17200         }
17201     },
17202     
17203     /**
17204      * Returns true if the node is selected
17205      * @param {TreeNode} node The node to check
17206      * @return {Boolean}
17207      */
17208     isSelected : function(node){
17209         return this.selMap[node.id] ? true : false;  
17210     },
17211     
17212     /**
17213      * Returns an array of the selected nodes
17214      * @return {Array}
17215      */
17216     getSelectedNodes : function(){
17217         return this.selNodes;    
17218     },
17219
17220     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17221
17222     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17223
17224     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17225 });/*
17226  * Based on:
17227  * Ext JS Library 1.1.1
17228  * Copyright(c) 2006-2007, Ext JS, LLC.
17229  *
17230  * Originally Released Under LGPL - original licence link has changed is not relivant.
17231  *
17232  * Fork - LGPL
17233  * <script type="text/javascript">
17234  */
17235  
17236 /**
17237  * @class Roo.tree.TreeNode
17238  * @extends Roo.data.Node
17239  * @cfg {String} text The text for this node
17240  * @cfg {Boolean} expanded true to start the node expanded
17241  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17242  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17243  * @cfg {Boolean} disabled true to start the node disabled
17244  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17245  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17246  * @cfg {String} cls A css class to be added to the node
17247  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17248  * @cfg {String} href URL of the link used for the node (defaults to #)
17249  * @cfg {String} hrefTarget target frame for the link
17250  * @cfg {String} qtip An Ext QuickTip for the node
17251  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17252  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17253  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17254  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17255  * (defaults to undefined with no checkbox rendered)
17256  * @constructor
17257  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17258  */
17259 Roo.tree.TreeNode = function(attributes){
17260     attributes = attributes || {};
17261     if(typeof attributes == "string"){
17262         attributes = {text: attributes};
17263     }
17264     this.childrenRendered = false;
17265     this.rendered = false;
17266     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17267     this.expanded = attributes.expanded === true;
17268     this.isTarget = attributes.isTarget !== false;
17269     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17270     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17271
17272     /**
17273      * Read-only. The text for this node. To change it use setText().
17274      * @type String
17275      */
17276     this.text = attributes.text;
17277     /**
17278      * True if this node is disabled.
17279      * @type Boolean
17280      */
17281     this.disabled = attributes.disabled === true;
17282
17283     this.addEvents({
17284         /**
17285         * @event textchange
17286         * Fires when the text for this node is changed
17287         * @param {Node} this This node
17288         * @param {String} text The new text
17289         * @param {String} oldText The old text
17290         */
17291         "textchange" : true,
17292         /**
17293         * @event beforeexpand
17294         * Fires before this node is expanded, return false to cancel.
17295         * @param {Node} this This node
17296         * @param {Boolean} deep
17297         * @param {Boolean} anim
17298         */
17299         "beforeexpand" : true,
17300         /**
17301         * @event beforecollapse
17302         * Fires before this node is collapsed, return false to cancel.
17303         * @param {Node} this This node
17304         * @param {Boolean} deep
17305         * @param {Boolean} anim
17306         */
17307         "beforecollapse" : true,
17308         /**
17309         * @event expand
17310         * Fires when this node is expanded
17311         * @param {Node} this This node
17312         */
17313         "expand" : true,
17314         /**
17315         * @event disabledchange
17316         * Fires when the disabled status of this node changes
17317         * @param {Node} this This node
17318         * @param {Boolean} disabled
17319         */
17320         "disabledchange" : true,
17321         /**
17322         * @event collapse
17323         * Fires when this node is collapsed
17324         * @param {Node} this This node
17325         */
17326         "collapse" : true,
17327         /**
17328         * @event beforeclick
17329         * Fires before click processing. Return false to cancel the default action.
17330         * @param {Node} this This node
17331         * @param {Roo.EventObject} e The event object
17332         */
17333         "beforeclick":true,
17334         /**
17335         * @event checkchange
17336         * Fires when a node with a checkbox's checked property changes
17337         * @param {Node} this This node
17338         * @param {Boolean} checked
17339         */
17340         "checkchange":true,
17341         /**
17342         * @event click
17343         * Fires when this node is clicked
17344         * @param {Node} this This node
17345         * @param {Roo.EventObject} e The event object
17346         */
17347         "click":true,
17348         /**
17349         * @event dblclick
17350         * Fires when this node is double clicked
17351         * @param {Node} this This node
17352         * @param {Roo.EventObject} e The event object
17353         */
17354         "dblclick":true,
17355         /**
17356         * @event contextmenu
17357         * Fires when this node is right clicked
17358         * @param {Node} this This node
17359         * @param {Roo.EventObject} e The event object
17360         */
17361         "contextmenu":true,
17362         /**
17363         * @event beforechildrenrendered
17364         * Fires right before the child nodes for this node are rendered
17365         * @param {Node} this This node
17366         */
17367         "beforechildrenrendered":true
17368     });
17369
17370     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17371
17372     /**
17373      * Read-only. The UI for this node
17374      * @type TreeNodeUI
17375      */
17376     this.ui = new uiClass(this);
17377     
17378     // finally support items[]
17379     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17380         return;
17381     }
17382     
17383     Roo.each(this.attributes.items, function(c) {
17384         this.appendChild(Roo.factory(c,Roo.Tree));
17385     }, this);
17386     delete this.attribute.items;
17387     
17388     
17389     
17390 };
17391 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17392     preventHScroll: true,
17393     /**
17394      * Returns true if this node is expanded
17395      * @return {Boolean}
17396      */
17397     isExpanded : function(){
17398         return this.expanded;
17399     },
17400
17401     /**
17402      * Returns the UI object for this node
17403      * @return {TreeNodeUI}
17404      */
17405     getUI : function(){
17406         return this.ui;
17407     },
17408
17409     // private override
17410     setFirstChild : function(node){
17411         var of = this.firstChild;
17412         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17413         if(this.childrenRendered && of && node != of){
17414             of.renderIndent(true, true);
17415         }
17416         if(this.rendered){
17417             this.renderIndent(true, true);
17418         }
17419     },
17420
17421     // private override
17422     setLastChild : function(node){
17423         var ol = this.lastChild;
17424         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17425         if(this.childrenRendered && ol && node != ol){
17426             ol.renderIndent(true, true);
17427         }
17428         if(this.rendered){
17429             this.renderIndent(true, true);
17430         }
17431     },
17432
17433     // these methods are overridden to provide lazy rendering support
17434     // private override
17435     appendChild : function()
17436     {
17437         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17438         if(node && this.childrenRendered){
17439             node.render();
17440         }
17441         this.ui.updateExpandIcon();
17442         return node;
17443     },
17444
17445     // private override
17446     removeChild : function(node){
17447         this.ownerTree.getSelectionModel().unselect(node);
17448         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17449         // if it's been rendered remove dom node
17450         if(this.childrenRendered){
17451             node.ui.remove();
17452         }
17453         if(this.childNodes.length < 1){
17454             this.collapse(false, false);
17455         }else{
17456             this.ui.updateExpandIcon();
17457         }
17458         if(!this.firstChild) {
17459             this.childrenRendered = false;
17460         }
17461         return node;
17462     },
17463
17464     // private override
17465     insertBefore : function(node, refNode){
17466         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17467         if(newNode && refNode && this.childrenRendered){
17468             node.render();
17469         }
17470         this.ui.updateExpandIcon();
17471         return newNode;
17472     },
17473
17474     /**
17475      * Sets the text for this node
17476      * @param {String} text
17477      */
17478     setText : function(text){
17479         var oldText = this.text;
17480         this.text = text;
17481         this.attributes.text = text;
17482         if(this.rendered){ // event without subscribing
17483             this.ui.onTextChange(this, text, oldText);
17484         }
17485         this.fireEvent("textchange", this, text, oldText);
17486     },
17487
17488     /**
17489      * Triggers selection of this node
17490      */
17491     select : function(){
17492         this.getOwnerTree().getSelectionModel().select(this);
17493     },
17494
17495     /**
17496      * Triggers deselection of this node
17497      */
17498     unselect : function(){
17499         this.getOwnerTree().getSelectionModel().unselect(this);
17500     },
17501
17502     /**
17503      * Returns true if this node is selected
17504      * @return {Boolean}
17505      */
17506     isSelected : function(){
17507         return this.getOwnerTree().getSelectionModel().isSelected(this);
17508     },
17509
17510     /**
17511      * Expand this node.
17512      * @param {Boolean} deep (optional) True to expand all children as well
17513      * @param {Boolean} anim (optional) false to cancel the default animation
17514      * @param {Function} callback (optional) A callback to be called when
17515      * expanding this node completes (does not wait for deep expand to complete).
17516      * Called with 1 parameter, this node.
17517      */
17518     expand : function(deep, anim, callback){
17519         if(!this.expanded){
17520             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17521                 return;
17522             }
17523             if(!this.childrenRendered){
17524                 this.renderChildren();
17525             }
17526             this.expanded = true;
17527             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17528                 this.ui.animExpand(function(){
17529                     this.fireEvent("expand", this);
17530                     if(typeof callback == "function"){
17531                         callback(this);
17532                     }
17533                     if(deep === true){
17534                         this.expandChildNodes(true);
17535                     }
17536                 }.createDelegate(this));
17537                 return;
17538             }else{
17539                 this.ui.expand();
17540                 this.fireEvent("expand", this);
17541                 if(typeof callback == "function"){
17542                     callback(this);
17543                 }
17544             }
17545         }else{
17546            if(typeof callback == "function"){
17547                callback(this);
17548            }
17549         }
17550         if(deep === true){
17551             this.expandChildNodes(true);
17552         }
17553     },
17554
17555     isHiddenRoot : function(){
17556         return this.isRoot && !this.getOwnerTree().rootVisible;
17557     },
17558
17559     /**
17560      * Collapse this node.
17561      * @param {Boolean} deep (optional) True to collapse all children as well
17562      * @param {Boolean} anim (optional) false to cancel the default animation
17563      */
17564     collapse : function(deep, anim){
17565         if(this.expanded && !this.isHiddenRoot()){
17566             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17567                 return;
17568             }
17569             this.expanded = false;
17570             if((this.getOwnerTree().animate && anim !== false) || anim){
17571                 this.ui.animCollapse(function(){
17572                     this.fireEvent("collapse", this);
17573                     if(deep === true){
17574                         this.collapseChildNodes(true);
17575                     }
17576                 }.createDelegate(this));
17577                 return;
17578             }else{
17579                 this.ui.collapse();
17580                 this.fireEvent("collapse", this);
17581             }
17582         }
17583         if(deep === true){
17584             var cs = this.childNodes;
17585             for(var i = 0, len = cs.length; i < len; i++) {
17586                 cs[i].collapse(true, false);
17587             }
17588         }
17589     },
17590
17591     // private
17592     delayedExpand : function(delay){
17593         if(!this.expandProcId){
17594             this.expandProcId = this.expand.defer(delay, this);
17595         }
17596     },
17597
17598     // private
17599     cancelExpand : function(){
17600         if(this.expandProcId){
17601             clearTimeout(this.expandProcId);
17602         }
17603         this.expandProcId = false;
17604     },
17605
17606     /**
17607      * Toggles expanded/collapsed state of the node
17608      */
17609     toggle : function(){
17610         if(this.expanded){
17611             this.collapse();
17612         }else{
17613             this.expand();
17614         }
17615     },
17616
17617     /**
17618      * Ensures all parent nodes are expanded
17619      */
17620     ensureVisible : function(callback){
17621         var tree = this.getOwnerTree();
17622         tree.expandPath(this.parentNode.getPath(), false, function(){
17623             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17624             Roo.callback(callback);
17625         }.createDelegate(this));
17626     },
17627
17628     /**
17629      * Expand all child nodes
17630      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17631      */
17632     expandChildNodes : function(deep){
17633         var cs = this.childNodes;
17634         for(var i = 0, len = cs.length; i < len; i++) {
17635                 cs[i].expand(deep);
17636         }
17637     },
17638
17639     /**
17640      * Collapse all child nodes
17641      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17642      */
17643     collapseChildNodes : function(deep){
17644         var cs = this.childNodes;
17645         for(var i = 0, len = cs.length; i < len; i++) {
17646                 cs[i].collapse(deep);
17647         }
17648     },
17649
17650     /**
17651      * Disables this node
17652      */
17653     disable : function(){
17654         this.disabled = true;
17655         this.unselect();
17656         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17657             this.ui.onDisableChange(this, true);
17658         }
17659         this.fireEvent("disabledchange", this, true);
17660     },
17661
17662     /**
17663      * Enables this node
17664      */
17665     enable : function(){
17666         this.disabled = false;
17667         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17668             this.ui.onDisableChange(this, false);
17669         }
17670         this.fireEvent("disabledchange", this, false);
17671     },
17672
17673     // private
17674     renderChildren : function(suppressEvent){
17675         if(suppressEvent !== false){
17676             this.fireEvent("beforechildrenrendered", this);
17677         }
17678         var cs = this.childNodes;
17679         for(var i = 0, len = cs.length; i < len; i++){
17680             cs[i].render(true);
17681         }
17682         this.childrenRendered = true;
17683     },
17684
17685     // private
17686     sort : function(fn, scope){
17687         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17688         if(this.childrenRendered){
17689             var cs = this.childNodes;
17690             for(var i = 0, len = cs.length; i < len; i++){
17691                 cs[i].render(true);
17692             }
17693         }
17694     },
17695
17696     // private
17697     render : function(bulkRender){
17698         this.ui.render(bulkRender);
17699         if(!this.rendered){
17700             this.rendered = true;
17701             if(this.expanded){
17702                 this.expanded = false;
17703                 this.expand(false, false);
17704             }
17705         }
17706     },
17707
17708     // private
17709     renderIndent : function(deep, refresh){
17710         if(refresh){
17711             this.ui.childIndent = null;
17712         }
17713         this.ui.renderIndent();
17714         if(deep === true && this.childrenRendered){
17715             var cs = this.childNodes;
17716             for(var i = 0, len = cs.length; i < len; i++){
17717                 cs[i].renderIndent(true, refresh);
17718             }
17719         }
17720     }
17721 });/*
17722  * Based on:
17723  * Ext JS Library 1.1.1
17724  * Copyright(c) 2006-2007, Ext JS, LLC.
17725  *
17726  * Originally Released Under LGPL - original licence link has changed is not relivant.
17727  *
17728  * Fork - LGPL
17729  * <script type="text/javascript">
17730  */
17731  
17732 /**
17733  * @class Roo.tree.AsyncTreeNode
17734  * @extends Roo.tree.TreeNode
17735  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17736  * @constructor
17737  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17738  */
17739  Roo.tree.AsyncTreeNode = function(config){
17740     this.loaded = false;
17741     this.loading = false;
17742     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17743     /**
17744     * @event beforeload
17745     * Fires before this node is loaded, return false to cancel
17746     * @param {Node} this This node
17747     */
17748     this.addEvents({'beforeload':true, 'load': true});
17749     /**
17750     * @event load
17751     * Fires when this node is loaded
17752     * @param {Node} this This node
17753     */
17754     /**
17755      * The loader used by this node (defaults to using the tree's defined loader)
17756      * @type TreeLoader
17757      * @property loader
17758      */
17759 };
17760 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17761     expand : function(deep, anim, callback){
17762         if(this.loading){ // if an async load is already running, waiting til it's done
17763             var timer;
17764             var f = function(){
17765                 if(!this.loading){ // done loading
17766                     clearInterval(timer);
17767                     this.expand(deep, anim, callback);
17768                 }
17769             }.createDelegate(this);
17770             timer = setInterval(f, 200);
17771             return;
17772         }
17773         if(!this.loaded){
17774             if(this.fireEvent("beforeload", this) === false){
17775                 return;
17776             }
17777             this.loading = true;
17778             this.ui.beforeLoad(this);
17779             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17780             if(loader){
17781                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17782                 return;
17783             }
17784         }
17785         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17786     },
17787     
17788     /**
17789      * Returns true if this node is currently loading
17790      * @return {Boolean}
17791      */
17792     isLoading : function(){
17793         return this.loading;  
17794     },
17795     
17796     loadComplete : function(deep, anim, callback){
17797         this.loading = false;
17798         this.loaded = true;
17799         this.ui.afterLoad(this);
17800         this.fireEvent("load", this);
17801         this.expand(deep, anim, callback);
17802     },
17803     
17804     /**
17805      * Returns true if this node has been loaded
17806      * @return {Boolean}
17807      */
17808     isLoaded : function(){
17809         return this.loaded;
17810     },
17811     
17812     hasChildNodes : function(){
17813         if(!this.isLeaf() && !this.loaded){
17814             return true;
17815         }else{
17816             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17817         }
17818     },
17819
17820     /**
17821      * Trigger a reload for this node
17822      * @param {Function} callback
17823      */
17824     reload : function(callback){
17825         this.collapse(false, false);
17826         while(this.firstChild){
17827             this.removeChild(this.firstChild);
17828         }
17829         this.childrenRendered = false;
17830         this.loaded = false;
17831         if(this.isHiddenRoot()){
17832             this.expanded = false;
17833         }
17834         this.expand(false, false, callback);
17835     }
17836 });/*
17837  * Based on:
17838  * Ext JS Library 1.1.1
17839  * Copyright(c) 2006-2007, Ext JS, LLC.
17840  *
17841  * Originally Released Under LGPL - original licence link has changed is not relivant.
17842  *
17843  * Fork - LGPL
17844  * <script type="text/javascript">
17845  */
17846  
17847 /**
17848  * @class Roo.tree.TreeNodeUI
17849  * @constructor
17850  * @param {Object} node The node to render
17851  * The TreeNode UI implementation is separate from the
17852  * tree implementation. Unless you are customizing the tree UI,
17853  * you should never have to use this directly.
17854  */
17855 Roo.tree.TreeNodeUI = function(node){
17856     this.node = node;
17857     this.rendered = false;
17858     this.animating = false;
17859     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17860 };
17861
17862 Roo.tree.TreeNodeUI.prototype = {
17863     removeChild : function(node){
17864         if(this.rendered){
17865             this.ctNode.removeChild(node.ui.getEl());
17866         }
17867     },
17868
17869     beforeLoad : function(){
17870          this.addClass("x-tree-node-loading");
17871     },
17872
17873     afterLoad : function(){
17874          this.removeClass("x-tree-node-loading");
17875     },
17876
17877     onTextChange : function(node, text, oldText){
17878         if(this.rendered){
17879             this.textNode.innerHTML = text;
17880         }
17881     },
17882
17883     onDisableChange : function(node, state){
17884         this.disabled = state;
17885         if(state){
17886             this.addClass("x-tree-node-disabled");
17887         }else{
17888             this.removeClass("x-tree-node-disabled");
17889         }
17890     },
17891
17892     onSelectedChange : function(state){
17893         if(state){
17894             this.focus();
17895             this.addClass("x-tree-selected");
17896         }else{
17897             //this.blur();
17898             this.removeClass("x-tree-selected");
17899         }
17900     },
17901
17902     onMove : function(tree, node, oldParent, newParent, index, refNode){
17903         this.childIndent = null;
17904         if(this.rendered){
17905             var targetNode = newParent.ui.getContainer();
17906             if(!targetNode){//target not rendered
17907                 this.holder = document.createElement("div");
17908                 this.holder.appendChild(this.wrap);
17909                 return;
17910             }
17911             var insertBefore = refNode ? refNode.ui.getEl() : null;
17912             if(insertBefore){
17913                 targetNode.insertBefore(this.wrap, insertBefore);
17914             }else{
17915                 targetNode.appendChild(this.wrap);
17916             }
17917             this.node.renderIndent(true);
17918         }
17919     },
17920
17921     addClass : function(cls){
17922         if(this.elNode){
17923             Roo.fly(this.elNode).addClass(cls);
17924         }
17925     },
17926
17927     removeClass : function(cls){
17928         if(this.elNode){
17929             Roo.fly(this.elNode).removeClass(cls);
17930         }
17931     },
17932
17933     remove : function(){
17934         if(this.rendered){
17935             this.holder = document.createElement("div");
17936             this.holder.appendChild(this.wrap);
17937         }
17938     },
17939
17940     fireEvent : function(){
17941         return this.node.fireEvent.apply(this.node, arguments);
17942     },
17943
17944     initEvents : function(){
17945         this.node.on("move", this.onMove, this);
17946         var E = Roo.EventManager;
17947         var a = this.anchor;
17948
17949         var el = Roo.fly(a, '_treeui');
17950
17951         if(Roo.isOpera){ // opera render bug ignores the CSS
17952             el.setStyle("text-decoration", "none");
17953         }
17954
17955         el.on("click", this.onClick, this);
17956         el.on("dblclick", this.onDblClick, this);
17957
17958         if(this.checkbox){
17959             Roo.EventManager.on(this.checkbox,
17960                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17961         }
17962
17963         el.on("contextmenu", this.onContextMenu, this);
17964
17965         var icon = Roo.fly(this.iconNode);
17966         icon.on("click", this.onClick, this);
17967         icon.on("dblclick", this.onDblClick, this);
17968         icon.on("contextmenu", this.onContextMenu, this);
17969         E.on(this.ecNode, "click", this.ecClick, this, true);
17970
17971         if(this.node.disabled){
17972             this.addClass("x-tree-node-disabled");
17973         }
17974         if(this.node.hidden){
17975             this.addClass("x-tree-node-disabled");
17976         }
17977         var ot = this.node.getOwnerTree();
17978         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17979         if(dd && (!this.node.isRoot || ot.rootVisible)){
17980             Roo.dd.Registry.register(this.elNode, {
17981                 node: this.node,
17982                 handles: this.getDDHandles(),
17983                 isHandle: false
17984             });
17985         }
17986     },
17987
17988     getDDHandles : function(){
17989         return [this.iconNode, this.textNode];
17990     },
17991
17992     hide : function(){
17993         if(this.rendered){
17994             this.wrap.style.display = "none";
17995         }
17996     },
17997
17998     show : function(){
17999         if(this.rendered){
18000             this.wrap.style.display = "";
18001         }
18002     },
18003
18004     onContextMenu : function(e){
18005         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18006             e.preventDefault();
18007             this.focus();
18008             this.fireEvent("contextmenu", this.node, e);
18009         }
18010     },
18011
18012     onClick : function(e){
18013         if(this.dropping){
18014             e.stopEvent();
18015             return;
18016         }
18017         if(this.fireEvent("beforeclick", this.node, e) !== false){
18018             if(!this.disabled && this.node.attributes.href){
18019                 this.fireEvent("click", this.node, e);
18020                 return;
18021             }
18022             e.preventDefault();
18023             if(this.disabled){
18024                 return;
18025             }
18026
18027             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18028                 this.node.toggle();
18029             }
18030
18031             this.fireEvent("click", this.node, e);
18032         }else{
18033             e.stopEvent();
18034         }
18035     },
18036
18037     onDblClick : function(e){
18038         e.preventDefault();
18039         if(this.disabled){
18040             return;
18041         }
18042         if(this.checkbox){
18043             this.toggleCheck();
18044         }
18045         if(!this.animating && this.node.hasChildNodes()){
18046             this.node.toggle();
18047         }
18048         this.fireEvent("dblclick", this.node, e);
18049     },
18050
18051     onCheckChange : function(){
18052         var checked = this.checkbox.checked;
18053         this.node.attributes.checked = checked;
18054         this.fireEvent('checkchange', this.node, checked);
18055     },
18056
18057     ecClick : function(e){
18058         if(!this.animating && this.node.hasChildNodes()){
18059             this.node.toggle();
18060         }
18061     },
18062
18063     startDrop : function(){
18064         this.dropping = true;
18065     },
18066
18067     // delayed drop so the click event doesn't get fired on a drop
18068     endDrop : function(){
18069        setTimeout(function(){
18070            this.dropping = false;
18071        }.createDelegate(this), 50);
18072     },
18073
18074     expand : function(){
18075         this.updateExpandIcon();
18076         this.ctNode.style.display = "";
18077     },
18078
18079     focus : function(){
18080         if(!this.node.preventHScroll){
18081             try{this.anchor.focus();
18082             }catch(e){}
18083         }else if(!Roo.isIE){
18084             try{
18085                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18086                 var l = noscroll.scrollLeft;
18087                 this.anchor.focus();
18088                 noscroll.scrollLeft = l;
18089             }catch(e){}
18090         }
18091     },
18092
18093     toggleCheck : function(value){
18094         var cb = this.checkbox;
18095         if(cb){
18096             cb.checked = (value === undefined ? !cb.checked : value);
18097         }
18098     },
18099
18100     blur : function(){
18101         try{
18102             this.anchor.blur();
18103         }catch(e){}
18104     },
18105
18106     animExpand : function(callback){
18107         var ct = Roo.get(this.ctNode);
18108         ct.stopFx();
18109         if(!this.node.hasChildNodes()){
18110             this.updateExpandIcon();
18111             this.ctNode.style.display = "";
18112             Roo.callback(callback);
18113             return;
18114         }
18115         this.animating = true;
18116         this.updateExpandIcon();
18117
18118         ct.slideIn('t', {
18119            callback : function(){
18120                this.animating = false;
18121                Roo.callback(callback);
18122             },
18123             scope: this,
18124             duration: this.node.ownerTree.duration || .25
18125         });
18126     },
18127
18128     highlight : function(){
18129         var tree = this.node.getOwnerTree();
18130         Roo.fly(this.wrap).highlight(
18131             tree.hlColor || "C3DAF9",
18132             {endColor: tree.hlBaseColor}
18133         );
18134     },
18135
18136     collapse : function(){
18137         this.updateExpandIcon();
18138         this.ctNode.style.display = "none";
18139     },
18140
18141     animCollapse : function(callback){
18142         var ct = Roo.get(this.ctNode);
18143         ct.enableDisplayMode('block');
18144         ct.stopFx();
18145
18146         this.animating = true;
18147         this.updateExpandIcon();
18148
18149         ct.slideOut('t', {
18150             callback : function(){
18151                this.animating = false;
18152                Roo.callback(callback);
18153             },
18154             scope: this,
18155             duration: this.node.ownerTree.duration || .25
18156         });
18157     },
18158
18159     getContainer : function(){
18160         return this.ctNode;
18161     },
18162
18163     getEl : function(){
18164         return this.wrap;
18165     },
18166
18167     appendDDGhost : function(ghostNode){
18168         ghostNode.appendChild(this.elNode.cloneNode(true));
18169     },
18170
18171     getDDRepairXY : function(){
18172         return Roo.lib.Dom.getXY(this.iconNode);
18173     },
18174
18175     onRender : function(){
18176         this.render();
18177     },
18178
18179     render : function(bulkRender){
18180         var n = this.node, a = n.attributes;
18181         var targetNode = n.parentNode ?
18182               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18183
18184         if(!this.rendered){
18185             this.rendered = true;
18186
18187             this.renderElements(n, a, targetNode, bulkRender);
18188
18189             if(a.qtip){
18190                if(this.textNode.setAttributeNS){
18191                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18192                    if(a.qtipTitle){
18193                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18194                    }
18195                }else{
18196                    this.textNode.setAttribute("ext:qtip", a.qtip);
18197                    if(a.qtipTitle){
18198                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18199                    }
18200                }
18201             }else if(a.qtipCfg){
18202                 a.qtipCfg.target = Roo.id(this.textNode);
18203                 Roo.QuickTips.register(a.qtipCfg);
18204             }
18205             this.initEvents();
18206             if(!this.node.expanded){
18207                 this.updateExpandIcon();
18208             }
18209         }else{
18210             if(bulkRender === true) {
18211                 targetNode.appendChild(this.wrap);
18212             }
18213         }
18214     },
18215
18216     renderElements : function(n, a, targetNode, bulkRender)
18217     {
18218         // add some indent caching, this helps performance when rendering a large tree
18219         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18220         var t = n.getOwnerTree();
18221         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18222         if (typeof(n.attributes.html) != 'undefined') {
18223             txt = n.attributes.html;
18224         }
18225         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18226         var cb = typeof a.checked == 'boolean';
18227         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18228         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18229             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18230             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18231             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18232             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18233             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18234              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18235                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18236             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18237             "</li>"];
18238
18239         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18240             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18241                                 n.nextSibling.ui.getEl(), buf.join(""));
18242         }else{
18243             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18244         }
18245
18246         this.elNode = this.wrap.childNodes[0];
18247         this.ctNode = this.wrap.childNodes[1];
18248         var cs = this.elNode.childNodes;
18249         this.indentNode = cs[0];
18250         this.ecNode = cs[1];
18251         this.iconNode = cs[2];
18252         var index = 3;
18253         if(cb){
18254             this.checkbox = cs[3];
18255             index++;
18256         }
18257         this.anchor = cs[index];
18258         this.textNode = cs[index].firstChild;
18259     },
18260
18261     getAnchor : function(){
18262         return this.anchor;
18263     },
18264
18265     getTextEl : function(){
18266         return this.textNode;
18267     },
18268
18269     getIconEl : function(){
18270         return this.iconNode;
18271     },
18272
18273     isChecked : function(){
18274         return this.checkbox ? this.checkbox.checked : false;
18275     },
18276
18277     updateExpandIcon : function(){
18278         if(this.rendered){
18279             var n = this.node, c1, c2;
18280             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18281             var hasChild = n.hasChildNodes();
18282             if(hasChild){
18283                 if(n.expanded){
18284                     cls += "-minus";
18285                     c1 = "x-tree-node-collapsed";
18286                     c2 = "x-tree-node-expanded";
18287                 }else{
18288                     cls += "-plus";
18289                     c1 = "x-tree-node-expanded";
18290                     c2 = "x-tree-node-collapsed";
18291                 }
18292                 if(this.wasLeaf){
18293                     this.removeClass("x-tree-node-leaf");
18294                     this.wasLeaf = false;
18295                 }
18296                 if(this.c1 != c1 || this.c2 != c2){
18297                     Roo.fly(this.elNode).replaceClass(c1, c2);
18298                     this.c1 = c1; this.c2 = c2;
18299                 }
18300             }else{
18301                 // this changes non-leafs into leafs if they have no children.
18302                 // it's not very rational behaviour..
18303                 
18304                 if(!this.wasLeaf && this.node.leaf){
18305                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18306                     delete this.c1;
18307                     delete this.c2;
18308                     this.wasLeaf = true;
18309                 }
18310             }
18311             var ecc = "x-tree-ec-icon "+cls;
18312             if(this.ecc != ecc){
18313                 this.ecNode.className = ecc;
18314                 this.ecc = ecc;
18315             }
18316         }
18317     },
18318
18319     getChildIndent : function(){
18320         if(!this.childIndent){
18321             var buf = [];
18322             var p = this.node;
18323             while(p){
18324                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18325                     if(!p.isLast()) {
18326                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18327                     } else {
18328                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18329                     }
18330                 }
18331                 p = p.parentNode;
18332             }
18333             this.childIndent = buf.join("");
18334         }
18335         return this.childIndent;
18336     },
18337
18338     renderIndent : function(){
18339         if(this.rendered){
18340             var indent = "";
18341             var p = this.node.parentNode;
18342             if(p){
18343                 indent = p.ui.getChildIndent();
18344             }
18345             if(this.indentMarkup != indent){ // don't rerender if not required
18346                 this.indentNode.innerHTML = indent;
18347                 this.indentMarkup = indent;
18348             }
18349             this.updateExpandIcon();
18350         }
18351     }
18352 };
18353
18354 Roo.tree.RootTreeNodeUI = function(){
18355     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18356 };
18357 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18358     render : function(){
18359         if(!this.rendered){
18360             var targetNode = this.node.ownerTree.innerCt.dom;
18361             this.node.expanded = true;
18362             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18363             this.wrap = this.ctNode = targetNode.firstChild;
18364         }
18365     },
18366     collapse : function(){
18367     },
18368     expand : function(){
18369     }
18370 });/*
18371  * Based on:
18372  * Ext JS Library 1.1.1
18373  * Copyright(c) 2006-2007, Ext JS, LLC.
18374  *
18375  * Originally Released Under LGPL - original licence link has changed is not relivant.
18376  *
18377  * Fork - LGPL
18378  * <script type="text/javascript">
18379  */
18380 /**
18381  * @class Roo.tree.TreeLoader
18382  * @extends Roo.util.Observable
18383  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18384  * nodes from a specified URL. The response must be a javascript Array definition
18385  * who's elements are node definition objects. eg:
18386  * <pre><code>
18387    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18388     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18389 </code></pre>
18390  * <br><br>
18391  * A server request is sent, and child nodes are loaded only when a node is expanded.
18392  * The loading node's id is passed to the server under the parameter name "node" to
18393  * enable the server to produce the correct child nodes.
18394  * <br><br>
18395  * To pass extra parameters, an event handler may be attached to the "beforeload"
18396  * event, and the parameters specified in the TreeLoader's baseParams property:
18397  * <pre><code>
18398     myTreeLoader.on("beforeload", function(treeLoader, node) {
18399         this.baseParams.category = node.attributes.category;
18400     }, this);
18401 </code></pre><
18402  * This would pass an HTTP parameter called "category" to the server containing
18403  * the value of the Node's "category" attribute.
18404  * @constructor
18405  * Creates a new Treeloader.
18406  * @param {Object} config A config object containing config properties.
18407  */
18408 Roo.tree.TreeLoader = function(config){
18409     this.baseParams = {};
18410     this.requestMethod = "POST";
18411     Roo.apply(this, config);
18412
18413     this.addEvents({
18414     
18415         /**
18416          * @event beforeload
18417          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18418          * @param {Object} This TreeLoader object.
18419          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18420          * @param {Object} callback The callback function specified in the {@link #load} call.
18421          */
18422         beforeload : true,
18423         /**
18424          * @event load
18425          * Fires when the node has been successfuly loaded.
18426          * @param {Object} This TreeLoader object.
18427          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18428          * @param {Object} response The response object containing the data from the server.
18429          */
18430         load : true,
18431         /**
18432          * @event loadexception
18433          * Fires if the network request failed.
18434          * @param {Object} This TreeLoader object.
18435          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18436          * @param {Object} response The response object containing the data from the server.
18437          */
18438         loadexception : true,
18439         /**
18440          * @event create
18441          * Fires before a node is created, enabling you to return custom Node types 
18442          * @param {Object} This TreeLoader object.
18443          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18444          */
18445         create : true
18446     });
18447
18448     Roo.tree.TreeLoader.superclass.constructor.call(this);
18449 };
18450
18451 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18452     /**
18453     * @cfg {String} dataUrl The URL from which to request a Json string which
18454     * specifies an array of node definition object representing the child nodes
18455     * to be loaded.
18456     */
18457     /**
18458     * @cfg {Object} baseParams (optional) An object containing properties which
18459     * specify HTTP parameters to be passed to each request for child nodes.
18460     */
18461     /**
18462     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18463     * created by this loader. If the attributes sent by the server have an attribute in this object,
18464     * they take priority.
18465     */
18466     /**
18467     * @cfg {Object} uiProviders (optional) An object containing properties which
18468     * 
18469     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18470     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18471     * <i>uiProvider</i> attribute of a returned child node is a string rather
18472     * than a reference to a TreeNodeUI implementation, this that string value
18473     * is used as a property name in the uiProviders object. You can define the provider named
18474     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18475     */
18476     uiProviders : {},
18477
18478     /**
18479     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18480     * child nodes before loading.
18481     */
18482     clearOnLoad : true,
18483
18484     /**
18485     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18486     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18487     * Grid query { data : [ .....] }
18488     */
18489     
18490     root : false,
18491      /**
18492     * @cfg {String} queryParam (optional) 
18493     * Name of the query as it will be passed on the querystring (defaults to 'node')
18494     * eg. the request will be ?node=[id]
18495     */
18496     
18497     
18498     queryParam: false,
18499     
18500     /**
18501      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18502      * This is called automatically when a node is expanded, but may be used to reload
18503      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18504      * @param {Roo.tree.TreeNode} node
18505      * @param {Function} callback
18506      */
18507     load : function(node, callback){
18508         if(this.clearOnLoad){
18509             while(node.firstChild){
18510                 node.removeChild(node.firstChild);
18511             }
18512         }
18513         if(node.attributes.children){ // preloaded json children
18514             var cs = node.attributes.children;
18515             for(var i = 0, len = cs.length; i < len; i++){
18516                 node.appendChild(this.createNode(cs[i]));
18517             }
18518             if(typeof callback == "function"){
18519                 callback();
18520             }
18521         }else if(this.dataUrl){
18522             this.requestData(node, callback);
18523         }
18524     },
18525
18526     getParams: function(node){
18527         var buf = [], bp = this.baseParams;
18528         for(var key in bp){
18529             if(typeof bp[key] != "function"){
18530                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18531             }
18532         }
18533         var n = this.queryParam === false ? 'node' : this.queryParam;
18534         buf.push(n + "=", encodeURIComponent(node.id));
18535         return buf.join("");
18536     },
18537
18538     requestData : function(node, callback){
18539         if(this.fireEvent("beforeload", this, node, callback) !== false){
18540             this.transId = Roo.Ajax.request({
18541                 method:this.requestMethod,
18542                 url: this.dataUrl||this.url,
18543                 success: this.handleResponse,
18544                 failure: this.handleFailure,
18545                 scope: this,
18546                 argument: {callback: callback, node: node},
18547                 params: this.getParams(node)
18548             });
18549         }else{
18550             // if the load is cancelled, make sure we notify
18551             // the node that we are done
18552             if(typeof callback == "function"){
18553                 callback();
18554             }
18555         }
18556     },
18557
18558     isLoading : function(){
18559         return this.transId ? true : false;
18560     },
18561
18562     abort : function(){
18563         if(this.isLoading()){
18564             Roo.Ajax.abort(this.transId);
18565         }
18566     },
18567
18568     // private
18569     createNode : function(attr)
18570     {
18571         // apply baseAttrs, nice idea Corey!
18572         if(this.baseAttrs){
18573             Roo.applyIf(attr, this.baseAttrs);
18574         }
18575         if(this.applyLoader !== false){
18576             attr.loader = this;
18577         }
18578         // uiProvider = depreciated..
18579         
18580         if(typeof(attr.uiProvider) == 'string'){
18581            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18582                 /**  eval:var:attr */ eval(attr.uiProvider);
18583         }
18584         if(typeof(this.uiProviders['default']) != 'undefined') {
18585             attr.uiProvider = this.uiProviders['default'];
18586         }
18587         
18588         this.fireEvent('create', this, attr);
18589         
18590         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18591         return(attr.leaf ?
18592                         new Roo.tree.TreeNode(attr) :
18593                         new Roo.tree.AsyncTreeNode(attr));
18594     },
18595
18596     processResponse : function(response, node, callback)
18597     {
18598         var json = response.responseText;
18599         try {
18600             
18601             var o = Roo.decode(json);
18602             
18603             if (!o.success) {
18604                 // it's a failure condition.
18605                 var a = response.argument;
18606                 this.fireEvent("loadexception", this, a.node, response);
18607                 Roo.log("Load failed - should have a handler really");
18608                 return;
18609             }
18610             
18611             if (this.root !== false) {
18612                 o = o[this.root];
18613             }
18614             
18615             for(var i = 0, len = o.length; i < len; i++){
18616                 var n = this.createNode(o[i]);
18617                 if(n){
18618                     node.appendChild(n);
18619                 }
18620             }
18621             if(typeof callback == "function"){
18622                 callback(this, node);
18623             }
18624         }catch(e){
18625             this.handleFailure(response);
18626         }
18627     },
18628
18629     handleResponse : function(response){
18630         this.transId = false;
18631         var a = response.argument;
18632         this.processResponse(response, a.node, a.callback);
18633         this.fireEvent("load", this, a.node, response);
18634     },
18635
18636     handleFailure : function(response)
18637     {
18638         // should handle failure better..
18639         this.transId = false;
18640         var a = response.argument;
18641         this.fireEvent("loadexception", this, a.node, response);
18642         if(typeof a.callback == "function"){
18643             a.callback(this, a.node);
18644         }
18645     }
18646 });/*
18647  * Based on:
18648  * Ext JS Library 1.1.1
18649  * Copyright(c) 2006-2007, Ext JS, LLC.
18650  *
18651  * Originally Released Under LGPL - original licence link has changed is not relivant.
18652  *
18653  * Fork - LGPL
18654  * <script type="text/javascript">
18655  */
18656
18657 /**
18658 * @class Roo.tree.TreeFilter
18659 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18660 * @param {TreePanel} tree
18661 * @param {Object} config (optional)
18662  */
18663 Roo.tree.TreeFilter = function(tree, config){
18664     this.tree = tree;
18665     this.filtered = {};
18666     Roo.apply(this, config);
18667 };
18668
18669 Roo.tree.TreeFilter.prototype = {
18670     clearBlank:false,
18671     reverse:false,
18672     autoClear:false,
18673     remove:false,
18674
18675      /**
18676      * Filter the data by a specific attribute.
18677      * @param {String/RegExp} value Either string that the attribute value
18678      * should start with or a RegExp to test against the attribute
18679      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18680      * @param {TreeNode} startNode (optional) The node to start the filter at.
18681      */
18682     filter : function(value, attr, startNode){
18683         attr = attr || "text";
18684         var f;
18685         if(typeof value == "string"){
18686             var vlen = value.length;
18687             // auto clear empty filter
18688             if(vlen == 0 && this.clearBlank){
18689                 this.clear();
18690                 return;
18691             }
18692             value = value.toLowerCase();
18693             f = function(n){
18694                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18695             };
18696         }else if(value.exec){ // regex?
18697             f = function(n){
18698                 return value.test(n.attributes[attr]);
18699             };
18700         }else{
18701             throw 'Illegal filter type, must be string or regex';
18702         }
18703         this.filterBy(f, null, startNode);
18704         },
18705
18706     /**
18707      * Filter by a function. The passed function will be called with each
18708      * node in the tree (or from the startNode). If the function returns true, the node is kept
18709      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18710      * @param {Function} fn The filter function
18711      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18712      */
18713     filterBy : function(fn, scope, startNode){
18714         startNode = startNode || this.tree.root;
18715         if(this.autoClear){
18716             this.clear();
18717         }
18718         var af = this.filtered, rv = this.reverse;
18719         var f = function(n){
18720             if(n == startNode){
18721                 return true;
18722             }
18723             if(af[n.id]){
18724                 return false;
18725             }
18726             var m = fn.call(scope || n, n);
18727             if(!m || rv){
18728                 af[n.id] = n;
18729                 n.ui.hide();
18730                 return false;
18731             }
18732             return true;
18733         };
18734         startNode.cascade(f);
18735         if(this.remove){
18736            for(var id in af){
18737                if(typeof id != "function"){
18738                    var n = af[id];
18739                    if(n && n.parentNode){
18740                        n.parentNode.removeChild(n);
18741                    }
18742                }
18743            }
18744         }
18745     },
18746
18747     /**
18748      * Clears the current filter. Note: with the "remove" option
18749      * set a filter cannot be cleared.
18750      */
18751     clear : function(){
18752         var t = this.tree;
18753         var af = this.filtered;
18754         for(var id in af){
18755             if(typeof id != "function"){
18756                 var n = af[id];
18757                 if(n){
18758                     n.ui.show();
18759                 }
18760             }
18761         }
18762         this.filtered = {};
18763     }
18764 };
18765 /*
18766  * Based on:
18767  * Ext JS Library 1.1.1
18768  * Copyright(c) 2006-2007, Ext JS, LLC.
18769  *
18770  * Originally Released Under LGPL - original licence link has changed is not relivant.
18771  *
18772  * Fork - LGPL
18773  * <script type="text/javascript">
18774  */
18775  
18776
18777 /**
18778  * @class Roo.tree.TreeSorter
18779  * Provides sorting of nodes in a TreePanel
18780  * 
18781  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18782  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18783  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18784  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18785  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18786  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18787  * @constructor
18788  * @param {TreePanel} tree
18789  * @param {Object} config
18790  */
18791 Roo.tree.TreeSorter = function(tree, config){
18792     Roo.apply(this, config);
18793     tree.on("beforechildrenrendered", this.doSort, this);
18794     tree.on("append", this.updateSort, this);
18795     tree.on("insert", this.updateSort, this);
18796     
18797     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18798     var p = this.property || "text";
18799     var sortType = this.sortType;
18800     var fs = this.folderSort;
18801     var cs = this.caseSensitive === true;
18802     var leafAttr = this.leafAttr || 'leaf';
18803
18804     this.sortFn = function(n1, n2){
18805         if(fs){
18806             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18807                 return 1;
18808             }
18809             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18810                 return -1;
18811             }
18812         }
18813         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18814         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18815         if(v1 < v2){
18816                         return dsc ? +1 : -1;
18817                 }else if(v1 > v2){
18818                         return dsc ? -1 : +1;
18819         }else{
18820                 return 0;
18821         }
18822     };
18823 };
18824
18825 Roo.tree.TreeSorter.prototype = {
18826     doSort : function(node){
18827         node.sort(this.sortFn);
18828     },
18829     
18830     compareNodes : function(n1, n2){
18831         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18832     },
18833     
18834     updateSort : function(tree, node){
18835         if(node.childrenRendered){
18836             this.doSort.defer(1, this, [node]);
18837         }
18838     }
18839 };/*
18840  * Based on:
18841  * Ext JS Library 1.1.1
18842  * Copyright(c) 2006-2007, Ext JS, LLC.
18843  *
18844  * Originally Released Under LGPL - original licence link has changed is not relivant.
18845  *
18846  * Fork - LGPL
18847  * <script type="text/javascript">
18848  */
18849
18850 if(Roo.dd.DropZone){
18851     
18852 Roo.tree.TreeDropZone = function(tree, config){
18853     this.allowParentInsert = false;
18854     this.allowContainerDrop = false;
18855     this.appendOnly = false;
18856     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18857     this.tree = tree;
18858     this.lastInsertClass = "x-tree-no-status";
18859     this.dragOverData = {};
18860 };
18861
18862 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18863     ddGroup : "TreeDD",
18864     
18865     expandDelay : 1000,
18866     
18867     expandNode : function(node){
18868         if(node.hasChildNodes() && !node.isExpanded()){
18869             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18870         }
18871     },
18872     
18873     queueExpand : function(node){
18874         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18875     },
18876     
18877     cancelExpand : function(){
18878         if(this.expandProcId){
18879             clearTimeout(this.expandProcId);
18880             this.expandProcId = false;
18881         }
18882     },
18883     
18884     isValidDropPoint : function(n, pt, dd, e, data){
18885         if(!n || !data){ return false; }
18886         var targetNode = n.node;
18887         var dropNode = data.node;
18888         // default drop rules
18889         if(!(targetNode && targetNode.isTarget && pt)){
18890             return false;
18891         }
18892         if(pt == "append" && targetNode.allowChildren === false){
18893             return false;
18894         }
18895         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18896             return false;
18897         }
18898         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18899             return false;
18900         }
18901         // reuse the object
18902         var overEvent = this.dragOverData;
18903         overEvent.tree = this.tree;
18904         overEvent.target = targetNode;
18905         overEvent.data = data;
18906         overEvent.point = pt;
18907         overEvent.source = dd;
18908         overEvent.rawEvent = e;
18909         overEvent.dropNode = dropNode;
18910         overEvent.cancel = false;  
18911         var result = this.tree.fireEvent("nodedragover", overEvent);
18912         return overEvent.cancel === false && result !== false;
18913     },
18914     
18915     getDropPoint : function(e, n, dd){
18916         var tn = n.node;
18917         if(tn.isRoot){
18918             return tn.allowChildren !== false ? "append" : false; // always append for root
18919         }
18920         var dragEl = n.ddel;
18921         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18922         var y = Roo.lib.Event.getPageY(e);
18923         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18924         
18925         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18926         var noAppend = tn.allowChildren === false;
18927         if(this.appendOnly || tn.parentNode.allowChildren === false){
18928             return noAppend ? false : "append";
18929         }
18930         var noBelow = false;
18931         if(!this.allowParentInsert){
18932             noBelow = tn.hasChildNodes() && tn.isExpanded();
18933         }
18934         var q = (b - t) / (noAppend ? 2 : 3);
18935         if(y >= t && y < (t + q)){
18936             return "above";
18937         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18938             return "below";
18939         }else{
18940             return "append";
18941         }
18942     },
18943     
18944     onNodeEnter : function(n, dd, e, data){
18945         this.cancelExpand();
18946     },
18947     
18948     onNodeOver : function(n, dd, e, data){
18949         var pt = this.getDropPoint(e, n, dd);
18950         var node = n.node;
18951         
18952         // auto node expand check
18953         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18954             this.queueExpand(node);
18955         }else if(pt != "append"){
18956             this.cancelExpand();
18957         }
18958         
18959         // set the insert point style on the target node
18960         var returnCls = this.dropNotAllowed;
18961         if(this.isValidDropPoint(n, pt, dd, e, data)){
18962            if(pt){
18963                var el = n.ddel;
18964                var cls;
18965                if(pt == "above"){
18966                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18967                    cls = "x-tree-drag-insert-above";
18968                }else if(pt == "below"){
18969                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18970                    cls = "x-tree-drag-insert-below";
18971                }else{
18972                    returnCls = "x-tree-drop-ok-append";
18973                    cls = "x-tree-drag-append";
18974                }
18975                if(this.lastInsertClass != cls){
18976                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18977                    this.lastInsertClass = cls;
18978                }
18979            }
18980        }
18981        return returnCls;
18982     },
18983     
18984     onNodeOut : function(n, dd, e, data){
18985         this.cancelExpand();
18986         this.removeDropIndicators(n);
18987     },
18988     
18989     onNodeDrop : function(n, dd, e, data){
18990         var point = this.getDropPoint(e, n, dd);
18991         var targetNode = n.node;
18992         targetNode.ui.startDrop();
18993         if(!this.isValidDropPoint(n, point, dd, e, data)){
18994             targetNode.ui.endDrop();
18995             return false;
18996         }
18997         // first try to find the drop node
18998         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18999         var dropEvent = {
19000             tree : this.tree,
19001             target: targetNode,
19002             data: data,
19003             point: point,
19004             source: dd,
19005             rawEvent: e,
19006             dropNode: dropNode,
19007             cancel: !dropNode   
19008         };
19009         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19010         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19011             targetNode.ui.endDrop();
19012             return false;
19013         }
19014         // allow target changing
19015         targetNode = dropEvent.target;
19016         if(point == "append" && !targetNode.isExpanded()){
19017             targetNode.expand(false, null, function(){
19018                 this.completeDrop(dropEvent);
19019             }.createDelegate(this));
19020         }else{
19021             this.completeDrop(dropEvent);
19022         }
19023         return true;
19024     },
19025     
19026     completeDrop : function(de){
19027         var ns = de.dropNode, p = de.point, t = de.target;
19028         if(!(ns instanceof Array)){
19029             ns = [ns];
19030         }
19031         var n;
19032         for(var i = 0, len = ns.length; i < len; i++){
19033             n = ns[i];
19034             if(p == "above"){
19035                 t.parentNode.insertBefore(n, t);
19036             }else if(p == "below"){
19037                 t.parentNode.insertBefore(n, t.nextSibling);
19038             }else{
19039                 t.appendChild(n);
19040             }
19041         }
19042         n.ui.focus();
19043         if(this.tree.hlDrop){
19044             n.ui.highlight();
19045         }
19046         t.ui.endDrop();
19047         this.tree.fireEvent("nodedrop", de);
19048     },
19049     
19050     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19051         if(this.tree.hlDrop){
19052             dropNode.ui.focus();
19053             dropNode.ui.highlight();
19054         }
19055         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19056     },
19057     
19058     getTree : function(){
19059         return this.tree;
19060     },
19061     
19062     removeDropIndicators : function(n){
19063         if(n && n.ddel){
19064             var el = n.ddel;
19065             Roo.fly(el).removeClass([
19066                     "x-tree-drag-insert-above",
19067                     "x-tree-drag-insert-below",
19068                     "x-tree-drag-append"]);
19069             this.lastInsertClass = "_noclass";
19070         }
19071     },
19072     
19073     beforeDragDrop : function(target, e, id){
19074         this.cancelExpand();
19075         return true;
19076     },
19077     
19078     afterRepair : function(data){
19079         if(data && Roo.enableFx){
19080             data.node.ui.highlight();
19081         }
19082         this.hideProxy();
19083     }    
19084 });
19085
19086 }
19087 /*
19088  * Based on:
19089  * Ext JS Library 1.1.1
19090  * Copyright(c) 2006-2007, Ext JS, LLC.
19091  *
19092  * Originally Released Under LGPL - original licence link has changed is not relivant.
19093  *
19094  * Fork - LGPL
19095  * <script type="text/javascript">
19096  */
19097  
19098
19099 if(Roo.dd.DragZone){
19100 Roo.tree.TreeDragZone = function(tree, config){
19101     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19102     this.tree = tree;
19103 };
19104
19105 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19106     ddGroup : "TreeDD",
19107     
19108     onBeforeDrag : function(data, e){
19109         var n = data.node;
19110         return n && n.draggable && !n.disabled;
19111     },
19112     
19113     onInitDrag : function(e){
19114         var data = this.dragData;
19115         this.tree.getSelectionModel().select(data.node);
19116         this.proxy.update("");
19117         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19118         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19119     },
19120     
19121     getRepairXY : function(e, data){
19122         return data.node.ui.getDDRepairXY();
19123     },
19124     
19125     onEndDrag : function(data, e){
19126         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19127     },
19128     
19129     onValidDrop : function(dd, e, id){
19130         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19131         this.hideProxy();
19132     },
19133     
19134     beforeInvalidDrop : function(e, id){
19135         // this scrolls the original position back into view
19136         var sm = this.tree.getSelectionModel();
19137         sm.clearSelections();
19138         sm.select(this.dragData.node);
19139     }
19140 });
19141 }/*
19142  * Based on:
19143  * Ext JS Library 1.1.1
19144  * Copyright(c) 2006-2007, Ext JS, LLC.
19145  *
19146  * Originally Released Under LGPL - original licence link has changed is not relivant.
19147  *
19148  * Fork - LGPL
19149  * <script type="text/javascript">
19150  */
19151 /**
19152  * @class Roo.tree.TreeEditor
19153  * @extends Roo.Editor
19154  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19155  * as the editor field.
19156  * @constructor
19157  * @param {Object} config (used to be the tree panel.)
19158  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19159  * 
19160  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19161  * @cfg {Roo.form.TextField|Object} field The field configuration
19162  *
19163  * 
19164  */
19165 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19166     var tree = config;
19167     var field;
19168     if (oldconfig) { // old style..
19169         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19170     } else {
19171         // new style..
19172         tree = config.tree;
19173         config.field = config.field  || {};
19174         config.field.xtype = 'TextField';
19175         field = Roo.factory(config.field, Roo.form);
19176     }
19177     config = config || {};
19178     
19179     
19180     this.addEvents({
19181         /**
19182          * @event beforenodeedit
19183          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19184          * false from the handler of this event.
19185          * @param {Editor} this
19186          * @param {Roo.tree.Node} node 
19187          */
19188         "beforenodeedit" : true
19189     });
19190     
19191     //Roo.log(config);
19192     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19193
19194     this.tree = tree;
19195
19196     tree.on('beforeclick', this.beforeNodeClick, this);
19197     tree.getTreeEl().on('mousedown', this.hide, this);
19198     this.on('complete', this.updateNode, this);
19199     this.on('beforestartedit', this.fitToTree, this);
19200     this.on('startedit', this.bindScroll, this, {delay:10});
19201     this.on('specialkey', this.onSpecialKey, this);
19202 };
19203
19204 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19205     /**
19206      * @cfg {String} alignment
19207      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19208      */
19209     alignment: "l-l",
19210     // inherit
19211     autoSize: false,
19212     /**
19213      * @cfg {Boolean} hideEl
19214      * True to hide the bound element while the editor is displayed (defaults to false)
19215      */
19216     hideEl : false,
19217     /**
19218      * @cfg {String} cls
19219      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19220      */
19221     cls: "x-small-editor x-tree-editor",
19222     /**
19223      * @cfg {Boolean} shim
19224      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19225      */
19226     shim:false,
19227     // inherit
19228     shadow:"frame",
19229     /**
19230      * @cfg {Number} maxWidth
19231      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19232      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19233      * scroll and client offsets into account prior to each edit.
19234      */
19235     maxWidth: 250,
19236
19237     editDelay : 350,
19238
19239     // private
19240     fitToTree : function(ed, el){
19241         var td = this.tree.getTreeEl().dom, nd = el.dom;
19242         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19243             td.scrollLeft = nd.offsetLeft;
19244         }
19245         var w = Math.min(
19246                 this.maxWidth,
19247                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19248         this.setSize(w, '');
19249         
19250         return this.fireEvent('beforenodeedit', this, this.editNode);
19251         
19252     },
19253
19254     // private
19255     triggerEdit : function(node){
19256         this.completeEdit();
19257         this.editNode = node;
19258         this.startEdit(node.ui.textNode, node.text);
19259     },
19260
19261     // private
19262     bindScroll : function(){
19263         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19264     },
19265
19266     // private
19267     beforeNodeClick : function(node, e){
19268         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19269         this.lastClick = new Date();
19270         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19271             e.stopEvent();
19272             this.triggerEdit(node);
19273             return false;
19274         }
19275         return true;
19276     },
19277
19278     // private
19279     updateNode : function(ed, value){
19280         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19281         this.editNode.setText(value);
19282     },
19283
19284     // private
19285     onHide : function(){
19286         Roo.tree.TreeEditor.superclass.onHide.call(this);
19287         if(this.editNode){
19288             this.editNode.ui.focus();
19289         }
19290     },
19291
19292     // private
19293     onSpecialKey : function(field, e){
19294         var k = e.getKey();
19295         if(k == e.ESC){
19296             e.stopEvent();
19297             this.cancelEdit();
19298         }else if(k == e.ENTER && !e.hasModifier()){
19299             e.stopEvent();
19300             this.completeEdit();
19301         }
19302     }
19303 });//<Script type="text/javascript">
19304 /*
19305  * Based on:
19306  * Ext JS Library 1.1.1
19307  * Copyright(c) 2006-2007, Ext JS, LLC.
19308  *
19309  * Originally Released Under LGPL - original licence link has changed is not relivant.
19310  *
19311  * Fork - LGPL
19312  * <script type="text/javascript">
19313  */
19314  
19315 /**
19316  * Not documented??? - probably should be...
19317  */
19318
19319 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19320     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19321     
19322     renderElements : function(n, a, targetNode, bulkRender){
19323         //consel.log("renderElements?");
19324         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19325
19326         var t = n.getOwnerTree();
19327         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19328         
19329         var cols = t.columns;
19330         var bw = t.borderWidth;
19331         var c = cols[0];
19332         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19333          var cb = typeof a.checked == "boolean";
19334         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19335         var colcls = 'x-t-' + tid + '-c0';
19336         var buf = [
19337             '<li class="x-tree-node">',
19338             
19339                 
19340                 '<div class="x-tree-node-el ', a.cls,'">',
19341                     // extran...
19342                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19343                 
19344                 
19345                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19346                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19347                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19348                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19349                            (a.iconCls ? ' '+a.iconCls : ''),
19350                            '" unselectable="on" />',
19351                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19352                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19353                              
19354                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19355                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19356                             '<span unselectable="on" qtip="' + tx + '">',
19357                              tx,
19358                              '</span></a>' ,
19359                     '</div>',
19360                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19361                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19362                  ];
19363         for(var i = 1, len = cols.length; i < len; i++){
19364             c = cols[i];
19365             colcls = 'x-t-' + tid + '-c' +i;
19366             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19367             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19368                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19369                       "</div>");
19370          }
19371          
19372          buf.push(
19373             '</a>',
19374             '<div class="x-clear"></div></div>',
19375             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19376             "</li>");
19377         
19378         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19379             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19380                                 n.nextSibling.ui.getEl(), buf.join(""));
19381         }else{
19382             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19383         }
19384         var el = this.wrap.firstChild;
19385         this.elRow = el;
19386         this.elNode = el.firstChild;
19387         this.ranchor = el.childNodes[1];
19388         this.ctNode = this.wrap.childNodes[1];
19389         var cs = el.firstChild.childNodes;
19390         this.indentNode = cs[0];
19391         this.ecNode = cs[1];
19392         this.iconNode = cs[2];
19393         var index = 3;
19394         if(cb){
19395             this.checkbox = cs[3];
19396             index++;
19397         }
19398         this.anchor = cs[index];
19399         
19400         this.textNode = cs[index].firstChild;
19401         
19402         //el.on("click", this.onClick, this);
19403         //el.on("dblclick", this.onDblClick, this);
19404         
19405         
19406        // console.log(this);
19407     },
19408     initEvents : function(){
19409         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19410         
19411             
19412         var a = this.ranchor;
19413
19414         var el = Roo.get(a);
19415
19416         if(Roo.isOpera){ // opera render bug ignores the CSS
19417             el.setStyle("text-decoration", "none");
19418         }
19419
19420         el.on("click", this.onClick, this);
19421         el.on("dblclick", this.onDblClick, this);
19422         el.on("contextmenu", this.onContextMenu, this);
19423         
19424     },
19425     
19426     /*onSelectedChange : function(state){
19427         if(state){
19428             this.focus();
19429             this.addClass("x-tree-selected");
19430         }else{
19431             //this.blur();
19432             this.removeClass("x-tree-selected");
19433         }
19434     },*/
19435     addClass : function(cls){
19436         if(this.elRow){
19437             Roo.fly(this.elRow).addClass(cls);
19438         }
19439         
19440     },
19441     
19442     
19443     removeClass : function(cls){
19444         if(this.elRow){
19445             Roo.fly(this.elRow).removeClass(cls);
19446         }
19447     }
19448
19449     
19450     
19451 });//<Script type="text/javascript">
19452
19453 /*
19454  * Based on:
19455  * Ext JS Library 1.1.1
19456  * Copyright(c) 2006-2007, Ext JS, LLC.
19457  *
19458  * Originally Released Under LGPL - original licence link has changed is not relivant.
19459  *
19460  * Fork - LGPL
19461  * <script type="text/javascript">
19462  */
19463  
19464
19465 /**
19466  * @class Roo.tree.ColumnTree
19467  * @extends Roo.data.TreePanel
19468  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19469  * @cfg {int} borderWidth  compined right/left border allowance
19470  * @constructor
19471  * @param {String/HTMLElement/Element} el The container element
19472  * @param {Object} config
19473  */
19474 Roo.tree.ColumnTree =  function(el, config)
19475 {
19476    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19477    this.addEvents({
19478         /**
19479         * @event resize
19480         * Fire this event on a container when it resizes
19481         * @param {int} w Width
19482         * @param {int} h Height
19483         */
19484        "resize" : true
19485     });
19486     this.on('resize', this.onResize, this);
19487 };
19488
19489 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19490     //lines:false,
19491     
19492     
19493     borderWidth: Roo.isBorderBox ? 0 : 2, 
19494     headEls : false,
19495     
19496     render : function(){
19497         // add the header.....
19498        
19499         Roo.tree.ColumnTree.superclass.render.apply(this);
19500         
19501         this.el.addClass('x-column-tree');
19502         
19503         this.headers = this.el.createChild(
19504             {cls:'x-tree-headers'},this.innerCt.dom);
19505    
19506         var cols = this.columns, c;
19507         var totalWidth = 0;
19508         this.headEls = [];
19509         var  len = cols.length;
19510         for(var i = 0; i < len; i++){
19511              c = cols[i];
19512              totalWidth += c.width;
19513             this.headEls.push(this.headers.createChild({
19514                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19515                  cn: {
19516                      cls:'x-tree-hd-text',
19517                      html: c.header
19518                  },
19519                  style:'width:'+(c.width-this.borderWidth)+'px;'
19520              }));
19521         }
19522         this.headers.createChild({cls:'x-clear'});
19523         // prevent floats from wrapping when clipped
19524         this.headers.setWidth(totalWidth);
19525         //this.innerCt.setWidth(totalWidth);
19526         this.innerCt.setStyle({ overflow: 'auto' });
19527         this.onResize(this.width, this.height);
19528              
19529         
19530     },
19531     onResize : function(w,h)
19532     {
19533         this.height = h;
19534         this.width = w;
19535         // resize cols..
19536         this.innerCt.setWidth(this.width);
19537         this.innerCt.setHeight(this.height-20);
19538         
19539         // headers...
19540         var cols = this.columns, c;
19541         var totalWidth = 0;
19542         var expEl = false;
19543         var len = cols.length;
19544         for(var i = 0; i < len; i++){
19545             c = cols[i];
19546             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19547                 // it's the expander..
19548                 expEl  = this.headEls[i];
19549                 continue;
19550             }
19551             totalWidth += c.width;
19552             
19553         }
19554         if (expEl) {
19555             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19556         }
19557         this.headers.setWidth(w-20);
19558
19559         
19560         
19561         
19562     }
19563 });
19564 /*
19565  * Based on:
19566  * Ext JS Library 1.1.1
19567  * Copyright(c) 2006-2007, Ext JS, LLC.
19568  *
19569  * Originally Released Under LGPL - original licence link has changed is not relivant.
19570  *
19571  * Fork - LGPL
19572  * <script type="text/javascript">
19573  */
19574  
19575 /**
19576  * @class Roo.menu.Menu
19577  * @extends Roo.util.Observable
19578  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19579  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19580  * @constructor
19581  * Creates a new Menu
19582  * @param {Object} config Configuration options
19583  */
19584 Roo.menu.Menu = function(config){
19585     Roo.apply(this, config);
19586     this.id = this.id || Roo.id();
19587     this.addEvents({
19588         /**
19589          * @event beforeshow
19590          * Fires before this menu is displayed
19591          * @param {Roo.menu.Menu} this
19592          */
19593         beforeshow : true,
19594         /**
19595          * @event beforehide
19596          * Fires before this menu is hidden
19597          * @param {Roo.menu.Menu} this
19598          */
19599         beforehide : true,
19600         /**
19601          * @event show
19602          * Fires after this menu is displayed
19603          * @param {Roo.menu.Menu} this
19604          */
19605         show : true,
19606         /**
19607          * @event hide
19608          * Fires after this menu is hidden
19609          * @param {Roo.menu.Menu} this
19610          */
19611         hide : true,
19612         /**
19613          * @event click
19614          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19615          * @param {Roo.menu.Menu} this
19616          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19617          * @param {Roo.EventObject} e
19618          */
19619         click : true,
19620         /**
19621          * @event mouseover
19622          * Fires when the mouse is hovering over this menu
19623          * @param {Roo.menu.Menu} this
19624          * @param {Roo.EventObject} e
19625          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19626          */
19627         mouseover : true,
19628         /**
19629          * @event mouseout
19630          * Fires when the mouse exits this menu
19631          * @param {Roo.menu.Menu} this
19632          * @param {Roo.EventObject} e
19633          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19634          */
19635         mouseout : true,
19636         /**
19637          * @event itemclick
19638          * Fires when a menu item contained in this menu is clicked
19639          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19640          * @param {Roo.EventObject} e
19641          */
19642         itemclick: true
19643     });
19644     if (this.registerMenu) {
19645         Roo.menu.MenuMgr.register(this);
19646     }
19647     
19648     var mis = this.items;
19649     this.items = new Roo.util.MixedCollection();
19650     if(mis){
19651         this.add.apply(this, mis);
19652     }
19653 };
19654
19655 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19656     /**
19657      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19658      */
19659     minWidth : 120,
19660     /**
19661      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19662      * for bottom-right shadow (defaults to "sides")
19663      */
19664     shadow : "sides",
19665     /**
19666      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19667      * this menu (defaults to "tl-tr?")
19668      */
19669     subMenuAlign : "tl-tr?",
19670     /**
19671      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19672      * relative to its element of origin (defaults to "tl-bl?")
19673      */
19674     defaultAlign : "tl-bl?",
19675     /**
19676      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19677      */
19678     allowOtherMenus : false,
19679     /**
19680      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19681      */
19682     registerMenu : true,
19683
19684     hidden:true,
19685
19686     // private
19687     render : function(){
19688         if(this.el){
19689             return;
19690         }
19691         var el = this.el = new Roo.Layer({
19692             cls: "x-menu",
19693             shadow:this.shadow,
19694             constrain: false,
19695             parentEl: this.parentEl || document.body,
19696             zindex:15000
19697         });
19698
19699         this.keyNav = new Roo.menu.MenuNav(this);
19700
19701         if(this.plain){
19702             el.addClass("x-menu-plain");
19703         }
19704         if(this.cls){
19705             el.addClass(this.cls);
19706         }
19707         // generic focus element
19708         this.focusEl = el.createChild({
19709             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19710         });
19711         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19712         ul.on("click", this.onClick, this);
19713         ul.on("mouseover", this.onMouseOver, this);
19714         ul.on("mouseout", this.onMouseOut, this);
19715         this.items.each(function(item){
19716             var li = document.createElement("li");
19717             li.className = "x-menu-list-item";
19718             ul.dom.appendChild(li);
19719             item.render(li, this);
19720         }, this);
19721         this.ul = ul;
19722         this.autoWidth();
19723     },
19724
19725     // private
19726     autoWidth : function(){
19727         var el = this.el, ul = this.ul;
19728         if(!el){
19729             return;
19730         }
19731         var w = this.width;
19732         if(w){
19733             el.setWidth(w);
19734         }else if(Roo.isIE){
19735             el.setWidth(this.minWidth);
19736             var t = el.dom.offsetWidth; // force recalc
19737             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19738         }
19739     },
19740
19741     // private
19742     delayAutoWidth : function(){
19743         if(this.rendered){
19744             if(!this.awTask){
19745                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19746             }
19747             this.awTask.delay(20);
19748         }
19749     },
19750
19751     // private
19752     findTargetItem : function(e){
19753         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19754         if(t && t.menuItemId){
19755             return this.items.get(t.menuItemId);
19756         }
19757     },
19758
19759     // private
19760     onClick : function(e){
19761         var t;
19762         if(t = this.findTargetItem(e)){
19763             t.onClick(e);
19764             this.fireEvent("click", this, t, e);
19765         }
19766     },
19767
19768     // private
19769     setActiveItem : function(item, autoExpand){
19770         if(item != this.activeItem){
19771             if(this.activeItem){
19772                 this.activeItem.deactivate();
19773             }
19774             this.activeItem = item;
19775             item.activate(autoExpand);
19776         }else if(autoExpand){
19777             item.expandMenu();
19778         }
19779     },
19780
19781     // private
19782     tryActivate : function(start, step){
19783         var items = this.items;
19784         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19785             var item = items.get(i);
19786             if(!item.disabled && item.canActivate){
19787                 this.setActiveItem(item, false);
19788                 return item;
19789             }
19790         }
19791         return false;
19792     },
19793
19794     // private
19795     onMouseOver : function(e){
19796         var t;
19797         if(t = this.findTargetItem(e)){
19798             if(t.canActivate && !t.disabled){
19799                 this.setActiveItem(t, true);
19800             }
19801         }
19802         this.fireEvent("mouseover", this, e, t);
19803     },
19804
19805     // private
19806     onMouseOut : function(e){
19807         var t;
19808         if(t = this.findTargetItem(e)){
19809             if(t == this.activeItem && t.shouldDeactivate(e)){
19810                 this.activeItem.deactivate();
19811                 delete this.activeItem;
19812             }
19813         }
19814         this.fireEvent("mouseout", this, e, t);
19815     },
19816
19817     /**
19818      * Read-only.  Returns true if the menu is currently displayed, else false.
19819      * @type Boolean
19820      */
19821     isVisible : function(){
19822         return this.el && !this.hidden;
19823     },
19824
19825     /**
19826      * Displays this menu relative to another element
19827      * @param {String/HTMLElement/Roo.Element} element The element to align to
19828      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19829      * the element (defaults to this.defaultAlign)
19830      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19831      */
19832     show : function(el, pos, parentMenu){
19833         this.parentMenu = parentMenu;
19834         if(!this.el){
19835             this.render();
19836         }
19837         this.fireEvent("beforeshow", this);
19838         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19839     },
19840
19841     /**
19842      * Displays this menu at a specific xy position
19843      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19844      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19845      */
19846     showAt : function(xy, parentMenu, /* private: */_e){
19847         this.parentMenu = parentMenu;
19848         if(!this.el){
19849             this.render();
19850         }
19851         if(_e !== false){
19852             this.fireEvent("beforeshow", this);
19853             xy = this.el.adjustForConstraints(xy);
19854         }
19855         this.el.setXY(xy);
19856         this.el.show();
19857         this.hidden = false;
19858         this.focus();
19859         this.fireEvent("show", this);
19860     },
19861
19862     focus : function(){
19863         if(!this.hidden){
19864             this.doFocus.defer(50, this);
19865         }
19866     },
19867
19868     doFocus : function(){
19869         if(!this.hidden){
19870             this.focusEl.focus();
19871         }
19872     },
19873
19874     /**
19875      * Hides this menu and optionally all parent menus
19876      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19877      */
19878     hide : function(deep){
19879         if(this.el && this.isVisible()){
19880             this.fireEvent("beforehide", this);
19881             if(this.activeItem){
19882                 this.activeItem.deactivate();
19883                 this.activeItem = null;
19884             }
19885             this.el.hide();
19886             this.hidden = true;
19887             this.fireEvent("hide", this);
19888         }
19889         if(deep === true && this.parentMenu){
19890             this.parentMenu.hide(true);
19891         }
19892     },
19893
19894     /**
19895      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19896      * Any of the following are valid:
19897      * <ul>
19898      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19899      * <li>An HTMLElement object which will be converted to a menu item</li>
19900      * <li>A menu item config object that will be created as a new menu item</li>
19901      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19902      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19903      * </ul>
19904      * Usage:
19905      * <pre><code>
19906 // Create the menu
19907 var menu = new Roo.menu.Menu();
19908
19909 // Create a menu item to add by reference
19910 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19911
19912 // Add a bunch of items at once using different methods.
19913 // Only the last item added will be returned.
19914 var item = menu.add(
19915     menuItem,                // add existing item by ref
19916     'Dynamic Item',          // new TextItem
19917     '-',                     // new separator
19918     { text: 'Config Item' }  // new item by config
19919 );
19920 </code></pre>
19921      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19922      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19923      */
19924     add : function(){
19925         var a = arguments, l = a.length, item;
19926         for(var i = 0; i < l; i++){
19927             var el = a[i];
19928             if ((typeof(el) == "object") && el.xtype && el.xns) {
19929                 el = Roo.factory(el, Roo.menu);
19930             }
19931             
19932             if(el.render){ // some kind of Item
19933                 item = this.addItem(el);
19934             }else if(typeof el == "string"){ // string
19935                 if(el == "separator" || el == "-"){
19936                     item = this.addSeparator();
19937                 }else{
19938                     item = this.addText(el);
19939                 }
19940             }else if(el.tagName || el.el){ // element
19941                 item = this.addElement(el);
19942             }else if(typeof el == "object"){ // must be menu item config?
19943                 item = this.addMenuItem(el);
19944             }
19945         }
19946         return item;
19947     },
19948
19949     /**
19950      * Returns this menu's underlying {@link Roo.Element} object
19951      * @return {Roo.Element} The element
19952      */
19953     getEl : function(){
19954         if(!this.el){
19955             this.render();
19956         }
19957         return this.el;
19958     },
19959
19960     /**
19961      * Adds a separator bar to the menu
19962      * @return {Roo.menu.Item} The menu item that was added
19963      */
19964     addSeparator : function(){
19965         return this.addItem(new Roo.menu.Separator());
19966     },
19967
19968     /**
19969      * Adds an {@link Roo.Element} object to the menu
19970      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19971      * @return {Roo.menu.Item} The menu item that was added
19972      */
19973     addElement : function(el){
19974         return this.addItem(new Roo.menu.BaseItem(el));
19975     },
19976
19977     /**
19978      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19979      * @param {Roo.menu.Item} item The menu item to add
19980      * @return {Roo.menu.Item} The menu item that was added
19981      */
19982     addItem : function(item){
19983         this.items.add(item);
19984         if(this.ul){
19985             var li = document.createElement("li");
19986             li.className = "x-menu-list-item";
19987             this.ul.dom.appendChild(li);
19988             item.render(li, this);
19989             this.delayAutoWidth();
19990         }
19991         return item;
19992     },
19993
19994     /**
19995      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19996      * @param {Object} config A MenuItem config object
19997      * @return {Roo.menu.Item} The menu item that was added
19998      */
19999     addMenuItem : function(config){
20000         if(!(config instanceof Roo.menu.Item)){
20001             if(typeof config.checked == "boolean"){ // must be check menu item config?
20002                 config = new Roo.menu.CheckItem(config);
20003             }else{
20004                 config = new Roo.menu.Item(config);
20005             }
20006         }
20007         return this.addItem(config);
20008     },
20009
20010     /**
20011      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20012      * @param {String} text The text to display in the menu item
20013      * @return {Roo.menu.Item} The menu item that was added
20014      */
20015     addText : function(text){
20016         return this.addItem(new Roo.menu.TextItem({ text : text }));
20017     },
20018
20019     /**
20020      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20021      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20022      * @param {Roo.menu.Item} item The menu item to add
20023      * @return {Roo.menu.Item} The menu item that was added
20024      */
20025     insert : function(index, item){
20026         this.items.insert(index, item);
20027         if(this.ul){
20028             var li = document.createElement("li");
20029             li.className = "x-menu-list-item";
20030             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20031             item.render(li, this);
20032             this.delayAutoWidth();
20033         }
20034         return item;
20035     },
20036
20037     /**
20038      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20039      * @param {Roo.menu.Item} item The menu item to remove
20040      */
20041     remove : function(item){
20042         this.items.removeKey(item.id);
20043         item.destroy();
20044     },
20045
20046     /**
20047      * Removes and destroys all items in the menu
20048      */
20049     removeAll : function(){
20050         var f;
20051         while(f = this.items.first()){
20052             this.remove(f);
20053         }
20054     }
20055 });
20056
20057 // MenuNav is a private utility class used internally by the Menu
20058 Roo.menu.MenuNav = function(menu){
20059     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20060     this.scope = this.menu = menu;
20061 };
20062
20063 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20064     doRelay : function(e, h){
20065         var k = e.getKey();
20066         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20067             this.menu.tryActivate(0, 1);
20068             return false;
20069         }
20070         return h.call(this.scope || this, e, this.menu);
20071     },
20072
20073     up : function(e, m){
20074         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20075             m.tryActivate(m.items.length-1, -1);
20076         }
20077     },
20078
20079     down : function(e, m){
20080         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20081             m.tryActivate(0, 1);
20082         }
20083     },
20084
20085     right : function(e, m){
20086         if(m.activeItem){
20087             m.activeItem.expandMenu(true);
20088         }
20089     },
20090
20091     left : function(e, m){
20092         m.hide();
20093         if(m.parentMenu && m.parentMenu.activeItem){
20094             m.parentMenu.activeItem.activate();
20095         }
20096     },
20097
20098     enter : function(e, m){
20099         if(m.activeItem){
20100             e.stopPropagation();
20101             m.activeItem.onClick(e);
20102             m.fireEvent("click", this, m.activeItem);
20103             return true;
20104         }
20105     }
20106 });/*
20107  * Based on:
20108  * Ext JS Library 1.1.1
20109  * Copyright(c) 2006-2007, Ext JS, LLC.
20110  *
20111  * Originally Released Under LGPL - original licence link has changed is not relivant.
20112  *
20113  * Fork - LGPL
20114  * <script type="text/javascript">
20115  */
20116  
20117 /**
20118  * @class Roo.menu.MenuMgr
20119  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20120  * @singleton
20121  */
20122 Roo.menu.MenuMgr = function(){
20123    var menus, active, groups = {}, attached = false, lastShow = new Date();
20124
20125    // private - called when first menu is created
20126    function init(){
20127        menus = {};
20128        active = new Roo.util.MixedCollection();
20129        Roo.get(document).addKeyListener(27, function(){
20130            if(active.length > 0){
20131                hideAll();
20132            }
20133        });
20134    }
20135
20136    // private
20137    function hideAll(){
20138        if(active && active.length > 0){
20139            var c = active.clone();
20140            c.each(function(m){
20141                m.hide();
20142            });
20143        }
20144    }
20145
20146    // private
20147    function onHide(m){
20148        active.remove(m);
20149        if(active.length < 1){
20150            Roo.get(document).un("mousedown", onMouseDown);
20151            attached = false;
20152        }
20153    }
20154
20155    // private
20156    function onShow(m){
20157        var last = active.last();
20158        lastShow = new Date();
20159        active.add(m);
20160        if(!attached){
20161            Roo.get(document).on("mousedown", onMouseDown);
20162            attached = true;
20163        }
20164        if(m.parentMenu){
20165           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20166           m.parentMenu.activeChild = m;
20167        }else if(last && last.isVisible()){
20168           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20169        }
20170    }
20171
20172    // private
20173    function onBeforeHide(m){
20174        if(m.activeChild){
20175            m.activeChild.hide();
20176        }
20177        if(m.autoHideTimer){
20178            clearTimeout(m.autoHideTimer);
20179            delete m.autoHideTimer;
20180        }
20181    }
20182
20183    // private
20184    function onBeforeShow(m){
20185        var pm = m.parentMenu;
20186        if(!pm && !m.allowOtherMenus){
20187            hideAll();
20188        }else if(pm && pm.activeChild && active != m){
20189            pm.activeChild.hide();
20190        }
20191    }
20192
20193    // private
20194    function onMouseDown(e){
20195        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20196            hideAll();
20197        }
20198    }
20199
20200    // private
20201    function onBeforeCheck(mi, state){
20202        if(state){
20203            var g = groups[mi.group];
20204            for(var i = 0, l = g.length; i < l; i++){
20205                if(g[i] != mi){
20206                    g[i].setChecked(false);
20207                }
20208            }
20209        }
20210    }
20211
20212    return {
20213
20214        /**
20215         * Hides all menus that are currently visible
20216         */
20217        hideAll : function(){
20218             hideAll();  
20219        },
20220
20221        // private
20222        register : function(menu){
20223            if(!menus){
20224                init();
20225            }
20226            menus[menu.id] = menu;
20227            menu.on("beforehide", onBeforeHide);
20228            menu.on("hide", onHide);
20229            menu.on("beforeshow", onBeforeShow);
20230            menu.on("show", onShow);
20231            var g = menu.group;
20232            if(g && menu.events["checkchange"]){
20233                if(!groups[g]){
20234                    groups[g] = [];
20235                }
20236                groups[g].push(menu);
20237                menu.on("checkchange", onCheck);
20238            }
20239        },
20240
20241         /**
20242          * Returns a {@link Roo.menu.Menu} object
20243          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20244          * be used to generate and return a new Menu instance.
20245          */
20246        get : function(menu){
20247            if(typeof menu == "string"){ // menu id
20248                return menus[menu];
20249            }else if(menu.events){  // menu instance
20250                return menu;
20251            }else if(typeof menu.length == 'number'){ // array of menu items?
20252                return new Roo.menu.Menu({items:menu});
20253            }else{ // otherwise, must be a config
20254                return new Roo.menu.Menu(menu);
20255            }
20256        },
20257
20258        // private
20259        unregister : function(menu){
20260            delete menus[menu.id];
20261            menu.un("beforehide", onBeforeHide);
20262            menu.un("hide", onHide);
20263            menu.un("beforeshow", onBeforeShow);
20264            menu.un("show", onShow);
20265            var g = menu.group;
20266            if(g && menu.events["checkchange"]){
20267                groups[g].remove(menu);
20268                menu.un("checkchange", onCheck);
20269            }
20270        },
20271
20272        // private
20273        registerCheckable : function(menuItem){
20274            var g = menuItem.group;
20275            if(g){
20276                if(!groups[g]){
20277                    groups[g] = [];
20278                }
20279                groups[g].push(menuItem);
20280                menuItem.on("beforecheckchange", onBeforeCheck);
20281            }
20282        },
20283
20284        // private
20285        unregisterCheckable : function(menuItem){
20286            var g = menuItem.group;
20287            if(g){
20288                groups[g].remove(menuItem);
20289                menuItem.un("beforecheckchange", onBeforeCheck);
20290            }
20291        }
20292    };
20293 }();/*
20294  * Based on:
20295  * Ext JS Library 1.1.1
20296  * Copyright(c) 2006-2007, Ext JS, LLC.
20297  *
20298  * Originally Released Under LGPL - original licence link has changed is not relivant.
20299  *
20300  * Fork - LGPL
20301  * <script type="text/javascript">
20302  */
20303  
20304
20305 /**
20306  * @class Roo.menu.BaseItem
20307  * @extends Roo.Component
20308  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20309  * management and base configuration options shared by all menu components.
20310  * @constructor
20311  * Creates a new BaseItem
20312  * @param {Object} config Configuration options
20313  */
20314 Roo.menu.BaseItem = function(config){
20315     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20316
20317     this.addEvents({
20318         /**
20319          * @event click
20320          * Fires when this item is clicked
20321          * @param {Roo.menu.BaseItem} this
20322          * @param {Roo.EventObject} e
20323          */
20324         click: true,
20325         /**
20326          * @event activate
20327          * Fires when this item is activated
20328          * @param {Roo.menu.BaseItem} this
20329          */
20330         activate : true,
20331         /**
20332          * @event deactivate
20333          * Fires when this item is deactivated
20334          * @param {Roo.menu.BaseItem} this
20335          */
20336         deactivate : true
20337     });
20338
20339     if(this.handler){
20340         this.on("click", this.handler, this.scope, true);
20341     }
20342 };
20343
20344 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20345     /**
20346      * @cfg {Function} handler
20347      * A function that will handle the click event of this menu item (defaults to undefined)
20348      */
20349     /**
20350      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20351      */
20352     canActivate : false,
20353     /**
20354      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20355      */
20356     activeClass : "x-menu-item-active",
20357     /**
20358      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20359      */
20360     hideOnClick : true,
20361     /**
20362      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20363      */
20364     hideDelay : 100,
20365
20366     // private
20367     ctype: "Roo.menu.BaseItem",
20368
20369     // private
20370     actionMode : "container",
20371
20372     // private
20373     render : function(container, parentMenu){
20374         this.parentMenu = parentMenu;
20375         Roo.menu.BaseItem.superclass.render.call(this, container);
20376         this.container.menuItemId = this.id;
20377     },
20378
20379     // private
20380     onRender : function(container, position){
20381         this.el = Roo.get(this.el);
20382         container.dom.appendChild(this.el.dom);
20383     },
20384
20385     // private
20386     onClick : function(e){
20387         if(!this.disabled && this.fireEvent("click", this, e) !== false
20388                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20389             this.handleClick(e);
20390         }else{
20391             e.stopEvent();
20392         }
20393     },
20394
20395     // private
20396     activate : function(){
20397         if(this.disabled){
20398             return false;
20399         }
20400         var li = this.container;
20401         li.addClass(this.activeClass);
20402         this.region = li.getRegion().adjust(2, 2, -2, -2);
20403         this.fireEvent("activate", this);
20404         return true;
20405     },
20406
20407     // private
20408     deactivate : function(){
20409         this.container.removeClass(this.activeClass);
20410         this.fireEvent("deactivate", this);
20411     },
20412
20413     // private
20414     shouldDeactivate : function(e){
20415         return !this.region || !this.region.contains(e.getPoint());
20416     },
20417
20418     // private
20419     handleClick : function(e){
20420         if(this.hideOnClick){
20421             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20422         }
20423     },
20424
20425     // private
20426     expandMenu : function(autoActivate){
20427         // do nothing
20428     },
20429
20430     // private
20431     hideMenu : function(){
20432         // do nothing
20433     }
20434 });/*
20435  * Based on:
20436  * Ext JS Library 1.1.1
20437  * Copyright(c) 2006-2007, Ext JS, LLC.
20438  *
20439  * Originally Released Under LGPL - original licence link has changed is not relivant.
20440  *
20441  * Fork - LGPL
20442  * <script type="text/javascript">
20443  */
20444  
20445 /**
20446  * @class Roo.menu.Adapter
20447  * @extends Roo.menu.BaseItem
20448  * 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.
20449  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20450  * @constructor
20451  * Creates a new Adapter
20452  * @param {Object} config Configuration options
20453  */
20454 Roo.menu.Adapter = function(component, config){
20455     Roo.menu.Adapter.superclass.constructor.call(this, config);
20456     this.component = component;
20457 };
20458 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20459     // private
20460     canActivate : true,
20461
20462     // private
20463     onRender : function(container, position){
20464         this.component.render(container);
20465         this.el = this.component.getEl();
20466     },
20467
20468     // private
20469     activate : function(){
20470         if(this.disabled){
20471             return false;
20472         }
20473         this.component.focus();
20474         this.fireEvent("activate", this);
20475         return true;
20476     },
20477
20478     // private
20479     deactivate : function(){
20480         this.fireEvent("deactivate", this);
20481     },
20482
20483     // private
20484     disable : function(){
20485         this.component.disable();
20486         Roo.menu.Adapter.superclass.disable.call(this);
20487     },
20488
20489     // private
20490     enable : function(){
20491         this.component.enable();
20492         Roo.menu.Adapter.superclass.enable.call(this);
20493     }
20494 });/*
20495  * Based on:
20496  * Ext JS Library 1.1.1
20497  * Copyright(c) 2006-2007, Ext JS, LLC.
20498  *
20499  * Originally Released Under LGPL - original licence link has changed is not relivant.
20500  *
20501  * Fork - LGPL
20502  * <script type="text/javascript">
20503  */
20504
20505 /**
20506  * @class Roo.menu.TextItem
20507  * @extends Roo.menu.BaseItem
20508  * Adds a static text string to a menu, usually used as either a heading or group separator.
20509  * Note: old style constructor with text is still supported.
20510  * 
20511  * @constructor
20512  * Creates a new TextItem
20513  * @param {Object} cfg Configuration
20514  */
20515 Roo.menu.TextItem = function(cfg){
20516     if (typeof(cfg) == 'string') {
20517         this.text = cfg;
20518     } else {
20519         Roo.apply(this,cfg);
20520     }
20521     
20522     Roo.menu.TextItem.superclass.constructor.call(this);
20523 };
20524
20525 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20526     /**
20527      * @cfg {Boolean} text Text to show on item.
20528      */
20529     text : '',
20530     
20531     /**
20532      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20533      */
20534     hideOnClick : false,
20535     /**
20536      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20537      */
20538     itemCls : "x-menu-text",
20539
20540     // private
20541     onRender : function(){
20542         var s = document.createElement("span");
20543         s.className = this.itemCls;
20544         s.innerHTML = this.text;
20545         this.el = s;
20546         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20547     }
20548 });/*
20549  * Based on:
20550  * Ext JS Library 1.1.1
20551  * Copyright(c) 2006-2007, Ext JS, LLC.
20552  *
20553  * Originally Released Under LGPL - original licence link has changed is not relivant.
20554  *
20555  * Fork - LGPL
20556  * <script type="text/javascript">
20557  */
20558
20559 /**
20560  * @class Roo.menu.Separator
20561  * @extends Roo.menu.BaseItem
20562  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20563  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20564  * @constructor
20565  * @param {Object} config Configuration options
20566  */
20567 Roo.menu.Separator = function(config){
20568     Roo.menu.Separator.superclass.constructor.call(this, config);
20569 };
20570
20571 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20572     /**
20573      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20574      */
20575     itemCls : "x-menu-sep",
20576     /**
20577      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20578      */
20579     hideOnClick : false,
20580
20581     // private
20582     onRender : function(li){
20583         var s = document.createElement("span");
20584         s.className = this.itemCls;
20585         s.innerHTML = "&#160;";
20586         this.el = s;
20587         li.addClass("x-menu-sep-li");
20588         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20589     }
20590 });/*
20591  * Based on:
20592  * Ext JS Library 1.1.1
20593  * Copyright(c) 2006-2007, Ext JS, LLC.
20594  *
20595  * Originally Released Under LGPL - original licence link has changed is not relivant.
20596  *
20597  * Fork - LGPL
20598  * <script type="text/javascript">
20599  */
20600 /**
20601  * @class Roo.menu.Item
20602  * @extends Roo.menu.BaseItem
20603  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20604  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20605  * activation and click handling.
20606  * @constructor
20607  * Creates a new Item
20608  * @param {Object} config Configuration options
20609  */
20610 Roo.menu.Item = function(config){
20611     Roo.menu.Item.superclass.constructor.call(this, config);
20612     if(this.menu){
20613         this.menu = Roo.menu.MenuMgr.get(this.menu);
20614     }
20615 };
20616 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20617     
20618     /**
20619      * @cfg {String} text
20620      * The text to show on the menu item.
20621      */
20622     text: '',
20623      /**
20624      * @cfg {String} HTML to render in menu
20625      * The text to show on the menu item (HTML version).
20626      */
20627     html: '',
20628     /**
20629      * @cfg {String} icon
20630      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20631      */
20632     icon: undefined,
20633     /**
20634      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20635      */
20636     itemCls : "x-menu-item",
20637     /**
20638      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20639      */
20640     canActivate : true,
20641     /**
20642      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20643      */
20644     showDelay: 200,
20645     // doc'd in BaseItem
20646     hideDelay: 200,
20647
20648     // private
20649     ctype: "Roo.menu.Item",
20650     
20651     // private
20652     onRender : function(container, position){
20653         var el = document.createElement("a");
20654         el.hideFocus = true;
20655         el.unselectable = "on";
20656         el.href = this.href || "#";
20657         if(this.hrefTarget){
20658             el.target = this.hrefTarget;
20659         }
20660         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20661         
20662         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20663         
20664         el.innerHTML = String.format(
20665                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20666                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20667         this.el = el;
20668         Roo.menu.Item.superclass.onRender.call(this, container, position);
20669     },
20670
20671     /**
20672      * Sets the text to display in this menu item
20673      * @param {String} text The text to display
20674      * @param {Boolean} isHTML true to indicate text is pure html.
20675      */
20676     setText : function(text, isHTML){
20677         if (isHTML) {
20678             this.html = text;
20679         } else {
20680             this.text = text;
20681             this.html = '';
20682         }
20683         if(this.rendered){
20684             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20685      
20686             this.el.update(String.format(
20687                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20688                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20689             this.parentMenu.autoWidth();
20690         }
20691     },
20692
20693     // private
20694     handleClick : function(e){
20695         if(!this.href){ // if no link defined, stop the event automatically
20696             e.stopEvent();
20697         }
20698         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20699     },
20700
20701     // private
20702     activate : function(autoExpand){
20703         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20704             this.focus();
20705             if(autoExpand){
20706                 this.expandMenu();
20707             }
20708         }
20709         return true;
20710     },
20711
20712     // private
20713     shouldDeactivate : function(e){
20714         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20715             if(this.menu && this.menu.isVisible()){
20716                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20717             }
20718             return true;
20719         }
20720         return false;
20721     },
20722
20723     // private
20724     deactivate : function(){
20725         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20726         this.hideMenu();
20727     },
20728
20729     // private
20730     expandMenu : function(autoActivate){
20731         if(!this.disabled && this.menu){
20732             clearTimeout(this.hideTimer);
20733             delete this.hideTimer;
20734             if(!this.menu.isVisible() && !this.showTimer){
20735                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20736             }else if (this.menu.isVisible() && autoActivate){
20737                 this.menu.tryActivate(0, 1);
20738             }
20739         }
20740     },
20741
20742     // private
20743     deferExpand : function(autoActivate){
20744         delete this.showTimer;
20745         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20746         if(autoActivate){
20747             this.menu.tryActivate(0, 1);
20748         }
20749     },
20750
20751     // private
20752     hideMenu : function(){
20753         clearTimeout(this.showTimer);
20754         delete this.showTimer;
20755         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20756             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20757         }
20758     },
20759
20760     // private
20761     deferHide : function(){
20762         delete this.hideTimer;
20763         this.menu.hide();
20764     }
20765 });/*
20766  * Based on:
20767  * Ext JS Library 1.1.1
20768  * Copyright(c) 2006-2007, Ext JS, LLC.
20769  *
20770  * Originally Released Under LGPL - original licence link has changed is not relivant.
20771  *
20772  * Fork - LGPL
20773  * <script type="text/javascript">
20774  */
20775  
20776 /**
20777  * @class Roo.menu.CheckItem
20778  * @extends Roo.menu.Item
20779  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20780  * @constructor
20781  * Creates a new CheckItem
20782  * @param {Object} config Configuration options
20783  */
20784 Roo.menu.CheckItem = function(config){
20785     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20786     this.addEvents({
20787         /**
20788          * @event beforecheckchange
20789          * Fires before the checked value is set, providing an opportunity to cancel if needed
20790          * @param {Roo.menu.CheckItem} this
20791          * @param {Boolean} checked The new checked value that will be set
20792          */
20793         "beforecheckchange" : true,
20794         /**
20795          * @event checkchange
20796          * Fires after the checked value has been set
20797          * @param {Roo.menu.CheckItem} this
20798          * @param {Boolean} checked The checked value that was set
20799          */
20800         "checkchange" : true
20801     });
20802     if(this.checkHandler){
20803         this.on('checkchange', this.checkHandler, this.scope);
20804     }
20805 };
20806 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20807     /**
20808      * @cfg {String} group
20809      * All check items with the same group name will automatically be grouped into a single-select
20810      * radio button group (defaults to '')
20811      */
20812     /**
20813      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20814      */
20815     itemCls : "x-menu-item x-menu-check-item",
20816     /**
20817      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20818      */
20819     groupClass : "x-menu-group-item",
20820
20821     /**
20822      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20823      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20824      * initialized with checked = true will be rendered as checked.
20825      */
20826     checked: false,
20827
20828     // private
20829     ctype: "Roo.menu.CheckItem",
20830
20831     // private
20832     onRender : function(c){
20833         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20834         if(this.group){
20835             this.el.addClass(this.groupClass);
20836         }
20837         Roo.menu.MenuMgr.registerCheckable(this);
20838         if(this.checked){
20839             this.checked = false;
20840             this.setChecked(true, true);
20841         }
20842     },
20843
20844     // private
20845     destroy : function(){
20846         if(this.rendered){
20847             Roo.menu.MenuMgr.unregisterCheckable(this);
20848         }
20849         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20850     },
20851
20852     /**
20853      * Set the checked state of this item
20854      * @param {Boolean} checked The new checked value
20855      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20856      */
20857     setChecked : function(state, suppressEvent){
20858         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20859             if(this.container){
20860                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20861             }
20862             this.checked = state;
20863             if(suppressEvent !== true){
20864                 this.fireEvent("checkchange", this, state);
20865             }
20866         }
20867     },
20868
20869     // private
20870     handleClick : function(e){
20871        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20872            this.setChecked(!this.checked);
20873        }
20874        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20875     }
20876 });/*
20877  * Based on:
20878  * Ext JS Library 1.1.1
20879  * Copyright(c) 2006-2007, Ext JS, LLC.
20880  *
20881  * Originally Released Under LGPL - original licence link has changed is not relivant.
20882  *
20883  * Fork - LGPL
20884  * <script type="text/javascript">
20885  */
20886  
20887 /**
20888  * @class Roo.menu.DateItem
20889  * @extends Roo.menu.Adapter
20890  * A menu item that wraps the {@link Roo.DatPicker} component.
20891  * @constructor
20892  * Creates a new DateItem
20893  * @param {Object} config Configuration options
20894  */
20895 Roo.menu.DateItem = function(config){
20896     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20897     /** The Roo.DatePicker object @type Roo.DatePicker */
20898     this.picker = this.component;
20899     this.addEvents({select: true});
20900     
20901     this.picker.on("render", function(picker){
20902         picker.getEl().swallowEvent("click");
20903         picker.container.addClass("x-menu-date-item");
20904     });
20905
20906     this.picker.on("select", this.onSelect, this);
20907 };
20908
20909 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20910     // private
20911     onSelect : function(picker, date){
20912         this.fireEvent("select", this, date, picker);
20913         Roo.menu.DateItem.superclass.handleClick.call(this);
20914     }
20915 });/*
20916  * Based on:
20917  * Ext JS Library 1.1.1
20918  * Copyright(c) 2006-2007, Ext JS, LLC.
20919  *
20920  * Originally Released Under LGPL - original licence link has changed is not relivant.
20921  *
20922  * Fork - LGPL
20923  * <script type="text/javascript">
20924  */
20925  
20926 /**
20927  * @class Roo.menu.ColorItem
20928  * @extends Roo.menu.Adapter
20929  * A menu item that wraps the {@link Roo.ColorPalette} component.
20930  * @constructor
20931  * Creates a new ColorItem
20932  * @param {Object} config Configuration options
20933  */
20934 Roo.menu.ColorItem = function(config){
20935     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20936     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20937     this.palette = this.component;
20938     this.relayEvents(this.palette, ["select"]);
20939     if(this.selectHandler){
20940         this.on('select', this.selectHandler, this.scope);
20941     }
20942 };
20943 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20944  * Based on:
20945  * Ext JS Library 1.1.1
20946  * Copyright(c) 2006-2007, Ext JS, LLC.
20947  *
20948  * Originally Released Under LGPL - original licence link has changed is not relivant.
20949  *
20950  * Fork - LGPL
20951  * <script type="text/javascript">
20952  */
20953  
20954
20955 /**
20956  * @class Roo.menu.DateMenu
20957  * @extends Roo.menu.Menu
20958  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20959  * @constructor
20960  * Creates a new DateMenu
20961  * @param {Object} config Configuration options
20962  */
20963 Roo.menu.DateMenu = function(config){
20964     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20965     this.plain = true;
20966     var di = new Roo.menu.DateItem(config);
20967     this.add(di);
20968     /**
20969      * The {@link Roo.DatePicker} instance for this DateMenu
20970      * @type DatePicker
20971      */
20972     this.picker = di.picker;
20973     /**
20974      * @event select
20975      * @param {DatePicker} picker
20976      * @param {Date} date
20977      */
20978     this.relayEvents(di, ["select"]);
20979
20980     this.on('beforeshow', function(){
20981         if(this.picker){
20982             this.picker.hideMonthPicker(true);
20983         }
20984     }, this);
20985 };
20986 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20987     cls:'x-date-menu'
20988 });/*
20989  * Based on:
20990  * Ext JS Library 1.1.1
20991  * Copyright(c) 2006-2007, Ext JS, LLC.
20992  *
20993  * Originally Released Under LGPL - original licence link has changed is not relivant.
20994  *
20995  * Fork - LGPL
20996  * <script type="text/javascript">
20997  */
20998  
20999
21000 /**
21001  * @class Roo.menu.ColorMenu
21002  * @extends Roo.menu.Menu
21003  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21004  * @constructor
21005  * Creates a new ColorMenu
21006  * @param {Object} config Configuration options
21007  */
21008 Roo.menu.ColorMenu = function(config){
21009     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21010     this.plain = true;
21011     var ci = new Roo.menu.ColorItem(config);
21012     this.add(ci);
21013     /**
21014      * The {@link Roo.ColorPalette} instance for this ColorMenu
21015      * @type ColorPalette
21016      */
21017     this.palette = ci.palette;
21018     /**
21019      * @event select
21020      * @param {ColorPalette} palette
21021      * @param {String} color
21022      */
21023     this.relayEvents(ci, ["select"]);
21024 };
21025 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21026  * Based on:
21027  * Ext JS Library 1.1.1
21028  * Copyright(c) 2006-2007, Ext JS, LLC.
21029  *
21030  * Originally Released Under LGPL - original licence link has changed is not relivant.
21031  *
21032  * Fork - LGPL
21033  * <script type="text/javascript">
21034  */
21035  
21036 /**
21037  * @class Roo.form.Field
21038  * @extends Roo.BoxComponent
21039  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21040  * @constructor
21041  * Creates a new Field
21042  * @param {Object} config Configuration options
21043  */
21044 Roo.form.Field = function(config){
21045     Roo.form.Field.superclass.constructor.call(this, config);
21046 };
21047
21048 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21049     /**
21050      * @cfg {String} fieldLabel Label to use when rendering a form.
21051      */
21052        /**
21053      * @cfg {String} qtip Mouse over tip
21054      */
21055      
21056     /**
21057      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21058      */
21059     invalidClass : "x-form-invalid",
21060     /**
21061      * @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")
21062      */
21063     invalidText : "The value in this field is invalid",
21064     /**
21065      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21066      */
21067     focusClass : "x-form-focus",
21068     /**
21069      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21070       automatic validation (defaults to "keyup").
21071      */
21072     validationEvent : "keyup",
21073     /**
21074      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21075      */
21076     validateOnBlur : true,
21077     /**
21078      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21079      */
21080     validationDelay : 250,
21081     /**
21082      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21083      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21084      */
21085     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21086     /**
21087      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21088      */
21089     fieldClass : "x-form-field",
21090     /**
21091      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21092      *<pre>
21093 Value         Description
21094 -----------   ----------------------------------------------------------------------
21095 qtip          Display a quick tip when the user hovers over the field
21096 title         Display a default browser title attribute popup
21097 under         Add a block div beneath the field containing the error text
21098 side          Add an error icon to the right of the field with a popup on hover
21099 [element id]  Add the error text directly to the innerHTML of the specified element
21100 </pre>
21101      */
21102     msgTarget : 'qtip',
21103     /**
21104      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21105      */
21106     msgFx : 'normal',
21107
21108     /**
21109      * @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.
21110      */
21111     readOnly : false,
21112
21113     /**
21114      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21115      */
21116     disabled : false,
21117
21118     /**
21119      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21120      */
21121     inputType : undefined,
21122     
21123     /**
21124      * @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).
21125          */
21126         tabIndex : undefined,
21127         
21128     // private
21129     isFormField : true,
21130
21131     // private
21132     hasFocus : false,
21133     /**
21134      * @property {Roo.Element} fieldEl
21135      * Element Containing the rendered Field (with label etc.)
21136      */
21137     /**
21138      * @cfg {Mixed} value A value to initialize this field with.
21139      */
21140     value : undefined,
21141
21142     /**
21143      * @cfg {String} name The field's HTML name attribute.
21144      */
21145     /**
21146      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21147      */
21148
21149         // private ??
21150         initComponent : function(){
21151         Roo.form.Field.superclass.initComponent.call(this);
21152         this.addEvents({
21153             /**
21154              * @event focus
21155              * Fires when this field receives input focus.
21156              * @param {Roo.form.Field} this
21157              */
21158             focus : true,
21159             /**
21160              * @event blur
21161              * Fires when this field loses input focus.
21162              * @param {Roo.form.Field} this
21163              */
21164             blur : true,
21165             /**
21166              * @event specialkey
21167              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21168              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21169              * @param {Roo.form.Field} this
21170              * @param {Roo.EventObject} e The event object
21171              */
21172             specialkey : true,
21173             /**
21174              * @event change
21175              * Fires just before the field blurs if the field value has changed.
21176              * @param {Roo.form.Field} this
21177              * @param {Mixed} newValue The new value
21178              * @param {Mixed} oldValue The original value
21179              */
21180             change : true,
21181             /**
21182              * @event invalid
21183              * Fires after the field has been marked as invalid.
21184              * @param {Roo.form.Field} this
21185              * @param {String} msg The validation message
21186              */
21187             invalid : true,
21188             /**
21189              * @event valid
21190              * Fires after the field has been validated with no errors.
21191              * @param {Roo.form.Field} this
21192              */
21193             valid : true,
21194              /**
21195              * @event keyup
21196              * Fires after the key up
21197              * @param {Roo.form.Field} this
21198              * @param {Roo.EventObject}  e The event Object
21199              */
21200             keyup : true
21201         });
21202     },
21203
21204     /**
21205      * Returns the name attribute of the field if available
21206      * @return {String} name The field name
21207      */
21208     getName: function(){
21209          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21210     },
21211
21212     // private
21213     onRender : function(ct, position){
21214         Roo.form.Field.superclass.onRender.call(this, ct, position);
21215         if(!this.el){
21216             var cfg = this.getAutoCreate();
21217             if(!cfg.name){
21218                 cfg.name = this.name || this.id;
21219             }
21220             if(this.inputType){
21221                 cfg.type = this.inputType;
21222             }
21223             this.el = ct.createChild(cfg, position);
21224         }
21225         var type = this.el.dom.type;
21226         if(type){
21227             if(type == 'password'){
21228                 type = 'text';
21229             }
21230             this.el.addClass('x-form-'+type);
21231         }
21232         if(this.readOnly){
21233             this.el.dom.readOnly = true;
21234         }
21235         if(this.tabIndex !== undefined){
21236             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21237         }
21238
21239         this.el.addClass([this.fieldClass, this.cls]);
21240         this.initValue();
21241     },
21242
21243     /**
21244      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21245      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21246      * @return {Roo.form.Field} this
21247      */
21248     applyTo : function(target){
21249         this.allowDomMove = false;
21250         this.el = Roo.get(target);
21251         this.render(this.el.dom.parentNode);
21252         return this;
21253     },
21254
21255     // private
21256     initValue : function(){
21257         if(this.value !== undefined){
21258             this.setValue(this.value);
21259         }else if(this.el.dom.value.length > 0){
21260             this.setValue(this.el.dom.value);
21261         }
21262     },
21263
21264     /**
21265      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21266      */
21267     isDirty : function() {
21268         if(this.disabled) {
21269             return false;
21270         }
21271         return String(this.getValue()) !== String(this.originalValue);
21272     },
21273
21274     // private
21275     afterRender : function(){
21276         Roo.form.Field.superclass.afterRender.call(this);
21277         this.initEvents();
21278     },
21279
21280     // private
21281     fireKey : function(e){
21282         //Roo.log('field ' + e.getKey());
21283         if(e.isNavKeyPress()){
21284             this.fireEvent("specialkey", this, e);
21285         }
21286     },
21287
21288     /**
21289      * Resets the current field value to the originally loaded value and clears any validation messages
21290      */
21291     reset : function(){
21292         this.setValue(this.originalValue);
21293         this.clearInvalid();
21294     },
21295
21296     // private
21297     initEvents : function(){
21298         // safari killled keypress - so keydown is now used..
21299         this.el.on("keydown" , this.fireKey,  this);
21300         this.el.on("focus", this.onFocus,  this);
21301         this.el.on("blur", this.onBlur,  this);
21302         this.el.relayEvent('keyup', this);
21303
21304         // reference to original value for reset
21305         this.originalValue = this.getValue();
21306     },
21307
21308     // private
21309     onFocus : function(){
21310         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21311             this.el.addClass(this.focusClass);
21312         }
21313         if(!this.hasFocus){
21314             this.hasFocus = true;
21315             this.startValue = this.getValue();
21316             this.fireEvent("focus", this);
21317         }
21318     },
21319
21320     beforeBlur : Roo.emptyFn,
21321
21322     // private
21323     onBlur : function(){
21324         this.beforeBlur();
21325         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21326             this.el.removeClass(this.focusClass);
21327         }
21328         this.hasFocus = false;
21329         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21330             this.validate();
21331         }
21332         var v = this.getValue();
21333         if(String(v) !== String(this.startValue)){
21334             this.fireEvent('change', this, v, this.startValue);
21335         }
21336         this.fireEvent("blur", this);
21337     },
21338
21339     /**
21340      * Returns whether or not the field value is currently valid
21341      * @param {Boolean} preventMark True to disable marking the field invalid
21342      * @return {Boolean} True if the value is valid, else false
21343      */
21344     isValid : function(preventMark){
21345         if(this.disabled){
21346             return true;
21347         }
21348         var restore = this.preventMark;
21349         this.preventMark = preventMark === true;
21350         var v = this.validateValue(this.processValue(this.getRawValue()));
21351         this.preventMark = restore;
21352         return v;
21353     },
21354
21355     /**
21356      * Validates the field value
21357      * @return {Boolean} True if the value is valid, else false
21358      */
21359     validate : function(){
21360         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21361             this.clearInvalid();
21362             return true;
21363         }
21364         return false;
21365     },
21366
21367     processValue : function(value){
21368         return value;
21369     },
21370
21371     // private
21372     // Subclasses should provide the validation implementation by overriding this
21373     validateValue : function(value){
21374         return true;
21375     },
21376
21377     /**
21378      * Mark this field as invalid
21379      * @param {String} msg The validation message
21380      */
21381     markInvalid : function(msg){
21382         if(!this.rendered || this.preventMark){ // not rendered
21383             return;
21384         }
21385         this.el.addClass(this.invalidClass);
21386         msg = msg || this.invalidText;
21387         switch(this.msgTarget){
21388             case 'qtip':
21389                 this.el.dom.qtip = msg;
21390                 this.el.dom.qclass = 'x-form-invalid-tip';
21391                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21392                     Roo.QuickTips.enable();
21393                 }
21394                 break;
21395             case 'title':
21396                 this.el.dom.title = msg;
21397                 break;
21398             case 'under':
21399                 if(!this.errorEl){
21400                     var elp = this.el.findParent('.x-form-element', 5, true);
21401                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21402                     this.errorEl.setWidth(elp.getWidth(true)-20);
21403                 }
21404                 this.errorEl.update(msg);
21405                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21406                 break;
21407             case 'side':
21408                 if(!this.errorIcon){
21409                     var elp = this.el.findParent('.x-form-element', 5, true);
21410                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21411                 }
21412                 this.alignErrorIcon();
21413                 this.errorIcon.dom.qtip = msg;
21414                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21415                 this.errorIcon.show();
21416                 this.on('resize', this.alignErrorIcon, this);
21417                 break;
21418             default:
21419                 var t = Roo.getDom(this.msgTarget);
21420                 t.innerHTML = msg;
21421                 t.style.display = this.msgDisplay;
21422                 break;
21423         }
21424         this.fireEvent('invalid', this, msg);
21425     },
21426
21427     // private
21428     alignErrorIcon : function(){
21429         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21430     },
21431
21432     /**
21433      * Clear any invalid styles/messages for this field
21434      */
21435     clearInvalid : function(){
21436         if(!this.rendered || this.preventMark){ // not rendered
21437             return;
21438         }
21439         this.el.removeClass(this.invalidClass);
21440         switch(this.msgTarget){
21441             case 'qtip':
21442                 this.el.dom.qtip = '';
21443                 break;
21444             case 'title':
21445                 this.el.dom.title = '';
21446                 break;
21447             case 'under':
21448                 if(this.errorEl){
21449                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21450                 }
21451                 break;
21452             case 'side':
21453                 if(this.errorIcon){
21454                     this.errorIcon.dom.qtip = '';
21455                     this.errorIcon.hide();
21456                     this.un('resize', this.alignErrorIcon, this);
21457                 }
21458                 break;
21459             default:
21460                 var t = Roo.getDom(this.msgTarget);
21461                 t.innerHTML = '';
21462                 t.style.display = 'none';
21463                 break;
21464         }
21465         this.fireEvent('valid', this);
21466     },
21467
21468     /**
21469      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21470      * @return {Mixed} value The field value
21471      */
21472     getRawValue : function(){
21473         var v = this.el.getValue();
21474         if(v === this.emptyText){
21475             v = '';
21476         }
21477         return v;
21478     },
21479
21480     /**
21481      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21482      * @return {Mixed} value The field value
21483      */
21484     getValue : function(){
21485         var v = this.el.getValue();
21486         if(v === this.emptyText || v === undefined){
21487             v = '';
21488         }
21489         return v;
21490     },
21491
21492     /**
21493      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21494      * @param {Mixed} value The value to set
21495      */
21496     setRawValue : function(v){
21497         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21498     },
21499
21500     /**
21501      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21502      * @param {Mixed} value The value to set
21503      */
21504     setValue : function(v){
21505         this.value = v;
21506         if(this.rendered){
21507             this.el.dom.value = (v === null || v === undefined ? '' : v);
21508              this.validate();
21509         }
21510     },
21511
21512     adjustSize : function(w, h){
21513         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21514         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21515         return s;
21516     },
21517
21518     adjustWidth : function(tag, w){
21519         tag = tag.toLowerCase();
21520         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21521             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21522                 if(tag == 'input'){
21523                     return w + 2;
21524                 }
21525                 if(tag = 'textarea'){
21526                     return w-2;
21527                 }
21528             }else if(Roo.isOpera){
21529                 if(tag == 'input'){
21530                     return w + 2;
21531                 }
21532                 if(tag = 'textarea'){
21533                     return w-2;
21534                 }
21535             }
21536         }
21537         return w;
21538     }
21539 });
21540
21541
21542 // anything other than normal should be considered experimental
21543 Roo.form.Field.msgFx = {
21544     normal : {
21545         show: function(msgEl, f){
21546             msgEl.setDisplayed('block');
21547         },
21548
21549         hide : function(msgEl, f){
21550             msgEl.setDisplayed(false).update('');
21551         }
21552     },
21553
21554     slide : {
21555         show: function(msgEl, f){
21556             msgEl.slideIn('t', {stopFx:true});
21557         },
21558
21559         hide : function(msgEl, f){
21560             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21561         }
21562     },
21563
21564     slideRight : {
21565         show: function(msgEl, f){
21566             msgEl.fixDisplay();
21567             msgEl.alignTo(f.el, 'tl-tr');
21568             msgEl.slideIn('l', {stopFx:true});
21569         },
21570
21571         hide : function(msgEl, f){
21572             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21573         }
21574     }
21575 };/*
21576  * Based on:
21577  * Ext JS Library 1.1.1
21578  * Copyright(c) 2006-2007, Ext JS, LLC.
21579  *
21580  * Originally Released Under LGPL - original licence link has changed is not relivant.
21581  *
21582  * Fork - LGPL
21583  * <script type="text/javascript">
21584  */
21585  
21586
21587 /**
21588  * @class Roo.form.TextField
21589  * @extends Roo.form.Field
21590  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21591  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21592  * @constructor
21593  * Creates a new TextField
21594  * @param {Object} config Configuration options
21595  */
21596 Roo.form.TextField = function(config){
21597     Roo.form.TextField.superclass.constructor.call(this, config);
21598     this.addEvents({
21599         /**
21600          * @event autosize
21601          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21602          * according to the default logic, but this event provides a hook for the developer to apply additional
21603          * logic at runtime to resize the field if needed.
21604              * @param {Roo.form.Field} this This text field
21605              * @param {Number} width The new field width
21606              */
21607         autosize : true
21608     });
21609 };
21610
21611 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21612     /**
21613      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21614      */
21615     grow : false,
21616     /**
21617      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21618      */
21619     growMin : 30,
21620     /**
21621      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21622      */
21623     growMax : 800,
21624     /**
21625      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21626      */
21627     vtype : null,
21628     /**
21629      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21630      */
21631     maskRe : null,
21632     /**
21633      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21634      */
21635     disableKeyFilter : false,
21636     /**
21637      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21638      */
21639     allowBlank : true,
21640     /**
21641      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21642      */
21643     minLength : 0,
21644     /**
21645      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21646      */
21647     maxLength : Number.MAX_VALUE,
21648     /**
21649      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21650      */
21651     minLengthText : "The minimum length for this field is {0}",
21652     /**
21653      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21654      */
21655     maxLengthText : "The maximum length for this field is {0}",
21656     /**
21657      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21658      */
21659     selectOnFocus : false,
21660     /**
21661      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21662      */
21663     blankText : "This field is required",
21664     /**
21665      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21666      * If available, this function will be called only after the basic validators all return true, and will be passed the
21667      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21668      */
21669     validator : null,
21670     /**
21671      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21672      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21673      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21674      */
21675     regex : null,
21676     /**
21677      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21678      */
21679     regexText : "",
21680     /**
21681      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21682      */
21683     emptyText : null,
21684     /**
21685      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21686      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21687      */
21688     emptyClass : 'x-form-empty-field',
21689
21690     // private
21691     initEvents : function(){
21692         Roo.form.TextField.superclass.initEvents.call(this);
21693         if(this.validationEvent == 'keyup'){
21694             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21695             this.el.on('keyup', this.filterValidation, this);
21696         }
21697         else if(this.validationEvent !== false){
21698             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21699         }
21700         if(this.selectOnFocus || this.emptyText){
21701             this.on("focus", this.preFocus, this);
21702             if(this.emptyText){
21703                 this.on('blur', this.postBlur, this);
21704                 this.applyEmptyText();
21705             }
21706         }
21707         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21708             this.el.on("keypress", this.filterKeys, this);
21709         }
21710         if(this.grow){
21711             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21712             this.el.on("click", this.autoSize,  this);
21713         }
21714     },
21715
21716     processValue : function(value){
21717         if(this.stripCharsRe){
21718             var newValue = value.replace(this.stripCharsRe, '');
21719             if(newValue !== value){
21720                 this.setRawValue(newValue);
21721                 return newValue;
21722             }
21723         }
21724         return value;
21725     },
21726
21727     filterValidation : function(e){
21728         if(!e.isNavKeyPress()){
21729             this.validationTask.delay(this.validationDelay);
21730         }
21731     },
21732
21733     // private
21734     onKeyUp : function(e){
21735         if(!e.isNavKeyPress()){
21736             this.autoSize();
21737         }
21738     },
21739
21740     /**
21741      * Resets the current field value to the originally-loaded value and clears any validation messages.
21742      * Also adds emptyText and emptyClass if the original value was blank.
21743      */
21744     reset : function(){
21745         Roo.form.TextField.superclass.reset.call(this);
21746         this.applyEmptyText();
21747     },
21748
21749     applyEmptyText : function(){
21750         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21751             this.setRawValue(this.emptyText);
21752             this.el.addClass(this.emptyClass);
21753         }
21754     },
21755
21756     // private
21757     preFocus : function(){
21758         if(this.emptyText){
21759             if(this.el.dom.value == this.emptyText){
21760                 this.setRawValue('');
21761             }
21762             this.el.removeClass(this.emptyClass);
21763         }
21764         if(this.selectOnFocus){
21765             this.el.dom.select();
21766         }
21767     },
21768
21769     // private
21770     postBlur : function(){
21771         this.applyEmptyText();
21772     },
21773
21774     // private
21775     filterKeys : function(e){
21776         var k = e.getKey();
21777         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21778             return;
21779         }
21780         var c = e.getCharCode(), cc = String.fromCharCode(c);
21781         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21782             return;
21783         }
21784         if(!this.maskRe.test(cc)){
21785             e.stopEvent();
21786         }
21787     },
21788
21789     setValue : function(v){
21790         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21791             this.el.removeClass(this.emptyClass);
21792         }
21793         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21794         this.applyEmptyText();
21795         this.autoSize();
21796     },
21797
21798     /**
21799      * Validates a value according to the field's validation rules and marks the field as invalid
21800      * if the validation fails
21801      * @param {Mixed} value The value to validate
21802      * @return {Boolean} True if the value is valid, else false
21803      */
21804     validateValue : function(value){
21805         if(value.length < 1 || value === this.emptyText){ // if it's blank
21806              if(this.allowBlank){
21807                 this.clearInvalid();
21808                 return true;
21809              }else{
21810                 this.markInvalid(this.blankText);
21811                 return false;
21812              }
21813         }
21814         if(value.length < this.minLength){
21815             this.markInvalid(String.format(this.minLengthText, this.minLength));
21816             return false;
21817         }
21818         if(value.length > this.maxLength){
21819             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21820             return false;
21821         }
21822         if(this.vtype){
21823             var vt = Roo.form.VTypes;
21824             if(!vt[this.vtype](value, this)){
21825                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21826                 return false;
21827             }
21828         }
21829         if(typeof this.validator == "function"){
21830             var msg = this.validator(value);
21831             if(msg !== true){
21832                 this.markInvalid(msg);
21833                 return false;
21834             }
21835         }
21836         if(this.regex && !this.regex.test(value)){
21837             this.markInvalid(this.regexText);
21838             return false;
21839         }
21840         return true;
21841     },
21842
21843     /**
21844      * Selects text in this field
21845      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21846      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21847      */
21848     selectText : function(start, end){
21849         var v = this.getRawValue();
21850         if(v.length > 0){
21851             start = start === undefined ? 0 : start;
21852             end = end === undefined ? v.length : end;
21853             var d = this.el.dom;
21854             if(d.setSelectionRange){
21855                 d.setSelectionRange(start, end);
21856             }else if(d.createTextRange){
21857                 var range = d.createTextRange();
21858                 range.moveStart("character", start);
21859                 range.moveEnd("character", v.length-end);
21860                 range.select();
21861             }
21862         }
21863     },
21864
21865     /**
21866      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21867      * This only takes effect if grow = true, and fires the autosize event.
21868      */
21869     autoSize : function(){
21870         if(!this.grow || !this.rendered){
21871             return;
21872         }
21873         if(!this.metrics){
21874             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21875         }
21876         var el = this.el;
21877         var v = el.dom.value;
21878         var d = document.createElement('div');
21879         d.appendChild(document.createTextNode(v));
21880         v = d.innerHTML;
21881         d = null;
21882         v += "&#160;";
21883         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21884         this.el.setWidth(w);
21885         this.fireEvent("autosize", this, w);
21886     }
21887 });/*
21888  * Based on:
21889  * Ext JS Library 1.1.1
21890  * Copyright(c) 2006-2007, Ext JS, LLC.
21891  *
21892  * Originally Released Under LGPL - original licence link has changed is not relivant.
21893  *
21894  * Fork - LGPL
21895  * <script type="text/javascript">
21896  */
21897  
21898 /**
21899  * @class Roo.form.Hidden
21900  * @extends Roo.form.TextField
21901  * Simple Hidden element used on forms 
21902  * 
21903  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21904  * 
21905  * @constructor
21906  * Creates a new Hidden form element.
21907  * @param {Object} config Configuration options
21908  */
21909
21910
21911
21912 // easy hidden field...
21913 Roo.form.Hidden = function(config){
21914     Roo.form.Hidden.superclass.constructor.call(this, config);
21915 };
21916   
21917 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21918     fieldLabel:      '',
21919     inputType:      'hidden',
21920     width:          50,
21921     allowBlank:     true,
21922     labelSeparator: '',
21923     hidden:         true,
21924     itemCls :       'x-form-item-display-none'
21925
21926
21927 });
21928
21929
21930 /*
21931  * Based on:
21932  * Ext JS Library 1.1.1
21933  * Copyright(c) 2006-2007, Ext JS, LLC.
21934  *
21935  * Originally Released Under LGPL - original licence link has changed is not relivant.
21936  *
21937  * Fork - LGPL
21938  * <script type="text/javascript">
21939  */
21940  
21941 /**
21942  * @class Roo.form.TriggerField
21943  * @extends Roo.form.TextField
21944  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21945  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21946  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21947  * for which you can provide a custom implementation.  For example:
21948  * <pre><code>
21949 var trigger = new Roo.form.TriggerField();
21950 trigger.onTriggerClick = myTriggerFn;
21951 trigger.applyTo('my-field');
21952 </code></pre>
21953  *
21954  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21955  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21956  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21957  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21958  * @constructor
21959  * Create a new TriggerField.
21960  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21961  * to the base TextField)
21962  */
21963 Roo.form.TriggerField = function(config){
21964     this.mimicing = false;
21965     Roo.form.TriggerField.superclass.constructor.call(this, config);
21966 };
21967
21968 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21969     /**
21970      * @cfg {String} triggerClass A CSS class to apply to the trigger
21971      */
21972     /**
21973      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21974      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21975      */
21976     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21977     /**
21978      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21979      */
21980     hideTrigger:false,
21981
21982     /** @cfg {Boolean} grow @hide */
21983     /** @cfg {Number} growMin @hide */
21984     /** @cfg {Number} growMax @hide */
21985
21986     /**
21987      * @hide 
21988      * @method
21989      */
21990     autoSize: Roo.emptyFn,
21991     // private
21992     monitorTab : true,
21993     // private
21994     deferHeight : true,
21995
21996     
21997     actionMode : 'wrap',
21998     // private
21999     onResize : function(w, h){
22000         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22001         if(typeof w == 'number'){
22002             var x = w - this.trigger.getWidth();
22003             this.el.setWidth(this.adjustWidth('input', x));
22004             this.trigger.setStyle('left', x+'px');
22005         }
22006     },
22007
22008     // private
22009     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22010
22011     // private
22012     getResizeEl : function(){
22013         return this.wrap;
22014     },
22015
22016     // private
22017     getPositionEl : function(){
22018         return this.wrap;
22019     },
22020
22021     // private
22022     alignErrorIcon : function(){
22023         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22024     },
22025
22026     // private
22027     onRender : function(ct, position){
22028         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22029         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22030         this.trigger = this.wrap.createChild(this.triggerConfig ||
22031                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22032         if(this.hideTrigger){
22033             this.trigger.setDisplayed(false);
22034         }
22035         this.initTrigger();
22036         if(!this.width){
22037             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22038         }
22039     },
22040
22041     // private
22042     initTrigger : function(){
22043         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22044         this.trigger.addClassOnOver('x-form-trigger-over');
22045         this.trigger.addClassOnClick('x-form-trigger-click');
22046     },
22047
22048     // private
22049     onDestroy : function(){
22050         if(this.trigger){
22051             this.trigger.removeAllListeners();
22052             this.trigger.remove();
22053         }
22054         if(this.wrap){
22055             this.wrap.remove();
22056         }
22057         Roo.form.TriggerField.superclass.onDestroy.call(this);
22058     },
22059
22060     // private
22061     onFocus : function(){
22062         Roo.form.TriggerField.superclass.onFocus.call(this);
22063         if(!this.mimicing){
22064             this.wrap.addClass('x-trigger-wrap-focus');
22065             this.mimicing = true;
22066             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22067             if(this.monitorTab){
22068                 this.el.on("keydown", this.checkTab, this);
22069             }
22070         }
22071     },
22072
22073     // private
22074     checkTab : function(e){
22075         if(e.getKey() == e.TAB){
22076             this.triggerBlur();
22077         }
22078     },
22079
22080     // private
22081     onBlur : function(){
22082         // do nothing
22083     },
22084
22085     // private
22086     mimicBlur : function(e, t){
22087         if(!this.wrap.contains(t) && this.validateBlur()){
22088             this.triggerBlur();
22089         }
22090     },
22091
22092     // private
22093     triggerBlur : function(){
22094         this.mimicing = false;
22095         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22096         if(this.monitorTab){
22097             this.el.un("keydown", this.checkTab, this);
22098         }
22099         this.wrap.removeClass('x-trigger-wrap-focus');
22100         Roo.form.TriggerField.superclass.onBlur.call(this);
22101     },
22102
22103     // private
22104     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22105     validateBlur : function(e, t){
22106         return true;
22107     },
22108
22109     // private
22110     onDisable : function(){
22111         Roo.form.TriggerField.superclass.onDisable.call(this);
22112         if(this.wrap){
22113             this.wrap.addClass('x-item-disabled');
22114         }
22115     },
22116
22117     // private
22118     onEnable : function(){
22119         Roo.form.TriggerField.superclass.onEnable.call(this);
22120         if(this.wrap){
22121             this.wrap.removeClass('x-item-disabled');
22122         }
22123     },
22124
22125     // private
22126     onShow : function(){
22127         var ae = this.getActionEl();
22128         
22129         if(ae){
22130             ae.dom.style.display = '';
22131             ae.dom.style.visibility = 'visible';
22132         }
22133     },
22134
22135     // private
22136     
22137     onHide : function(){
22138         var ae = this.getActionEl();
22139         ae.dom.style.display = 'none';
22140     },
22141
22142     /**
22143      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22144      * by an implementing function.
22145      * @method
22146      * @param {EventObject} e
22147      */
22148     onTriggerClick : Roo.emptyFn
22149 });
22150
22151 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22152 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22153 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22154 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22155     initComponent : function(){
22156         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22157
22158         this.triggerConfig = {
22159             tag:'span', cls:'x-form-twin-triggers', cn:[
22160             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22161             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22162         ]};
22163     },
22164
22165     getTrigger : function(index){
22166         return this.triggers[index];
22167     },
22168
22169     initTrigger : function(){
22170         var ts = this.trigger.select('.x-form-trigger', true);
22171         this.wrap.setStyle('overflow', 'hidden');
22172         var triggerField = this;
22173         ts.each(function(t, all, index){
22174             t.hide = function(){
22175                 var w = triggerField.wrap.getWidth();
22176                 this.dom.style.display = 'none';
22177                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22178             };
22179             t.show = function(){
22180                 var w = triggerField.wrap.getWidth();
22181                 this.dom.style.display = '';
22182                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22183             };
22184             var triggerIndex = 'Trigger'+(index+1);
22185
22186             if(this['hide'+triggerIndex]){
22187                 t.dom.style.display = 'none';
22188             }
22189             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22190             t.addClassOnOver('x-form-trigger-over');
22191             t.addClassOnClick('x-form-trigger-click');
22192         }, this);
22193         this.triggers = ts.elements;
22194     },
22195
22196     onTrigger1Click : Roo.emptyFn,
22197     onTrigger2Click : Roo.emptyFn
22198 });/*
22199  * Based on:
22200  * Ext JS Library 1.1.1
22201  * Copyright(c) 2006-2007, Ext JS, LLC.
22202  *
22203  * Originally Released Under LGPL - original licence link has changed is not relivant.
22204  *
22205  * Fork - LGPL
22206  * <script type="text/javascript">
22207  */
22208  
22209 /**
22210  * @class Roo.form.TextArea
22211  * @extends Roo.form.TextField
22212  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22213  * support for auto-sizing.
22214  * @constructor
22215  * Creates a new TextArea
22216  * @param {Object} config Configuration options
22217  */
22218 Roo.form.TextArea = function(config){
22219     Roo.form.TextArea.superclass.constructor.call(this, config);
22220     // these are provided exchanges for backwards compat
22221     // minHeight/maxHeight were replaced by growMin/growMax to be
22222     // compatible with TextField growing config values
22223     if(this.minHeight !== undefined){
22224         this.growMin = this.minHeight;
22225     }
22226     if(this.maxHeight !== undefined){
22227         this.growMax = this.maxHeight;
22228     }
22229 };
22230
22231 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22232     /**
22233      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22234      */
22235     growMin : 60,
22236     /**
22237      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22238      */
22239     growMax: 1000,
22240     /**
22241      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22242      * in the field (equivalent to setting overflow: hidden, defaults to false)
22243      */
22244     preventScrollbars: false,
22245     /**
22246      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22247      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22248      */
22249
22250     // private
22251     onRender : function(ct, position){
22252         if(!this.el){
22253             this.defaultAutoCreate = {
22254                 tag: "textarea",
22255                 style:"width:300px;height:60px;",
22256                 autocomplete: "off"
22257             };
22258         }
22259         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22260         if(this.grow){
22261             this.textSizeEl = Roo.DomHelper.append(document.body, {
22262                 tag: "pre", cls: "x-form-grow-sizer"
22263             });
22264             if(this.preventScrollbars){
22265                 this.el.setStyle("overflow", "hidden");
22266             }
22267             this.el.setHeight(this.growMin);
22268         }
22269     },
22270
22271     onDestroy : function(){
22272         if(this.textSizeEl){
22273             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22274         }
22275         Roo.form.TextArea.superclass.onDestroy.call(this);
22276     },
22277
22278     // private
22279     onKeyUp : function(e){
22280         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22281             this.autoSize();
22282         }
22283     },
22284
22285     /**
22286      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22287      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22288      */
22289     autoSize : function(){
22290         if(!this.grow || !this.textSizeEl){
22291             return;
22292         }
22293         var el = this.el;
22294         var v = el.dom.value;
22295         var ts = this.textSizeEl;
22296
22297         ts.innerHTML = '';
22298         ts.appendChild(document.createTextNode(v));
22299         v = ts.innerHTML;
22300
22301         Roo.fly(ts).setWidth(this.el.getWidth());
22302         if(v.length < 1){
22303             v = "&#160;&#160;";
22304         }else{
22305             if(Roo.isIE){
22306                 v = v.replace(/\n/g, '<p>&#160;</p>');
22307             }
22308             v += "&#160;\n&#160;";
22309         }
22310         ts.innerHTML = v;
22311         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22312         if(h != this.lastHeight){
22313             this.lastHeight = h;
22314             this.el.setHeight(h);
22315             this.fireEvent("autosize", this, h);
22316         }
22317     }
22318 });/*
22319  * Based on:
22320  * Ext JS Library 1.1.1
22321  * Copyright(c) 2006-2007, Ext JS, LLC.
22322  *
22323  * Originally Released Under LGPL - original licence link has changed is not relivant.
22324  *
22325  * Fork - LGPL
22326  * <script type="text/javascript">
22327  */
22328  
22329
22330 /**
22331  * @class Roo.form.NumberField
22332  * @extends Roo.form.TextField
22333  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22334  * @constructor
22335  * Creates a new NumberField
22336  * @param {Object} config Configuration options
22337  */
22338 Roo.form.NumberField = function(config){
22339     Roo.form.NumberField.superclass.constructor.call(this, config);
22340 };
22341
22342 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22343     /**
22344      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22345      */
22346     fieldClass: "x-form-field x-form-num-field",
22347     /**
22348      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22349      */
22350     allowDecimals : true,
22351     /**
22352      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22353      */
22354     decimalSeparator : ".",
22355     /**
22356      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22357      */
22358     decimalPrecision : 2,
22359     /**
22360      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22361      */
22362     allowNegative : true,
22363     /**
22364      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22365      */
22366     minValue : Number.NEGATIVE_INFINITY,
22367     /**
22368      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22369      */
22370     maxValue : Number.MAX_VALUE,
22371     /**
22372      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22373      */
22374     minText : "The minimum value for this field is {0}",
22375     /**
22376      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22377      */
22378     maxText : "The maximum value for this field is {0}",
22379     /**
22380      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22381      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22382      */
22383     nanText : "{0} is not a valid number",
22384
22385     // private
22386     initEvents : function(){
22387         Roo.form.NumberField.superclass.initEvents.call(this);
22388         var allowed = "0123456789";
22389         if(this.allowDecimals){
22390             allowed += this.decimalSeparator;
22391         }
22392         if(this.allowNegative){
22393             allowed += "-";
22394         }
22395         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22396         var keyPress = function(e){
22397             var k = e.getKey();
22398             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22399                 return;
22400             }
22401             var c = e.getCharCode();
22402             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22403                 e.stopEvent();
22404             }
22405         };
22406         this.el.on("keypress", keyPress, this);
22407     },
22408
22409     // private
22410     validateValue : function(value){
22411         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22412             return false;
22413         }
22414         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22415              return true;
22416         }
22417         var num = this.parseValue(value);
22418         if(isNaN(num)){
22419             this.markInvalid(String.format(this.nanText, value));
22420             return false;
22421         }
22422         if(num < this.minValue){
22423             this.markInvalid(String.format(this.minText, this.minValue));
22424             return false;
22425         }
22426         if(num > this.maxValue){
22427             this.markInvalid(String.format(this.maxText, this.maxValue));
22428             return false;
22429         }
22430         return true;
22431     },
22432
22433     getValue : function(){
22434         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22435     },
22436
22437     // private
22438     parseValue : function(value){
22439         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22440         return isNaN(value) ? '' : value;
22441     },
22442
22443     // private
22444     fixPrecision : function(value){
22445         var nan = isNaN(value);
22446         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22447             return nan ? '' : value;
22448         }
22449         return parseFloat(value).toFixed(this.decimalPrecision);
22450     },
22451
22452     setValue : function(v){
22453         v = this.fixPrecision(v);
22454         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22455     },
22456
22457     // private
22458     decimalPrecisionFcn : function(v){
22459         return Math.floor(v);
22460     },
22461
22462     beforeBlur : function(){
22463         var v = this.parseValue(this.getRawValue());
22464         if(v){
22465             this.setValue(v);
22466         }
22467     }
22468 });/*
22469  * Based on:
22470  * Ext JS Library 1.1.1
22471  * Copyright(c) 2006-2007, Ext JS, LLC.
22472  *
22473  * Originally Released Under LGPL - original licence link has changed is not relivant.
22474  *
22475  * Fork - LGPL
22476  * <script type="text/javascript">
22477  */
22478  
22479 /**
22480  * @class Roo.form.DateField
22481  * @extends Roo.form.TriggerField
22482  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22483 * @constructor
22484 * Create a new DateField
22485 * @param {Object} config
22486  */
22487 Roo.form.DateField = function(config){
22488     Roo.form.DateField.superclass.constructor.call(this, config);
22489     
22490       this.addEvents({
22491          
22492         /**
22493          * @event select
22494          * Fires when a date is selected
22495              * @param {Roo.form.DateField} combo This combo box
22496              * @param {Date} date The date selected
22497              */
22498         'select' : true
22499          
22500     });
22501     
22502     
22503     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22504     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22505     this.ddMatch = null;
22506     if(this.disabledDates){
22507         var dd = this.disabledDates;
22508         var re = "(?:";
22509         for(var i = 0; i < dd.length; i++){
22510             re += dd[i];
22511             if(i != dd.length-1) re += "|";
22512         }
22513         this.ddMatch = new RegExp(re + ")");
22514     }
22515 };
22516
22517 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22518     /**
22519      * @cfg {String} format
22520      * The default date format string which can be overriden for localization support.  The format must be
22521      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22522      */
22523     format : "m/d/y",
22524     /**
22525      * @cfg {String} altFormats
22526      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22527      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22528      */
22529     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22530     /**
22531      * @cfg {Array} disabledDays
22532      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22533      */
22534     disabledDays : null,
22535     /**
22536      * @cfg {String} disabledDaysText
22537      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22538      */
22539     disabledDaysText : "Disabled",
22540     /**
22541      * @cfg {Array} disabledDates
22542      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22543      * expression so they are very powerful. Some examples:
22544      * <ul>
22545      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22546      * <li>["03/08", "09/16"] would disable those days for every year</li>
22547      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22548      * <li>["03/../2006"] would disable every day in March 2006</li>
22549      * <li>["^03"] would disable every day in every March</li>
22550      * </ul>
22551      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22552      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22553      */
22554     disabledDates : null,
22555     /**
22556      * @cfg {String} disabledDatesText
22557      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22558      */
22559     disabledDatesText : "Disabled",
22560     /**
22561      * @cfg {Date/String} minValue
22562      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22563      * valid format (defaults to null).
22564      */
22565     minValue : null,
22566     /**
22567      * @cfg {Date/String} maxValue
22568      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22569      * valid format (defaults to null).
22570      */
22571     maxValue : null,
22572     /**
22573      * @cfg {String} minText
22574      * The error text to display when the date in the cell is before minValue (defaults to
22575      * 'The date in this field must be after {minValue}').
22576      */
22577     minText : "The date in this field must be equal to or after {0}",
22578     /**
22579      * @cfg {String} maxText
22580      * The error text to display when the date in the cell is after maxValue (defaults to
22581      * 'The date in this field must be before {maxValue}').
22582      */
22583     maxText : "The date in this field must be equal to or before {0}",
22584     /**
22585      * @cfg {String} invalidText
22586      * The error text to display when the date in the field is invalid (defaults to
22587      * '{value} is not a valid date - it must be in the format {format}').
22588      */
22589     invalidText : "{0} is not a valid date - it must be in the format {1}",
22590     /**
22591      * @cfg {String} triggerClass
22592      * An additional CSS class used to style the trigger button.  The trigger will always get the
22593      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22594      * which displays a calendar icon).
22595      */
22596     triggerClass : 'x-form-date-trigger',
22597     
22598
22599     /**
22600      * @cfg {bool} useIso
22601      * if enabled, then the date field will use a hidden field to store the 
22602      * real value as iso formated date. default (false)
22603      */ 
22604     useIso : false,
22605     /**
22606      * @cfg {String/Object} autoCreate
22607      * A DomHelper element spec, or true for a default element spec (defaults to
22608      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22609      */ 
22610     // private
22611     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22612     
22613     // private
22614     hiddenField: false,
22615     
22616     onRender : function(ct, position)
22617     {
22618         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22619         if (this.useIso) {
22620             this.el.dom.removeAttribute('name'); 
22621             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22622                     'before', true);
22623             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22624             // prevent input submission
22625             this.hiddenName = this.name;
22626         }
22627             
22628             
22629     },
22630     
22631     // private
22632     validateValue : function(value)
22633     {
22634         value = this.formatDate(value);
22635         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22636             return false;
22637         }
22638         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22639              return true;
22640         }
22641         var svalue = value;
22642         value = this.parseDate(value);
22643         if(!value){
22644             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22645             return false;
22646         }
22647         var time = value.getTime();
22648         if(this.minValue && time < this.minValue.getTime()){
22649             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22650             return false;
22651         }
22652         if(this.maxValue && time > this.maxValue.getTime()){
22653             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22654             return false;
22655         }
22656         if(this.disabledDays){
22657             var day = value.getDay();
22658             for(var i = 0; i < this.disabledDays.length; i++) {
22659                 if(day === this.disabledDays[i]){
22660                     this.markInvalid(this.disabledDaysText);
22661                     return false;
22662                 }
22663             }
22664         }
22665         var fvalue = this.formatDate(value);
22666         if(this.ddMatch && this.ddMatch.test(fvalue)){
22667             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22668             return false;
22669         }
22670         return true;
22671     },
22672
22673     // private
22674     // Provides logic to override the default TriggerField.validateBlur which just returns true
22675     validateBlur : function(){
22676         return !this.menu || !this.menu.isVisible();
22677     },
22678
22679     /**
22680      * Returns the current date value of the date field.
22681      * @return {Date} The date value
22682      */
22683     getValue : function(){
22684         
22685         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22686     },
22687
22688     /**
22689      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22690      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22691      * (the default format used is "m/d/y").
22692      * <br />Usage:
22693      * <pre><code>
22694 //All of these calls set the same date value (May 4, 2006)
22695
22696 //Pass a date object:
22697 var dt = new Date('5/4/06');
22698 dateField.setValue(dt);
22699
22700 //Pass a date string (default format):
22701 dateField.setValue('5/4/06');
22702
22703 //Pass a date string (custom format):
22704 dateField.format = 'Y-m-d';
22705 dateField.setValue('2006-5-4');
22706 </code></pre>
22707      * @param {String/Date} date The date or valid date string
22708      */
22709     setValue : function(date){
22710         if (this.hiddenField) {
22711             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22712         }
22713         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22714     },
22715
22716     // private
22717     parseDate : function(value){
22718         if(!value || value instanceof Date){
22719             return value;
22720         }
22721         var v = Date.parseDate(value, this.format);
22722         if(!v && this.altFormats){
22723             if(!this.altFormatsArray){
22724                 this.altFormatsArray = this.altFormats.split("|");
22725             }
22726             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22727                 v = Date.parseDate(value, this.altFormatsArray[i]);
22728             }
22729         }
22730         return v;
22731     },
22732
22733     // private
22734     formatDate : function(date, fmt){
22735         return (!date || !(date instanceof Date)) ?
22736                date : date.dateFormat(fmt || this.format);
22737     },
22738
22739     // private
22740     menuListeners : {
22741         select: function(m, d){
22742             this.setValue(d);
22743             this.fireEvent('select', this, d);
22744         },
22745         show : function(){ // retain focus styling
22746             this.onFocus();
22747         },
22748         hide : function(){
22749             this.focus.defer(10, this);
22750             var ml = this.menuListeners;
22751             this.menu.un("select", ml.select,  this);
22752             this.menu.un("show", ml.show,  this);
22753             this.menu.un("hide", ml.hide,  this);
22754         }
22755     },
22756
22757     // private
22758     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22759     onTriggerClick : function(){
22760         if(this.disabled){
22761             return;
22762         }
22763         if(this.menu == null){
22764             this.menu = new Roo.menu.DateMenu();
22765         }
22766         Roo.apply(this.menu.picker,  {
22767             showClear: this.allowBlank,
22768             minDate : this.minValue,
22769             maxDate : this.maxValue,
22770             disabledDatesRE : this.ddMatch,
22771             disabledDatesText : this.disabledDatesText,
22772             disabledDays : this.disabledDays,
22773             disabledDaysText : this.disabledDaysText,
22774             format : this.format,
22775             minText : String.format(this.minText, this.formatDate(this.minValue)),
22776             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22777         });
22778         this.menu.on(Roo.apply({}, this.menuListeners, {
22779             scope:this
22780         }));
22781         this.menu.picker.setValue(this.getValue() || new Date());
22782         this.menu.show(this.el, "tl-bl?");
22783     },
22784
22785     beforeBlur : function(){
22786         var v = this.parseDate(this.getRawValue());
22787         if(v){
22788             this.setValue(v);
22789         }
22790     }
22791
22792     /** @cfg {Boolean} grow @hide */
22793     /** @cfg {Number} growMin @hide */
22794     /** @cfg {Number} growMax @hide */
22795     /**
22796      * @hide
22797      * @method autoSize
22798      */
22799 });/*
22800  * Based on:
22801  * Ext JS Library 1.1.1
22802  * Copyright(c) 2006-2007, Ext JS, LLC.
22803  *
22804  * Originally Released Under LGPL - original licence link has changed is not relivant.
22805  *
22806  * Fork - LGPL
22807  * <script type="text/javascript">
22808  */
22809  
22810
22811 /**
22812  * @class Roo.form.ComboBox
22813  * @extends Roo.form.TriggerField
22814  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22815  * @constructor
22816  * Create a new ComboBox.
22817  * @param {Object} config Configuration options
22818  */
22819 Roo.form.ComboBox = function(config){
22820     Roo.form.ComboBox.superclass.constructor.call(this, config);
22821     this.addEvents({
22822         /**
22823          * @event expand
22824          * Fires when the dropdown list is expanded
22825              * @param {Roo.form.ComboBox} combo This combo box
22826              */
22827         'expand' : true,
22828         /**
22829          * @event collapse
22830          * Fires when the dropdown list is collapsed
22831              * @param {Roo.form.ComboBox} combo This combo box
22832              */
22833         'collapse' : true,
22834         /**
22835          * @event beforeselect
22836          * Fires before a list item is selected. Return false to cancel the selection.
22837              * @param {Roo.form.ComboBox} combo This combo box
22838              * @param {Roo.data.Record} record The data record returned from the underlying store
22839              * @param {Number} index The index of the selected item in the dropdown list
22840              */
22841         'beforeselect' : true,
22842         /**
22843          * @event select
22844          * Fires when a list item is selected
22845              * @param {Roo.form.ComboBox} combo This combo box
22846              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22847              * @param {Number} index The index of the selected item in the dropdown list
22848              */
22849         'select' : true,
22850         /**
22851          * @event beforequery
22852          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22853          * The event object passed has these properties:
22854              * @param {Roo.form.ComboBox} combo This combo box
22855              * @param {String} query The query
22856              * @param {Boolean} forceAll true to force "all" query
22857              * @param {Boolean} cancel true to cancel the query
22858              * @param {Object} e The query event object
22859              */
22860         'beforequery': true,
22861          /**
22862          * @event add
22863          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22864              * @param {Roo.form.ComboBox} combo This combo box
22865              */
22866         'add' : true,
22867         /**
22868          * @event edit
22869          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22870              * @param {Roo.form.ComboBox} combo This combo box
22871              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22872              */
22873         'edit' : true
22874         
22875         
22876     });
22877     if(this.transform){
22878         this.allowDomMove = false;
22879         var s = Roo.getDom(this.transform);
22880         if(!this.hiddenName){
22881             this.hiddenName = s.name;
22882         }
22883         if(!this.store){
22884             this.mode = 'local';
22885             var d = [], opts = s.options;
22886             for(var i = 0, len = opts.length;i < len; i++){
22887                 var o = opts[i];
22888                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22889                 if(o.selected) {
22890                     this.value = value;
22891                 }
22892                 d.push([value, o.text]);
22893             }
22894             this.store = new Roo.data.SimpleStore({
22895                 'id': 0,
22896                 fields: ['value', 'text'],
22897                 data : d
22898             });
22899             this.valueField = 'value';
22900             this.displayField = 'text';
22901         }
22902         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22903         if(!this.lazyRender){
22904             this.target = true;
22905             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22906             s.parentNode.removeChild(s); // remove it
22907             this.render(this.el.parentNode);
22908         }else{
22909             s.parentNode.removeChild(s); // remove it
22910         }
22911
22912     }
22913     if (this.store) {
22914         this.store = Roo.factory(this.store, Roo.data);
22915     }
22916     
22917     this.selectedIndex = -1;
22918     if(this.mode == 'local'){
22919         if(config.queryDelay === undefined){
22920             this.queryDelay = 10;
22921         }
22922         if(config.minChars === undefined){
22923             this.minChars = 0;
22924         }
22925     }
22926 };
22927
22928 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22929     /**
22930      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22931      */
22932     /**
22933      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22934      * rendering into an Roo.Editor, defaults to false)
22935      */
22936     /**
22937      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22938      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22939      */
22940     /**
22941      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22942      */
22943     /**
22944      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22945      * the dropdown list (defaults to undefined, with no header element)
22946      */
22947
22948      /**
22949      * @cfg {String/Roo.Template} tpl The template to use to render the output
22950      */
22951      
22952     // private
22953     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22954     /**
22955      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22956      */
22957     listWidth: undefined,
22958     /**
22959      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22960      * mode = 'remote' or 'text' if mode = 'local')
22961      */
22962     displayField: undefined,
22963     /**
22964      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22965      * mode = 'remote' or 'value' if mode = 'local'). 
22966      * Note: use of a valueField requires the user make a selection
22967      * in order for a value to be mapped.
22968      */
22969     valueField: undefined,
22970     
22971     
22972     /**
22973      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22974      * field's data value (defaults to the underlying DOM element's name)
22975      */
22976     hiddenName: undefined,
22977     /**
22978      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22979      */
22980     listClass: '',
22981     /**
22982      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22983      */
22984     selectedClass: 'x-combo-selected',
22985     /**
22986      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22987      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22988      * which displays a downward arrow icon).
22989      */
22990     triggerClass : 'x-form-arrow-trigger',
22991     /**
22992      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22993      */
22994     shadow:'sides',
22995     /**
22996      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22997      * anchor positions (defaults to 'tl-bl')
22998      */
22999     listAlign: 'tl-bl?',
23000     /**
23001      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23002      */
23003     maxHeight: 300,
23004     /**
23005      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23006      * query specified by the allQuery config option (defaults to 'query')
23007      */
23008     triggerAction: 'query',
23009     /**
23010      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23011      * (defaults to 4, does not apply if editable = false)
23012      */
23013     minChars : 4,
23014     /**
23015      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23016      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23017      */
23018     typeAhead: false,
23019     /**
23020      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23021      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23022      */
23023     queryDelay: 500,
23024     /**
23025      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23026      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23027      */
23028     pageSize: 0,
23029     /**
23030      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23031      * when editable = true (defaults to false)
23032      */
23033     selectOnFocus:false,
23034     /**
23035      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23036      */
23037     queryParam: 'query',
23038     /**
23039      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23040      * when mode = 'remote' (defaults to 'Loading...')
23041      */
23042     loadingText: 'Loading...',
23043     /**
23044      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23045      */
23046     resizable: false,
23047     /**
23048      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23049      */
23050     handleHeight : 8,
23051     /**
23052      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23053      * traditional select (defaults to true)
23054      */
23055     editable: true,
23056     /**
23057      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23058      */
23059     allQuery: '',
23060     /**
23061      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23062      */
23063     mode: 'remote',
23064     /**
23065      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23066      * listWidth has a higher value)
23067      */
23068     minListWidth : 70,
23069     /**
23070      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23071      * allow the user to set arbitrary text into the field (defaults to false)
23072      */
23073     forceSelection:false,
23074     /**
23075      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23076      * if typeAhead = true (defaults to 250)
23077      */
23078     typeAheadDelay : 250,
23079     /**
23080      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23081      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23082      */
23083     valueNotFoundText : undefined,
23084     /**
23085      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23086      */
23087     blockFocus : false,
23088     
23089     /**
23090      * @cfg {Boolean} disableClear Disable showing of clear button.
23091      */
23092     disableClear : false,
23093     /**
23094      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23095      */
23096     alwaysQuery : false,
23097     
23098     //private
23099     addicon : false,
23100     editicon: false,
23101     
23102     // element that contains real text value.. (when hidden is used..)
23103      
23104     // private
23105     onRender : function(ct, position){
23106         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23107         if(this.hiddenName){
23108             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23109                     'before', true);
23110             this.hiddenField.value =
23111                 this.hiddenValue !== undefined ? this.hiddenValue :
23112                 this.value !== undefined ? this.value : '';
23113
23114             // prevent input submission
23115             this.el.dom.removeAttribute('name');
23116              
23117              
23118         }
23119         if(Roo.isGecko){
23120             this.el.dom.setAttribute('autocomplete', 'off');
23121         }
23122
23123         var cls = 'x-combo-list';
23124
23125         this.list = new Roo.Layer({
23126             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23127         });
23128
23129         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23130         this.list.setWidth(lw);
23131         this.list.swallowEvent('mousewheel');
23132         this.assetHeight = 0;
23133
23134         if(this.title){
23135             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23136             this.assetHeight += this.header.getHeight();
23137         }
23138
23139         this.innerList = this.list.createChild({cls:cls+'-inner'});
23140         this.innerList.on('mouseover', this.onViewOver, this);
23141         this.innerList.on('mousemove', this.onViewMove, this);
23142         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23143         
23144         if(this.allowBlank && !this.pageSize && !this.disableClear){
23145             this.footer = this.list.createChild({cls:cls+'-ft'});
23146             this.pageTb = new Roo.Toolbar(this.footer);
23147            
23148         }
23149         if(this.pageSize){
23150             this.footer = this.list.createChild({cls:cls+'-ft'});
23151             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23152                     {pageSize: this.pageSize});
23153             
23154         }
23155         
23156         if (this.pageTb && this.allowBlank && !this.disableClear) {
23157             var _this = this;
23158             this.pageTb.add(new Roo.Toolbar.Fill(), {
23159                 cls: 'x-btn-icon x-btn-clear',
23160                 text: '&#160;',
23161                 handler: function()
23162                 {
23163                     _this.collapse();
23164                     _this.clearValue();
23165                     _this.onSelect(false, -1);
23166                 }
23167             });
23168         }
23169         if (this.footer) {
23170             this.assetHeight += this.footer.getHeight();
23171         }
23172         
23173
23174         if(!this.tpl){
23175             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23176         }
23177
23178         this.view = new Roo.View(this.innerList, this.tpl, {
23179             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23180         });
23181
23182         this.view.on('click', this.onViewClick, this);
23183
23184         this.store.on('beforeload', this.onBeforeLoad, this);
23185         this.store.on('load', this.onLoad, this);
23186         this.store.on('loadexception', this.onLoadException, this);
23187
23188         if(this.resizable){
23189             this.resizer = new Roo.Resizable(this.list,  {
23190                pinned:true, handles:'se'
23191             });
23192             this.resizer.on('resize', function(r, w, h){
23193                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23194                 this.listWidth = w;
23195                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23196                 this.restrictHeight();
23197             }, this);
23198             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23199         }
23200         if(!this.editable){
23201             this.editable = true;
23202             this.setEditable(false);
23203         }  
23204         
23205         
23206         if (typeof(this.events.add.listeners) != 'undefined') {
23207             
23208             this.addicon = this.wrap.createChild(
23209                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23210        
23211             this.addicon.on('click', function(e) {
23212                 this.fireEvent('add', this);
23213             }, this);
23214         }
23215         if (typeof(this.events.edit.listeners) != 'undefined') {
23216             
23217             this.editicon = this.wrap.createChild(
23218                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23219             if (this.addicon) {
23220                 this.editicon.setStyle('margin-left', '40px');
23221             }
23222             this.editicon.on('click', function(e) {
23223                 
23224                 // we fire even  if inothing is selected..
23225                 this.fireEvent('edit', this, this.lastData );
23226                 
23227             }, this);
23228         }
23229         
23230         
23231         
23232     },
23233
23234     // private
23235     initEvents : function(){
23236         Roo.form.ComboBox.superclass.initEvents.call(this);
23237
23238         this.keyNav = new Roo.KeyNav(this.el, {
23239             "up" : function(e){
23240                 this.inKeyMode = true;
23241                 this.selectPrev();
23242             },
23243
23244             "down" : function(e){
23245                 if(!this.isExpanded()){
23246                     this.onTriggerClick();
23247                 }else{
23248                     this.inKeyMode = true;
23249                     this.selectNext();
23250                 }
23251             },
23252
23253             "enter" : function(e){
23254                 this.onViewClick();
23255                 //return true;
23256             },
23257
23258             "esc" : function(e){
23259                 this.collapse();
23260             },
23261
23262             "tab" : function(e){
23263                 this.onViewClick(false);
23264                 this.fireEvent("specialkey", this, e);
23265                 return true;
23266             },
23267
23268             scope : this,
23269
23270             doRelay : function(foo, bar, hname){
23271                 if(hname == 'down' || this.scope.isExpanded()){
23272                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23273                 }
23274                 return true;
23275             },
23276
23277             forceKeyDown: true
23278         });
23279         this.queryDelay = Math.max(this.queryDelay || 10,
23280                 this.mode == 'local' ? 10 : 250);
23281         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23282         if(this.typeAhead){
23283             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23284         }
23285         if(this.editable !== false){
23286             this.el.on("keyup", this.onKeyUp, this);
23287         }
23288         if(this.forceSelection){
23289             this.on('blur', this.doForce, this);
23290         }
23291     },
23292
23293     onDestroy : function(){
23294         if(this.view){
23295             this.view.setStore(null);
23296             this.view.el.removeAllListeners();
23297             this.view.el.remove();
23298             this.view.purgeListeners();
23299         }
23300         if(this.list){
23301             this.list.destroy();
23302         }
23303         if(this.store){
23304             this.store.un('beforeload', this.onBeforeLoad, this);
23305             this.store.un('load', this.onLoad, this);
23306             this.store.un('loadexception', this.onLoadException, this);
23307         }
23308         Roo.form.ComboBox.superclass.onDestroy.call(this);
23309     },
23310
23311     // private
23312     fireKey : function(e){
23313         if(e.isNavKeyPress() && !this.list.isVisible()){
23314             this.fireEvent("specialkey", this, e);
23315         }
23316     },
23317
23318     // private
23319     onResize: function(w, h){
23320         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23321         
23322         if(typeof w != 'number'){
23323             // we do not handle it!?!?
23324             return;
23325         }
23326         var tw = this.trigger.getWidth();
23327         tw += this.addicon ? this.addicon.getWidth() : 0;
23328         tw += this.editicon ? this.editicon.getWidth() : 0;
23329         var x = w - tw;
23330         this.el.setWidth( this.adjustWidth('input', x));
23331             
23332         this.trigger.setStyle('left', x+'px');
23333         
23334         if(this.list && this.listWidth === undefined){
23335             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23336             this.list.setWidth(lw);
23337             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23338         }
23339         
23340     
23341         
23342     },
23343
23344     /**
23345      * Allow or prevent the user from directly editing the field text.  If false is passed,
23346      * the user will only be able to select from the items defined in the dropdown list.  This method
23347      * is the runtime equivalent of setting the 'editable' config option at config time.
23348      * @param {Boolean} value True to allow the user to directly edit the field text
23349      */
23350     setEditable : function(value){
23351         if(value == this.editable){
23352             return;
23353         }
23354         this.editable = value;
23355         if(!value){
23356             this.el.dom.setAttribute('readOnly', true);
23357             this.el.on('mousedown', this.onTriggerClick,  this);
23358             this.el.addClass('x-combo-noedit');
23359         }else{
23360             this.el.dom.setAttribute('readOnly', false);
23361             this.el.un('mousedown', this.onTriggerClick,  this);
23362             this.el.removeClass('x-combo-noedit');
23363         }
23364     },
23365
23366     // private
23367     onBeforeLoad : function(){
23368         if(!this.hasFocus){
23369             return;
23370         }
23371         this.innerList.update(this.loadingText ?
23372                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23373         this.restrictHeight();
23374         this.selectedIndex = -1;
23375     },
23376
23377     // private
23378     onLoad : function(){
23379         if(!this.hasFocus){
23380             return;
23381         }
23382         if(this.store.getCount() > 0){
23383             this.expand();
23384             this.restrictHeight();
23385             if(this.lastQuery == this.allQuery){
23386                 if(this.editable){
23387                     this.el.dom.select();
23388                 }
23389                 if(!this.selectByValue(this.value, true)){
23390                     this.select(0, true);
23391                 }
23392             }else{
23393                 this.selectNext();
23394                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23395                     this.taTask.delay(this.typeAheadDelay);
23396                 }
23397             }
23398         }else{
23399             this.onEmptyResults();
23400         }
23401         //this.el.focus();
23402     },
23403     // private
23404     onLoadException : function()
23405     {
23406         this.collapse();
23407         Roo.log(this.store.reader.jsonData);
23408         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23409             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23410         }
23411         
23412         
23413     },
23414     // private
23415     onTypeAhead : function(){
23416         if(this.store.getCount() > 0){
23417             var r = this.store.getAt(0);
23418             var newValue = r.data[this.displayField];
23419             var len = newValue.length;
23420             var selStart = this.getRawValue().length;
23421             if(selStart != len){
23422                 this.setRawValue(newValue);
23423                 this.selectText(selStart, newValue.length);
23424             }
23425         }
23426     },
23427
23428     // private
23429     onSelect : function(record, index){
23430         if(this.fireEvent('beforeselect', this, record, index) !== false){
23431             this.setFromData(index > -1 ? record.data : false);
23432             this.collapse();
23433             this.fireEvent('select', this, record, index);
23434         }
23435     },
23436
23437     /**
23438      * Returns the currently selected field value or empty string if no value is set.
23439      * @return {String} value The selected value
23440      */
23441     getValue : function(){
23442         if(this.valueField){
23443             return typeof this.value != 'undefined' ? this.value : '';
23444         }else{
23445             return Roo.form.ComboBox.superclass.getValue.call(this);
23446         }
23447     },
23448
23449     /**
23450      * Clears any text/value currently set in the field
23451      */
23452     clearValue : function(){
23453         if(this.hiddenField){
23454             this.hiddenField.value = '';
23455         }
23456         this.value = '';
23457         this.setRawValue('');
23458         this.lastSelectionText = '';
23459         this.applyEmptyText();
23460     },
23461
23462     /**
23463      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23464      * will be displayed in the field.  If the value does not match the data value of an existing item,
23465      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23466      * Otherwise the field will be blank (although the value will still be set).
23467      * @param {String} value The value to match
23468      */
23469     setValue : function(v){
23470         var text = v;
23471         if(this.valueField){
23472             var r = this.findRecord(this.valueField, v);
23473             if(r){
23474                 text = r.data[this.displayField];
23475             }else if(this.valueNotFoundText !== undefined){
23476                 text = this.valueNotFoundText;
23477             }
23478         }
23479         this.lastSelectionText = text;
23480         if(this.hiddenField){
23481             this.hiddenField.value = v;
23482         }
23483         Roo.form.ComboBox.superclass.setValue.call(this, text);
23484         this.value = v;
23485     },
23486     /**
23487      * @property {Object} the last set data for the element
23488      */
23489     
23490     lastData : false,
23491     /**
23492      * Sets the value of the field based on a object which is related to the record format for the store.
23493      * @param {Object} value the value to set as. or false on reset?
23494      */
23495     setFromData : function(o){
23496         var dv = ''; // display value
23497         var vv = ''; // value value..
23498         this.lastData = o;
23499         if (this.displayField) {
23500             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23501         } else {
23502             // this is an error condition!!!
23503             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23504         }
23505         
23506         if(this.valueField){
23507             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23508         }
23509         if(this.hiddenField){
23510             this.hiddenField.value = vv;
23511             
23512             this.lastSelectionText = dv;
23513             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23514             this.value = vv;
23515             return;
23516         }
23517         // no hidden field.. - we store the value in 'value', but still display
23518         // display field!!!!
23519         this.lastSelectionText = dv;
23520         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23521         this.value = vv;
23522         
23523         
23524     },
23525     // private
23526     reset : function(){
23527         // overridden so that last data is reset..
23528         this.setValue(this.originalValue);
23529         this.clearInvalid();
23530         this.lastData = false;
23531     },
23532     // private
23533     findRecord : function(prop, value){
23534         var record;
23535         if(this.store.getCount() > 0){
23536             this.store.each(function(r){
23537                 if(r.data[prop] == value){
23538                     record = r;
23539                     return false;
23540                 }
23541                 return true;
23542             });
23543         }
23544         return record;
23545     },
23546     
23547     getName: function()
23548     {
23549         // returns hidden if it's set..
23550         if (!this.rendered) {return ''};
23551         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23552         
23553     },
23554     // private
23555     onViewMove : function(e, t){
23556         this.inKeyMode = false;
23557     },
23558
23559     // private
23560     onViewOver : function(e, t){
23561         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23562             return;
23563         }
23564         var item = this.view.findItemFromChild(t);
23565         if(item){
23566             var index = this.view.indexOf(item);
23567             this.select(index, false);
23568         }
23569     },
23570
23571     // private
23572     onViewClick : function(doFocus)
23573     {
23574         var index = this.view.getSelectedIndexes()[0];
23575         var r = this.store.getAt(index);
23576         if(r){
23577             this.onSelect(r, index);
23578         }
23579         if(doFocus !== false && !this.blockFocus){
23580             this.el.focus();
23581         }
23582     },
23583
23584     // private
23585     restrictHeight : function(){
23586         this.innerList.dom.style.height = '';
23587         var inner = this.innerList.dom;
23588         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23589         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23590         this.list.beginUpdate();
23591         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23592         this.list.alignTo(this.el, this.listAlign);
23593         this.list.endUpdate();
23594     },
23595
23596     // private
23597     onEmptyResults : function(){
23598         this.collapse();
23599     },
23600
23601     /**
23602      * Returns true if the dropdown list is expanded, else false.
23603      */
23604     isExpanded : function(){
23605         return this.list.isVisible();
23606     },
23607
23608     /**
23609      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23610      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23611      * @param {String} value The data value of the item to select
23612      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23613      * selected item if it is not currently in view (defaults to true)
23614      * @return {Boolean} True if the value matched an item in the list, else false
23615      */
23616     selectByValue : function(v, scrollIntoView){
23617         if(v !== undefined && v !== null){
23618             var r = this.findRecord(this.valueField || this.displayField, v);
23619             if(r){
23620                 this.select(this.store.indexOf(r), scrollIntoView);
23621                 return true;
23622             }
23623         }
23624         return false;
23625     },
23626
23627     /**
23628      * Select an item in the dropdown list by its numeric index in the list. 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 {Number} index The zero-based index of the list 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      */
23634     select : function(index, scrollIntoView){
23635         this.selectedIndex = index;
23636         this.view.select(index);
23637         if(scrollIntoView !== false){
23638             var el = this.view.getNode(index);
23639             if(el){
23640                 this.innerList.scrollChildIntoView(el, false);
23641             }
23642         }
23643     },
23644
23645     // private
23646     selectNext : function(){
23647         var ct = this.store.getCount();
23648         if(ct > 0){
23649             if(this.selectedIndex == -1){
23650                 this.select(0);
23651             }else if(this.selectedIndex < ct-1){
23652                 this.select(this.selectedIndex+1);
23653             }
23654         }
23655     },
23656
23657     // private
23658     selectPrev : function(){
23659         var ct = this.store.getCount();
23660         if(ct > 0){
23661             if(this.selectedIndex == -1){
23662                 this.select(0);
23663             }else if(this.selectedIndex != 0){
23664                 this.select(this.selectedIndex-1);
23665             }
23666         }
23667     },
23668
23669     // private
23670     onKeyUp : function(e){
23671         if(this.editable !== false && !e.isSpecialKey()){
23672             this.lastKey = e.getKey();
23673             this.dqTask.delay(this.queryDelay);
23674         }
23675     },
23676
23677     // private
23678     validateBlur : function(){
23679         return !this.list || !this.list.isVisible();   
23680     },
23681
23682     // private
23683     initQuery : function(){
23684         this.doQuery(this.getRawValue());
23685     },
23686
23687     // private
23688     doForce : function(){
23689         if(this.el.dom.value.length > 0){
23690             this.el.dom.value =
23691                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23692             this.applyEmptyText();
23693         }
23694     },
23695
23696     /**
23697      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23698      * query allowing the query action to be canceled if needed.
23699      * @param {String} query The SQL query to execute
23700      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23701      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23702      * saved in the current store (defaults to false)
23703      */
23704     doQuery : function(q, forceAll){
23705         if(q === undefined || q === null){
23706             q = '';
23707         }
23708         var qe = {
23709             query: q,
23710             forceAll: forceAll,
23711             combo: this,
23712             cancel:false
23713         };
23714         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23715             return false;
23716         }
23717         q = qe.query;
23718         forceAll = qe.forceAll;
23719         if(forceAll === true || (q.length >= this.minChars)){
23720             if(this.lastQuery != q || this.alwaysQuery){
23721                 this.lastQuery = q;
23722                 if(this.mode == 'local'){
23723                     this.selectedIndex = -1;
23724                     if(forceAll){
23725                         this.store.clearFilter();
23726                     }else{
23727                         this.store.filter(this.displayField, q);
23728                     }
23729                     this.onLoad();
23730                 }else{
23731                     this.store.baseParams[this.queryParam] = q;
23732                     this.store.load({
23733                         params: this.getParams(q)
23734                     });
23735                     this.expand();
23736                 }
23737             }else{
23738                 this.selectedIndex = -1;
23739                 this.onLoad();   
23740             }
23741         }
23742     },
23743
23744     // private
23745     getParams : function(q){
23746         var p = {};
23747         //p[this.queryParam] = q;
23748         if(this.pageSize){
23749             p.start = 0;
23750             p.limit = this.pageSize;
23751         }
23752         return p;
23753     },
23754
23755     /**
23756      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23757      */
23758     collapse : function(){
23759         if(!this.isExpanded()){
23760             return;
23761         }
23762         this.list.hide();
23763         Roo.get(document).un('mousedown', this.collapseIf, this);
23764         Roo.get(document).un('mousewheel', this.collapseIf, this);
23765         if (!this.editable) {
23766             Roo.get(document).un('keydown', this.listKeyPress, this);
23767         }
23768         this.fireEvent('collapse', this);
23769     },
23770
23771     // private
23772     collapseIf : function(e){
23773         if(!e.within(this.wrap) && !e.within(this.list)){
23774             this.collapse();
23775         }
23776     },
23777
23778     /**
23779      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23780      */
23781     expand : function(){
23782         if(this.isExpanded() || !this.hasFocus){
23783             return;
23784         }
23785         this.list.alignTo(this.el, this.listAlign);
23786         this.list.show();
23787         Roo.get(document).on('mousedown', this.collapseIf, this);
23788         Roo.get(document).on('mousewheel', this.collapseIf, this);
23789         if (!this.editable) {
23790             Roo.get(document).on('keydown', this.listKeyPress, this);
23791         }
23792         
23793         this.fireEvent('expand', this);
23794     },
23795
23796     // private
23797     // Implements the default empty TriggerField.onTriggerClick function
23798     onTriggerClick : function(){
23799         if(this.disabled){
23800             return;
23801         }
23802         if(this.isExpanded()){
23803             this.collapse();
23804             if (!this.blockFocus) {
23805                 this.el.focus();
23806             }
23807             
23808         }else {
23809             this.hasFocus = true;
23810             if(this.triggerAction == 'all') {
23811                 this.doQuery(this.allQuery, true);
23812             } else {
23813                 this.doQuery(this.getRawValue());
23814             }
23815             if (!this.blockFocus) {
23816                 this.el.focus();
23817             }
23818         }
23819     },
23820     listKeyPress : function(e)
23821     {
23822         //Roo.log('listkeypress');
23823         // scroll to first matching element based on key pres..
23824         if (e.isSpecialKey()) {
23825             return false;
23826         }
23827         var k = String.fromCharCode(e.getKey()).toUpperCase();
23828         //Roo.log(k);
23829         var match  = false;
23830         var csel = this.view.getSelectedNodes();
23831         var cselitem = false;
23832         if (csel.length) {
23833             var ix = this.view.indexOf(csel[0]);
23834             cselitem  = this.store.getAt(ix);
23835             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23836                 cselitem = false;
23837             }
23838             
23839         }
23840         
23841         this.store.each(function(v) { 
23842             if (cselitem) {
23843                 // start at existing selection.
23844                 if (cselitem.id == v.id) {
23845                     cselitem = false;
23846                 }
23847                 return;
23848             }
23849                 
23850             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23851                 match = this.store.indexOf(v);
23852                 return false;
23853             }
23854         }, this);
23855         
23856         if (match === false) {
23857             return true; // no more action?
23858         }
23859         // scroll to?
23860         this.view.select(match);
23861         var sn = Roo.get(this.view.getSelectedNodes()[0])
23862         sn.scrollIntoView(sn.dom.parentNode, false);
23863     }
23864
23865     /** 
23866     * @cfg {Boolean} grow 
23867     * @hide 
23868     */
23869     /** 
23870     * @cfg {Number} growMin 
23871     * @hide 
23872     */
23873     /** 
23874     * @cfg {Number} growMax 
23875     * @hide 
23876     */
23877     /**
23878      * @hide
23879      * @method autoSize
23880      */
23881 });/*
23882  * Based on:
23883  * Ext JS Library 1.1.1
23884  * Copyright(c) 2006-2007, Ext JS, LLC.
23885  *
23886  * Originally Released Under LGPL - original licence link has changed is not relivant.
23887  *
23888  * Fork - LGPL
23889  * <script type="text/javascript">
23890  */
23891 /**
23892  * @class Roo.form.Checkbox
23893  * @extends Roo.form.Field
23894  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23895  * @constructor
23896  * Creates a new Checkbox
23897  * @param {Object} config Configuration options
23898  */
23899 Roo.form.Checkbox = function(config){
23900     Roo.form.Checkbox.superclass.constructor.call(this, config);
23901     this.addEvents({
23902         /**
23903          * @event check
23904          * Fires when the checkbox is checked or unchecked.
23905              * @param {Roo.form.Checkbox} this This checkbox
23906              * @param {Boolean} checked The new checked value
23907              */
23908         check : true
23909     });
23910 };
23911
23912 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23913     /**
23914      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23915      */
23916     focusClass : undefined,
23917     /**
23918      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23919      */
23920     fieldClass: "x-form-field",
23921     /**
23922      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23923      */
23924     checked: false,
23925     /**
23926      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23927      * {tag: "input", type: "checkbox", autocomplete: "off"})
23928      */
23929     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23930     /**
23931      * @cfg {String} boxLabel The text that appears beside the checkbox
23932      */
23933     boxLabel : "",
23934     /**
23935      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23936      */  
23937     inputValue : '1',
23938     /**
23939      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23940      */
23941      valueOff: '0', // value when not checked..
23942
23943     actionMode : 'viewEl', 
23944     //
23945     // private
23946     itemCls : 'x-menu-check-item x-form-item',
23947     groupClass : 'x-menu-group-item',
23948     inputType : 'hidden',
23949     
23950     
23951     inSetChecked: false, // check that we are not calling self...
23952     
23953     inputElement: false, // real input element?
23954     basedOn: false, // ????
23955     
23956     isFormField: true, // not sure where this is needed!!!!
23957
23958     onResize : function(){
23959         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23960         if(!this.boxLabel){
23961             this.el.alignTo(this.wrap, 'c-c');
23962         }
23963     },
23964
23965     initEvents : function(){
23966         Roo.form.Checkbox.superclass.initEvents.call(this);
23967         this.el.on("click", this.onClick,  this);
23968         this.el.on("change", this.onClick,  this);
23969     },
23970
23971
23972     getResizeEl : function(){
23973         return this.wrap;
23974     },
23975
23976     getPositionEl : function(){
23977         return this.wrap;
23978     },
23979
23980     // private
23981     onRender : function(ct, position){
23982         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23983         /*
23984         if(this.inputValue !== undefined){
23985             this.el.dom.value = this.inputValue;
23986         }
23987         */
23988         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23989         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23990         var viewEl = this.wrap.createChild({ 
23991             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23992         this.viewEl = viewEl;   
23993         this.wrap.on('click', this.onClick,  this); 
23994         
23995         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23996         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23997         
23998         
23999         
24000         if(this.boxLabel){
24001             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24002         //    viewEl.on('click', this.onClick,  this); 
24003         }
24004         //if(this.checked){
24005             this.setChecked(this.checked);
24006         //}else{
24007             //this.checked = this.el.dom;
24008         //}
24009
24010     },
24011
24012     // private
24013     initValue : Roo.emptyFn,
24014
24015     /**
24016      * Returns the checked state of the checkbox.
24017      * @return {Boolean} True if checked, else false
24018      */
24019     getValue : function(){
24020         if(this.el){
24021             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24022         }
24023         return this.valueOff;
24024         
24025     },
24026
24027         // private
24028     onClick : function(){ 
24029         this.setChecked(!this.checked);
24030
24031         //if(this.el.dom.checked != this.checked){
24032         //    this.setValue(this.el.dom.checked);
24033        // }
24034     },
24035
24036     /**
24037      * Sets the checked state of the checkbox.
24038      * On is always based on a string comparison between inputValue and the param.
24039      * @param {Boolean/String} value - the value to set 
24040      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24041      */
24042     setValue : function(v,suppressEvent){
24043         
24044         
24045         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24046         //if(this.el && this.el.dom){
24047         //    this.el.dom.checked = this.checked;
24048         //    this.el.dom.defaultChecked = this.checked;
24049         //}
24050         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24051         //this.fireEvent("check", this, this.checked);
24052     },
24053     // private..
24054     setChecked : function(state,suppressEvent)
24055     {
24056         if (this.inSetChecked) {
24057             this.checked = state;
24058             return;
24059         }
24060         
24061     
24062         if(this.wrap){
24063             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24064         }
24065         this.checked = state;
24066         if(suppressEvent !== true){
24067             this.fireEvent('check', this, state);
24068         }
24069         this.inSetChecked = true;
24070         this.el.dom.value = state ? this.inputValue : this.valueOff;
24071         this.inSetChecked = false;
24072         
24073     },
24074     // handle setting of hidden value by some other method!!?!?
24075     setFromHidden: function()
24076     {
24077         if(!this.el){
24078             return;
24079         }
24080         //console.log("SET FROM HIDDEN");
24081         //alert('setFrom hidden');
24082         this.setValue(this.el.dom.value);
24083     },
24084     
24085     onDestroy : function()
24086     {
24087         if(this.viewEl){
24088             Roo.get(this.viewEl).remove();
24089         }
24090          
24091         Roo.form.Checkbox.superclass.onDestroy.call(this);
24092     }
24093
24094 });/*
24095  * Based on:
24096  * Ext JS Library 1.1.1
24097  * Copyright(c) 2006-2007, Ext JS, LLC.
24098  *
24099  * Originally Released Under LGPL - original licence link has changed is not relivant.
24100  *
24101  * Fork - LGPL
24102  * <script type="text/javascript">
24103  */
24104  
24105 /**
24106  * @class Roo.form.Radio
24107  * @extends Roo.form.Checkbox
24108  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24109  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24110  * @constructor
24111  * Creates a new Radio
24112  * @param {Object} config Configuration options
24113  */
24114 Roo.form.Radio = function(){
24115     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24116 };
24117 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24118     inputType: 'radio',
24119
24120     /**
24121      * If this radio is part of a group, it will return the selected value
24122      * @return {String}
24123      */
24124     getGroupValue : function(){
24125         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24126     }
24127 });//<script type="text/javascript">
24128
24129 /*
24130  * Ext JS Library 1.1.1
24131  * Copyright(c) 2006-2007, Ext JS, LLC.
24132  * licensing@extjs.com
24133  * 
24134  * http://www.extjs.com/license
24135  */
24136  
24137  /*
24138   * 
24139   * Known bugs:
24140   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24141   * - IE ? - no idea how much works there.
24142   * 
24143   * 
24144   * 
24145   */
24146  
24147
24148 /**
24149  * @class Ext.form.HtmlEditor
24150  * @extends Ext.form.Field
24151  * Provides a lightweight HTML Editor component.
24152  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
24153  * 
24154  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24155  * supported by this editor.</b><br/><br/>
24156  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24157  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24158  */
24159 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24160       /**
24161      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24162      */
24163     toolbars : false,
24164     /**
24165      * @cfg {String} createLinkText The default text for the create link prompt
24166      */
24167     createLinkText : 'Please enter the URL for the link:',
24168     /**
24169      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24170      */
24171     defaultLinkValue : 'http:/'+'/',
24172    
24173      /**
24174      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24175      *                        Roo.resizable.
24176      */
24177     resizable : false,
24178      /**
24179      * @cfg {Number} height (in pixels)
24180      */   
24181     height: 300,
24182    /**
24183      * @cfg {Number} width (in pixels)
24184      */   
24185     width: 500,
24186     
24187     /**
24188      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24189      * 
24190      */
24191     stylesheets: false,
24192     
24193     // id of frame..
24194     frameId: false,
24195     
24196     // private properties
24197     validationEvent : false,
24198     deferHeight: true,
24199     initialized : false,
24200     activated : false,
24201     sourceEditMode : false,
24202     onFocus : Roo.emptyFn,
24203     iframePad:3,
24204     hideMode:'offsets',
24205     
24206     defaultAutoCreate : { // modified by initCompnoent..
24207         tag: "textarea",
24208         style:"width:500px;height:300px;",
24209         autocomplete: "off"
24210     },
24211
24212     // private
24213     initComponent : function(){
24214         this.addEvents({
24215             /**
24216              * @event initialize
24217              * Fires when the editor is fully initialized (including the iframe)
24218              * @param {HtmlEditor} this
24219              */
24220             initialize: true,
24221             /**
24222              * @event activate
24223              * Fires when the editor is first receives the focus. Any insertion must wait
24224              * until after this event.
24225              * @param {HtmlEditor} this
24226              */
24227             activate: true,
24228              /**
24229              * @event beforesync
24230              * Fires before the textarea is updated with content from the editor iframe. Return false
24231              * to cancel the sync.
24232              * @param {HtmlEditor} this
24233              * @param {String} html
24234              */
24235             beforesync: true,
24236              /**
24237              * @event beforepush
24238              * Fires before the iframe editor is updated with content from the textarea. Return false
24239              * to cancel the push.
24240              * @param {HtmlEditor} this
24241              * @param {String} html
24242              */
24243             beforepush: true,
24244              /**
24245              * @event sync
24246              * Fires when the textarea is updated with content from the editor iframe.
24247              * @param {HtmlEditor} this
24248              * @param {String} html
24249              */
24250             sync: true,
24251              /**
24252              * @event push
24253              * Fires when the iframe editor is updated with content from the textarea.
24254              * @param {HtmlEditor} this
24255              * @param {String} html
24256              */
24257             push: true,
24258              /**
24259              * @event editmodechange
24260              * Fires when the editor switches edit modes
24261              * @param {HtmlEditor} this
24262              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24263              */
24264             editmodechange: true,
24265             /**
24266              * @event editorevent
24267              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24268              * @param {HtmlEditor} this
24269              */
24270             editorevent: true
24271         });
24272         this.defaultAutoCreate =  {
24273             tag: "textarea",
24274             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24275             autocomplete: "off"
24276         };
24277     },
24278
24279     /**
24280      * Protected method that will not generally be called directly. It
24281      * is called when the editor creates its toolbar. Override this method if you need to
24282      * add custom toolbar buttons.
24283      * @param {HtmlEditor} editor
24284      */
24285     createToolbar : function(editor){
24286         if (!editor.toolbars || !editor.toolbars.length) {
24287             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24288         }
24289         
24290         for (var i =0 ; i < editor.toolbars.length;i++) {
24291             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24292             editor.toolbars[i].init(editor);
24293         }
24294          
24295         
24296     },
24297
24298     /**
24299      * Protected method that will not generally be called directly. It
24300      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24301      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24302      */
24303     getDocMarkup : function(){
24304         // body styles..
24305         var st = '';
24306         if (this.stylesheets === false) {
24307             
24308             Roo.get(document.head).select('style').each(function(node) {
24309                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24310             });
24311             
24312             Roo.get(document.head).select('link').each(function(node) { 
24313                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24314             });
24315             
24316         } else if (!this.stylesheets.length) {
24317                 // simple..
24318                 st = '<style type="text/css">' +
24319                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24320                    '</style>';
24321         } else {
24322             Roo.each(this.stylesheets, function(s) {
24323                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24324             });
24325             
24326         }
24327         
24328         return '<html><head>' + st  +
24329             //<style type="text/css">' +
24330             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24331             //'</style>' +
24332             ' </head><body></body></html>';
24333     },
24334
24335     // private
24336     onRender : function(ct, position)
24337     {
24338         var _t = this;
24339         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24340         this.el.dom.style.border = '0 none';
24341         this.el.dom.setAttribute('tabIndex', -1);
24342         this.el.addClass('x-hidden');
24343         if(Roo.isIE){ // fix IE 1px bogus margin
24344             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24345         }
24346         this.wrap = this.el.wrap({
24347             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24348         });
24349         
24350         if (this.resizable) {
24351             this.resizeEl = new Roo.Resizable(this.wrap, {
24352                 pinned : true,
24353                 wrap: true,
24354                 dynamic : true,
24355                 minHeight : this.height,
24356                 height: this.height,
24357                 handles : this.resizable,
24358                 width: this.width,
24359                 listeners : {
24360                     resize : function(r, w, h) {
24361                         _t.onResize(w,h); // -something
24362                     }
24363                 }
24364             });
24365             
24366         }
24367
24368         this.frameId = Roo.id();
24369         
24370         this.createToolbar(this);
24371         
24372       
24373         
24374         var iframe = this.wrap.createChild({
24375             tag: 'iframe',
24376             id: this.frameId,
24377             name: this.frameId,
24378             frameBorder : 'no',
24379             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24380         }, this.el
24381         );
24382         
24383        // console.log(iframe);
24384         //this.wrap.dom.appendChild(iframe);
24385
24386         this.iframe = iframe.dom;
24387
24388          this.assignDocWin();
24389         
24390         this.doc.designMode = 'on';
24391        
24392         this.doc.open();
24393         this.doc.write(this.getDocMarkup());
24394         this.doc.close();
24395
24396         
24397         var task = { // must defer to wait for browser to be ready
24398             run : function(){
24399                 //console.log("run task?" + this.doc.readyState);
24400                 this.assignDocWin();
24401                 if(this.doc.body || this.doc.readyState == 'complete'){
24402                     try {
24403                         this.doc.designMode="on";
24404                     } catch (e) {
24405                         return;
24406                     }
24407                     Roo.TaskMgr.stop(task);
24408                     this.initEditor.defer(10, this);
24409                 }
24410             },
24411             interval : 10,
24412             duration:10000,
24413             scope: this
24414         };
24415         Roo.TaskMgr.start(task);
24416
24417         if(!this.width){
24418             this.setSize(this.wrap.getSize());
24419         }
24420         if (this.resizeEl) {
24421             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24422             // should trigger onReize..
24423         }
24424     },
24425
24426     // private
24427     onResize : function(w, h)
24428     {
24429         //Roo.log('resize: ' +w + ',' + h );
24430         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24431         if(this.el && this.iframe){
24432             if(typeof w == 'number'){
24433                 var aw = w - this.wrap.getFrameWidth('lr');
24434                 this.el.setWidth(this.adjustWidth('textarea', aw));
24435                 this.iframe.style.width = aw + 'px';
24436             }
24437             if(typeof h == 'number'){
24438                 var tbh = 0;
24439                 for (var i =0; i < this.toolbars.length;i++) {
24440                     // fixme - ask toolbars for heights?
24441                     tbh += this.toolbars[i].tb.el.getHeight();
24442                     if (this.toolbars[i].footer) {
24443                         tbh += this.toolbars[i].footer.el.getHeight();
24444                     }
24445                 }
24446                 
24447                 
24448                 
24449                 
24450                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24451                 ah -= 5; // knock a few pixes off for look..
24452                 this.el.setHeight(this.adjustWidth('textarea', ah));
24453                 this.iframe.style.height = ah + 'px';
24454                 if(this.doc){
24455                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24456                 }
24457             }
24458         }
24459     },
24460
24461     /**
24462      * Toggles the editor between standard and source edit mode.
24463      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24464      */
24465     toggleSourceEdit : function(sourceEditMode){
24466         
24467         this.sourceEditMode = sourceEditMode === true;
24468         
24469         if(this.sourceEditMode){
24470           
24471             this.syncValue();
24472             this.iframe.className = 'x-hidden';
24473             this.el.removeClass('x-hidden');
24474             this.el.dom.removeAttribute('tabIndex');
24475             this.el.focus();
24476         }else{
24477              
24478             this.pushValue();
24479             this.iframe.className = '';
24480             this.el.addClass('x-hidden');
24481             this.el.dom.setAttribute('tabIndex', -1);
24482             this.deferFocus();
24483         }
24484         this.setSize(this.wrap.getSize());
24485         this.fireEvent('editmodechange', this, this.sourceEditMode);
24486     },
24487
24488     // private used internally
24489     createLink : function(){
24490         var url = prompt(this.createLinkText, this.defaultLinkValue);
24491         if(url && url != 'http:/'+'/'){
24492             this.relayCmd('createlink', url);
24493         }
24494     },
24495
24496     // private (for BoxComponent)
24497     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24498
24499     // private (for BoxComponent)
24500     getResizeEl : function(){
24501         return this.wrap;
24502     },
24503
24504     // private (for BoxComponent)
24505     getPositionEl : function(){
24506         return this.wrap;
24507     },
24508
24509     // private
24510     initEvents : function(){
24511         this.originalValue = this.getValue();
24512     },
24513
24514     /**
24515      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24516      * @method
24517      */
24518     markInvalid : Roo.emptyFn,
24519     /**
24520      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24521      * @method
24522      */
24523     clearInvalid : Roo.emptyFn,
24524
24525     setValue : function(v){
24526         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24527         this.pushValue();
24528     },
24529
24530     /**
24531      * Protected method that will not generally be called directly. If you need/want
24532      * custom HTML cleanup, this is the method you should override.
24533      * @param {String} html The HTML to be cleaned
24534      * return {String} The cleaned HTML
24535      */
24536     cleanHtml : function(html){
24537         html = String(html);
24538         if(html.length > 5){
24539             if(Roo.isSafari){ // strip safari nonsense
24540                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24541             }
24542         }
24543         if(html == '&nbsp;'){
24544             html = '';
24545         }
24546         return html;
24547     },
24548
24549     /**
24550      * Protected method that will not generally be called directly. Syncs the contents
24551      * of the editor iframe with the textarea.
24552      */
24553     syncValue : function(){
24554         if(this.initialized){
24555             var bd = (this.doc.body || this.doc.documentElement);
24556             //this.cleanUpPaste();
24557             var html = bd.innerHTML;
24558             if(Roo.isSafari){
24559                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24560                 var m = bs.match(/text-align:(.*?);/i);
24561                 if(m && m[1]){
24562                     html = '<div style="'+m[0]+'">' + html + '</div>';
24563                 }
24564             }
24565             html = this.cleanHtml(html);
24566             if(this.fireEvent('beforesync', this, html) !== false){
24567                 this.el.dom.value = html;
24568                 this.fireEvent('sync', this, html);
24569             }
24570         }
24571     },
24572
24573     /**
24574      * Protected method that will not generally be called directly. Pushes the value of the textarea
24575      * into the iframe editor.
24576      */
24577     pushValue : function(){
24578         if(this.initialized){
24579             var v = this.el.dom.value;
24580             if(v.length < 1){
24581                 v = '&#160;';
24582             }
24583             
24584             if(this.fireEvent('beforepush', this, v) !== false){
24585                 var d = (this.doc.body || this.doc.documentElement);
24586                 d.innerHTML = v;
24587                 this.cleanUpPaste();
24588                 this.el.dom.value = d.innerHTML;
24589                 this.fireEvent('push', this, v);
24590             }
24591         }
24592     },
24593
24594     // private
24595     deferFocus : function(){
24596         this.focus.defer(10, this);
24597     },
24598
24599     // doc'ed in Field
24600     focus : function(){
24601         if(this.win && !this.sourceEditMode){
24602             this.win.focus();
24603         }else{
24604             this.el.focus();
24605         }
24606     },
24607     
24608     assignDocWin: function()
24609     {
24610         var iframe = this.iframe;
24611         
24612          if(Roo.isIE){
24613             this.doc = iframe.contentWindow.document;
24614             this.win = iframe.contentWindow;
24615         } else {
24616             if (!Roo.get(this.frameId)) {
24617                 return;
24618             }
24619             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24620             this.win = Roo.get(this.frameId).dom.contentWindow;
24621         }
24622     },
24623     
24624     // private
24625     initEditor : function(){
24626         //console.log("INIT EDITOR");
24627         this.assignDocWin();
24628         
24629         
24630         
24631         this.doc.designMode="on";
24632         this.doc.open();
24633         this.doc.write(this.getDocMarkup());
24634         this.doc.close();
24635         
24636         var dbody = (this.doc.body || this.doc.documentElement);
24637         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24638         // this copies styles from the containing element into thsi one..
24639         // not sure why we need all of this..
24640         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24641         ss['background-attachment'] = 'fixed'; // w3c
24642         dbody.bgProperties = 'fixed'; // ie
24643         Roo.DomHelper.applyStyles(dbody, ss);
24644         Roo.EventManager.on(this.doc, {
24645             //'mousedown': this.onEditorEvent,
24646             'mouseup': this.onEditorEvent,
24647             'dblclick': this.onEditorEvent,
24648             'click': this.onEditorEvent,
24649             'keyup': this.onEditorEvent,
24650             buffer:100,
24651             scope: this
24652         });
24653         if(Roo.isGecko){
24654             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24655         }
24656         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24657             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24658         }
24659         this.initialized = true;
24660
24661         this.fireEvent('initialize', this);
24662         this.pushValue();
24663     },
24664
24665     // private
24666     onDestroy : function(){
24667         
24668         
24669         
24670         if(this.rendered){
24671             
24672             for (var i =0; i < this.toolbars.length;i++) {
24673                 // fixme - ask toolbars for heights?
24674                 this.toolbars[i].onDestroy();
24675             }
24676             
24677             this.wrap.dom.innerHTML = '';
24678             this.wrap.remove();
24679         }
24680     },
24681
24682     // private
24683     onFirstFocus : function(){
24684         
24685         this.assignDocWin();
24686         
24687         
24688         this.activated = true;
24689         for (var i =0; i < this.toolbars.length;i++) {
24690             this.toolbars[i].onFirstFocus();
24691         }
24692        
24693         if(Roo.isGecko){ // prevent silly gecko errors
24694             this.win.focus();
24695             var s = this.win.getSelection();
24696             if(!s.focusNode || s.focusNode.nodeType != 3){
24697                 var r = s.getRangeAt(0);
24698                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24699                 r.collapse(true);
24700                 this.deferFocus();
24701             }
24702             try{
24703                 this.execCmd('useCSS', true);
24704                 this.execCmd('styleWithCSS', false);
24705             }catch(e){}
24706         }
24707         this.fireEvent('activate', this);
24708     },
24709
24710     // private
24711     adjustFont: function(btn){
24712         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24713         //if(Roo.isSafari){ // safari
24714         //    adjust *= 2;
24715        // }
24716         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24717         if(Roo.isSafari){ // safari
24718             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24719             v =  (v < 10) ? 10 : v;
24720             v =  (v > 48) ? 48 : v;
24721             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24722             
24723         }
24724         
24725         
24726         v = Math.max(1, v+adjust);
24727         
24728         this.execCmd('FontSize', v  );
24729     },
24730
24731     onEditorEvent : function(e){
24732         this.fireEvent('editorevent', this, e);
24733       //  this.updateToolbar();
24734         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24735     },
24736
24737     insertTag : function(tg)
24738     {
24739         // could be a bit smarter... -> wrap the current selected tRoo..
24740         
24741         this.execCmd("formatblock",   tg);
24742         
24743     },
24744     
24745     insertText : function(txt)
24746     {
24747         
24748         
24749         range = this.createRange();
24750         range.deleteContents();
24751                //alert(Sender.getAttribute('label'));
24752                
24753         range.insertNode(this.doc.createTextNode(txt));
24754     } ,
24755     
24756     // private
24757     relayBtnCmd : function(btn){
24758         this.relayCmd(btn.cmd);
24759     },
24760
24761     /**
24762      * Executes a Midas editor command on the editor document and performs necessary focus and
24763      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24764      * @param {String} cmd The Midas command
24765      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24766      */
24767     relayCmd : function(cmd, value){
24768         this.win.focus();
24769         this.execCmd(cmd, value);
24770         this.fireEvent('editorevent', this);
24771         //this.updateToolbar();
24772         this.deferFocus();
24773     },
24774
24775     /**
24776      * Executes a Midas editor command directly on the editor document.
24777      * For visual commands, you should use {@link #relayCmd} instead.
24778      * <b>This should only be called after the editor is initialized.</b>
24779      * @param {String} cmd The Midas command
24780      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24781      */
24782     execCmd : function(cmd, value){
24783         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24784         this.syncValue();
24785     },
24786
24787    
24788     /**
24789      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24790      * to insert tRoo.
24791      * @param {String} text
24792      */
24793     insertAtCursor : function(text){
24794         if(!this.activated){
24795             return;
24796         }
24797         if(Roo.isIE){
24798             this.win.focus();
24799             var r = this.doc.selection.createRange();
24800             if(r){
24801                 r.collapse(true);
24802                 r.pasteHTML(text);
24803                 this.syncValue();
24804                 this.deferFocus();
24805             }
24806         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24807             this.win.focus();
24808             this.execCmd('InsertHTML', text);
24809             this.deferFocus();
24810         }
24811     },
24812  // private
24813     mozKeyPress : function(e){
24814         if(e.ctrlKey){
24815             var c = e.getCharCode(), cmd;
24816           
24817             if(c > 0){
24818                 c = String.fromCharCode(c).toLowerCase();
24819                 switch(c){
24820                     case 'b':
24821                         cmd = 'bold';
24822                     break;
24823                     case 'i':
24824                         cmd = 'italic';
24825                     break;
24826                     case 'u':
24827                         cmd = 'underline';
24828                         break;
24829                     case 'v':
24830                         this.cleanUpPaste.defer(100, this);
24831                         return;
24832                     break;
24833                 }
24834                 if(cmd){
24835                     this.win.focus();
24836                     this.execCmd(cmd);
24837                     this.deferFocus();
24838                     e.preventDefault();
24839                 }
24840                 
24841             }
24842         }
24843     },
24844
24845     // private
24846     fixKeys : function(){ // load time branching for fastest keydown performance
24847         if(Roo.isIE){
24848             return function(e){
24849                 var k = e.getKey(), r;
24850                 if(k == e.TAB){
24851                     e.stopEvent();
24852                     r = this.doc.selection.createRange();
24853                     if(r){
24854                         r.collapse(true);
24855                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24856                         this.deferFocus();
24857                     }
24858                     return;
24859                 }
24860                 
24861                 if(k == e.ENTER){
24862                     r = this.doc.selection.createRange();
24863                     if(r){
24864                         var target = r.parentElement();
24865                         if(!target || target.tagName.toLowerCase() != 'li'){
24866                             e.stopEvent();
24867                             r.pasteHTML('<br />');
24868                             r.collapse(false);
24869                             r.select();
24870                         }
24871                     }
24872                 }
24873                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24874                     this.cleanUpPaste.defer(100, this);
24875                     return;
24876                 }
24877                 
24878                 
24879             };
24880         }else if(Roo.isOpera){
24881             return function(e){
24882                 var k = e.getKey();
24883                 if(k == e.TAB){
24884                     e.stopEvent();
24885                     this.win.focus();
24886                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24887                     this.deferFocus();
24888                 }
24889                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24890                     this.cleanUpPaste.defer(100, this);
24891                     return;
24892                 }
24893                 
24894             };
24895         }else if(Roo.isSafari){
24896             return function(e){
24897                 var k = e.getKey();
24898                 
24899                 if(k == e.TAB){
24900                     e.stopEvent();
24901                     this.execCmd('InsertText','\t');
24902                     this.deferFocus();
24903                     return;
24904                 }
24905                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24906                     this.cleanUpPaste.defer(100, this);
24907                     return;
24908                 }
24909                 
24910              };
24911         }
24912     }(),
24913     
24914     getAllAncestors: function()
24915     {
24916         var p = this.getSelectedNode();
24917         var a = [];
24918         if (!p) {
24919             a.push(p); // push blank onto stack..
24920             p = this.getParentElement();
24921         }
24922         
24923         
24924         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24925             a.push(p);
24926             p = p.parentNode;
24927         }
24928         a.push(this.doc.body);
24929         return a;
24930     },
24931     lastSel : false,
24932     lastSelNode : false,
24933     
24934     
24935     getSelection : function() 
24936     {
24937         this.assignDocWin();
24938         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24939     },
24940     
24941     getSelectedNode: function() 
24942     {
24943         // this may only work on Gecko!!!
24944         
24945         // should we cache this!!!!
24946         
24947         
24948         
24949          
24950         var range = this.createRange(this.getSelection()).cloneRange();
24951         
24952         if (Roo.isIE) {
24953             var parent = range.parentElement();
24954             while (true) {
24955                 var testRange = range.duplicate();
24956                 testRange.moveToElementText(parent);
24957                 if (testRange.inRange(range)) {
24958                     break;
24959                 }
24960                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24961                     break;
24962                 }
24963                 parent = parent.parentElement;
24964             }
24965             return parent;
24966         }
24967         
24968         // is ancestor a text element.
24969         var ac =  range.commonAncestorContainer;
24970         if (ac.nodeType == 3) {
24971             ac = ac.parentNode;
24972         }
24973         
24974         var ar = ac.childNodes;
24975          
24976         var nodes = [];
24977         var other_nodes = [];
24978         var has_other_nodes = false;
24979         for (var i=0;i<ar.length;i++) {
24980             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24981                 continue;
24982             }
24983             // fullly contained node.
24984             
24985             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24986                 nodes.push(ar[i]);
24987                 continue;
24988             }
24989             
24990             // probably selected..
24991             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24992                 other_nodes.push(ar[i]);
24993                 continue;
24994             }
24995             // outer..
24996             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24997                 continue;
24998             }
24999             
25000             
25001             has_other_nodes = true;
25002         }
25003         if (!nodes.length && other_nodes.length) {
25004             nodes= other_nodes;
25005         }
25006         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25007             return false;
25008         }
25009         
25010         return nodes[0];
25011     },
25012     createRange: function(sel)
25013     {
25014         // this has strange effects when using with 
25015         // top toolbar - not sure if it's a great idea.
25016         //this.editor.contentWindow.focus();
25017         if (typeof sel != "undefined") {
25018             try {
25019                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25020             } catch(e) {
25021                 return this.doc.createRange();
25022             }
25023         } else {
25024             return this.doc.createRange();
25025         }
25026     },
25027     getParentElement: function()
25028     {
25029         
25030         this.assignDocWin();
25031         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25032         
25033         var range = this.createRange(sel);
25034          
25035         try {
25036             var p = range.commonAncestorContainer;
25037             while (p.nodeType == 3) { // text node
25038                 p = p.parentNode;
25039             }
25040             return p;
25041         } catch (e) {
25042             return null;
25043         }
25044     
25045     },
25046     /***
25047      *
25048      * Range intersection.. the hard stuff...
25049      *  '-1' = before
25050      *  '0' = hits..
25051      *  '1' = after.
25052      *         [ -- selected range --- ]
25053      *   [fail]                        [fail]
25054      *
25055      *    basically..
25056      *      if end is before start or  hits it. fail.
25057      *      if start is after end or hits it fail.
25058      *
25059      *   if either hits (but other is outside. - then it's not 
25060      *   
25061      *    
25062      **/
25063     
25064     
25065     // @see http://www.thismuchiknow.co.uk/?p=64.
25066     rangeIntersectsNode : function(range, node)
25067     {
25068         var nodeRange = node.ownerDocument.createRange();
25069         try {
25070             nodeRange.selectNode(node);
25071         } catch (e) {
25072             nodeRange.selectNodeContents(node);
25073         }
25074     
25075         var rangeStartRange = range.cloneRange();
25076         rangeStartRange.collapse(true);
25077     
25078         var rangeEndRange = range.cloneRange();
25079         rangeEndRange.collapse(false);
25080     
25081         var nodeStartRange = nodeRange.cloneRange();
25082         nodeStartRange.collapse(true);
25083     
25084         var nodeEndRange = nodeRange.cloneRange();
25085         nodeEndRange.collapse(false);
25086     
25087         return rangeStartRange.compareBoundaryPoints(
25088                  Range.START_TO_START, nodeEndRange) == -1 &&
25089                rangeEndRange.compareBoundaryPoints(
25090                  Range.START_TO_START, nodeStartRange) == 1;
25091         
25092          
25093     },
25094     rangeCompareNode : function(range, node)
25095     {
25096         var nodeRange = node.ownerDocument.createRange();
25097         try {
25098             nodeRange.selectNode(node);
25099         } catch (e) {
25100             nodeRange.selectNodeContents(node);
25101         }
25102         
25103         
25104         range.collapse(true);
25105     
25106         nodeRange.collapse(true);
25107      
25108         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25109         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25110          
25111         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25112         
25113         var nodeIsBefore   =  ss == 1;
25114         var nodeIsAfter    = ee == -1;
25115         
25116         if (nodeIsBefore && nodeIsAfter)
25117             return 0; // outer
25118         if (!nodeIsBefore && nodeIsAfter)
25119             return 1; //right trailed.
25120         
25121         if (nodeIsBefore && !nodeIsAfter)
25122             return 2;  // left trailed.
25123         // fully contined.
25124         return 3;
25125     },
25126
25127     // private? - in a new class?
25128     cleanUpPaste :  function()
25129     {
25130         // cleans up the whole document..
25131          Roo.log('cleanuppaste');
25132         this.cleanUpChildren(this.doc.body);
25133         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25134         if (clean != this.doc.body.innerHTML) {
25135             this.doc.body.innerHTML = clean;
25136         }
25137         
25138     },
25139     
25140     cleanWordChars : function(input) {
25141         var he = Roo.form.HtmlEditor;
25142     
25143         var output = input;
25144         Roo.each(he.swapCodes, function(sw) { 
25145         
25146             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25147             output = output.replace(swapper, sw[1]);
25148         });
25149         return output;
25150     },
25151     
25152     
25153     cleanUpChildren : function (n)
25154     {
25155         if (!n.childNodes.length) {
25156             return;
25157         }
25158         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25159            this.cleanUpChild(n.childNodes[i]);
25160         }
25161     },
25162     
25163     
25164         
25165     
25166     cleanUpChild : function (node)
25167     {
25168         //console.log(node);
25169         if (node.nodeName == "#text") {
25170             // clean up silly Windows -- stuff?
25171             return; 
25172         }
25173         if (node.nodeName == "#comment") {
25174             node.parentNode.removeChild(node);
25175             // clean up silly Windows -- stuff?
25176             return; 
25177         }
25178         
25179         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25180             // remove node.
25181             node.parentNode.removeChild(node);
25182             return;
25183             
25184         }
25185         
25186         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25187         
25188         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25189         
25190         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25191             remove_keep_children = true;
25192         }
25193         
25194         if (remove_keep_children) {
25195             this.cleanUpChildren(node);
25196             // inserts everything just before this node...
25197             while (node.childNodes.length) {
25198                 var cn = node.childNodes[0];
25199                 node.removeChild(cn);
25200                 node.parentNode.insertBefore(cn, node);
25201             }
25202             node.parentNode.removeChild(node);
25203             return;
25204         }
25205         
25206         if (!node.attributes || !node.attributes.length) {
25207             this.cleanUpChildren(node);
25208             return;
25209         }
25210         
25211         function cleanAttr(n,v)
25212         {
25213             
25214             if (v.match(/^\./) || v.match(/^\//)) {
25215                 return;
25216             }
25217             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25218                 return;
25219             }
25220             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25221             node.removeAttribute(n);
25222             
25223         }
25224         
25225         function cleanStyle(n,v)
25226         {
25227             if (v.match(/expression/)) { //XSS?? should we even bother..
25228                 node.removeAttribute(n);
25229                 return;
25230             }
25231             
25232             
25233             var parts = v.split(/;/);
25234             Roo.each(parts, function(p) {
25235                 p = p.replace(/\s+/g,'');
25236                 if (!p.length) {
25237                     return true;
25238                 }
25239                 var l = p.split(':').shift().replace(/\s+/g,'');
25240                 
25241                 // only allow 'c whitelisted system attributes'
25242                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25243                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25244                     node.removeAttribute(n);
25245                     return false;
25246                 }
25247                 return true;
25248             });
25249             
25250             
25251         }
25252         
25253         
25254         for (var i = node.attributes.length-1; i > -1 ; i--) {
25255             var a = node.attributes[i];
25256             //console.log(a);
25257             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25258                 node.removeAttribute(a.name);
25259                 return;
25260             }
25261             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25262                 cleanAttr(a.name,a.value); // fixme..
25263                 return;
25264             }
25265             if (a.name == 'style') {
25266                 cleanStyle(a.name,a.value);
25267             }
25268             /// clean up MS crap..
25269             // tecnically this should be a list of valid class'es..
25270             
25271             
25272             if (a.name == 'class') {
25273                 if (a.value.match(/^Mso/)) {
25274                     node.className = '';
25275                 }
25276                 
25277                 if (a.value.match(/body/)) {
25278                     node.className = '';
25279                 }
25280             }
25281             
25282             // style cleanup!?
25283             // class cleanup?
25284             
25285         }
25286         
25287         
25288         this.cleanUpChildren(node);
25289         
25290         
25291     }
25292     
25293     
25294     // hide stuff that is not compatible
25295     /**
25296      * @event blur
25297      * @hide
25298      */
25299     /**
25300      * @event change
25301      * @hide
25302      */
25303     /**
25304      * @event focus
25305      * @hide
25306      */
25307     /**
25308      * @event specialkey
25309      * @hide
25310      */
25311     /**
25312      * @cfg {String} fieldClass @hide
25313      */
25314     /**
25315      * @cfg {String} focusClass @hide
25316      */
25317     /**
25318      * @cfg {String} autoCreate @hide
25319      */
25320     /**
25321      * @cfg {String} inputType @hide
25322      */
25323     /**
25324      * @cfg {String} invalidClass @hide
25325      */
25326     /**
25327      * @cfg {String} invalidText @hide
25328      */
25329     /**
25330      * @cfg {String} msgFx @hide
25331      */
25332     /**
25333      * @cfg {String} validateOnBlur @hide
25334      */
25335 });
25336
25337 Roo.form.HtmlEditor.white = [
25338         'area', 'br', 'img', 'input', 'hr', 'wbr',
25339         
25340        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25341        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25342        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25343        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25344        'table',   'ul',         'xmp', 
25345        
25346        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25347       'thead',   'tr', 
25348      
25349       'dir', 'menu', 'ol', 'ul', 'dl',
25350        
25351       'embed',  'object'
25352 ];
25353
25354
25355 Roo.form.HtmlEditor.black = [
25356     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25357         'applet', // 
25358         'base',   'basefont', 'bgsound', 'blink',  'body', 
25359         'frame',  'frameset', 'head',    'html',   'ilayer', 
25360         'iframe', 'layer',  'link',     'meta',    'object',   
25361         'script', 'style' ,'title',  'xml' // clean later..
25362 ];
25363 Roo.form.HtmlEditor.clean = [
25364     'script', 'style', 'title', 'xml'
25365 ];
25366 Roo.form.HtmlEditor.remove = [
25367     'font'
25368 ];
25369 // attributes..
25370
25371 Roo.form.HtmlEditor.ablack = [
25372     'on'
25373 ];
25374     
25375 Roo.form.HtmlEditor.aclean = [ 
25376     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25377 ];
25378
25379 // protocols..
25380 Roo.form.HtmlEditor.pwhite= [
25381         'http',  'https',  'mailto'
25382 ];
25383
25384 // white listed style attributes.
25385 Roo.form.HtmlEditor.cwhite= [
25386         'text-align',
25387         'font-size'
25388 ];
25389
25390
25391 Roo.form.HtmlEditor.swapCodes   =[ 
25392     [    8211, "--" ], 
25393     [    8212, "--" ], 
25394     [    8216,  "'" ],  
25395     [    8217, "'" ],  
25396     [    8220, '"' ],  
25397     [    8221, '"' ],  
25398     [    8226, "*" ],  
25399     [    8230, "..." ]
25400 ]; 
25401
25402     // <script type="text/javascript">
25403 /*
25404  * Based on
25405  * Ext JS Library 1.1.1
25406  * Copyright(c) 2006-2007, Ext JS, LLC.
25407  *  
25408  
25409  */
25410
25411 /**
25412  * @class Roo.form.HtmlEditorToolbar1
25413  * Basic Toolbar
25414  * 
25415  * Usage:
25416  *
25417  new Roo.form.HtmlEditor({
25418     ....
25419     toolbars : [
25420         new Roo.form.HtmlEditorToolbar1({
25421             disable : { fonts: 1 , format: 1, ..., ... , ...],
25422             btns : [ .... ]
25423         })
25424     }
25425      
25426  * 
25427  * @cfg {Object} disable List of elements to disable..
25428  * @cfg {Array} btns List of additional buttons.
25429  * 
25430  * 
25431  * NEEDS Extra CSS? 
25432  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25433  */
25434  
25435 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25436 {
25437     
25438     Roo.apply(this, config);
25439     
25440     // default disabled, based on 'good practice'..
25441     this.disable = this.disable || {};
25442     Roo.applyIf(this.disable, {
25443         fontSize : true,
25444         colors : true,
25445         specialElements : true
25446     });
25447     
25448     
25449     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25450     // dont call parent... till later.
25451 }
25452
25453 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25454     
25455     tb: false,
25456     
25457     rendered: false,
25458     
25459     editor : false,
25460     /**
25461      * @cfg {Object} disable  List of toolbar elements to disable
25462          
25463      */
25464     disable : false,
25465       /**
25466      * @cfg {Array} fontFamilies An array of available font families
25467      */
25468     fontFamilies : [
25469         'Arial',
25470         'Courier New',
25471         'Tahoma',
25472         'Times New Roman',
25473         'Verdana'
25474     ],
25475     
25476     specialChars : [
25477            "&#169;",
25478           "&#174;",     
25479           "&#8482;",    
25480           "&#163;" ,    
25481          // "&#8212;",    
25482           "&#8230;",    
25483           "&#247;" ,    
25484         //  "&#225;" ,     ?? a acute?
25485            "&#8364;"    , //Euro
25486        //   "&#8220;"    ,
25487         //  "&#8221;"    ,
25488         //  "&#8226;"    ,
25489           "&#176;"  //   , // degrees
25490
25491          // "&#233;"     , // e ecute
25492          // "&#250;"     , // u ecute?
25493     ],
25494     
25495     specialElements : [
25496         {
25497             text: "Insert Table",
25498             xtype: 'MenuItem',
25499             xns : Roo.Menu,
25500             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
25501                 
25502         },
25503         {    
25504             text: "Insert Image",
25505             xtype: 'MenuItem',
25506             xns : Roo.Menu,
25507             ihtml : '<img src="about:blank"/>'
25508             
25509         }
25510         
25511          
25512     ],
25513     
25514     
25515     inputElements : [ 
25516             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25517             "input:submit", "input:button", "select", "textarea", "label" ],
25518     formats : [
25519         ["p"] ,  
25520         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25521         ["pre"],[ "code"], 
25522         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25523     ],
25524      /**
25525      * @cfg {String} defaultFont default font to use.
25526      */
25527     defaultFont: 'tahoma',
25528    
25529     fontSelect : false,
25530     
25531     
25532     formatCombo : false,
25533     
25534     init : function(editor)
25535     {
25536         this.editor = editor;
25537         
25538         
25539         var fid = editor.frameId;
25540         var etb = this;
25541         function btn(id, toggle, handler){
25542             var xid = fid + '-'+ id ;
25543             return {
25544                 id : xid,
25545                 cmd : id,
25546                 cls : 'x-btn-icon x-edit-'+id,
25547                 enableToggle:toggle !== false,
25548                 scope: editor, // was editor...
25549                 handler:handler||editor.relayBtnCmd,
25550                 clickEvent:'mousedown',
25551                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25552                 tabIndex:-1
25553             };
25554         }
25555         
25556         
25557         
25558         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25559         this.tb = tb;
25560          // stop form submits
25561         tb.el.on('click', function(e){
25562             e.preventDefault(); // what does this do?
25563         });
25564
25565         if(!this.disable.font && !Roo.isSafari){
25566             /* why no safari for fonts
25567             editor.fontSelect = tb.el.createChild({
25568                 tag:'select',
25569                 tabIndex: -1,
25570                 cls:'x-font-select',
25571                 html: editor.createFontOptions()
25572             });
25573             editor.fontSelect.on('change', function(){
25574                 var font = editor.fontSelect.dom.value;
25575                 editor.relayCmd('fontname', font);
25576                 editor.deferFocus();
25577             }, editor);
25578             tb.add(
25579                 editor.fontSelect.dom,
25580                 '-'
25581             );
25582             */
25583         };
25584         if(!this.disable.formats){
25585             this.formatCombo = new Roo.form.ComboBox({
25586                 store: new Roo.data.SimpleStore({
25587                     id : 'tag',
25588                     fields: ['tag'],
25589                     data : this.formats // from states.js
25590                 }),
25591                 blockFocus : true,
25592                 //autoCreate : {tag: "div",  size: "20"},
25593                 displayField:'tag',
25594                 typeAhead: false,
25595                 mode: 'local',
25596                 editable : false,
25597                 triggerAction: 'all',
25598                 emptyText:'Add tag',
25599                 selectOnFocus:true,
25600                 width:135,
25601                 listeners : {
25602                     'select': function(c, r, i) {
25603                         editor.insertTag(r.get('tag'));
25604                         editor.focus();
25605                     }
25606                 }
25607
25608             });
25609             tb.addField(this.formatCombo);
25610             
25611         }
25612         
25613         if(!this.disable.format){
25614             tb.add(
25615                 btn('bold'),
25616                 btn('italic'),
25617                 btn('underline')
25618             );
25619         };
25620         if(!this.disable.fontSize){
25621             tb.add(
25622                 '-',
25623                 
25624                 
25625                 btn('increasefontsize', false, editor.adjustFont),
25626                 btn('decreasefontsize', false, editor.adjustFont)
25627             );
25628         };
25629         
25630         
25631         if(!this.disable.colors){
25632             tb.add(
25633                 '-', {
25634                     id:editor.frameId +'-forecolor',
25635                     cls:'x-btn-icon x-edit-forecolor',
25636                     clickEvent:'mousedown',
25637                     tooltip: this.buttonTips['forecolor'] || undefined,
25638                     tabIndex:-1,
25639                     menu : new Roo.menu.ColorMenu({
25640                         allowReselect: true,
25641                         focus: Roo.emptyFn,
25642                         value:'000000',
25643                         plain:true,
25644                         selectHandler: function(cp, color){
25645                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25646                             editor.deferFocus();
25647                         },
25648                         scope: editor,
25649                         clickEvent:'mousedown'
25650                     })
25651                 }, {
25652                     id:editor.frameId +'backcolor',
25653                     cls:'x-btn-icon x-edit-backcolor',
25654                     clickEvent:'mousedown',
25655                     tooltip: this.buttonTips['backcolor'] || undefined,
25656                     tabIndex:-1,
25657                     menu : new Roo.menu.ColorMenu({
25658                         focus: Roo.emptyFn,
25659                         value:'FFFFFF',
25660                         plain:true,
25661                         allowReselect: true,
25662                         selectHandler: function(cp, color){
25663                             if(Roo.isGecko){
25664                                 editor.execCmd('useCSS', false);
25665                                 editor.execCmd('hilitecolor', color);
25666                                 editor.execCmd('useCSS', true);
25667                                 editor.deferFocus();
25668                             }else{
25669                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25670                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25671                                 editor.deferFocus();
25672                             }
25673                         },
25674                         scope:editor,
25675                         clickEvent:'mousedown'
25676                     })
25677                 }
25678             );
25679         };
25680         // now add all the items...
25681         
25682
25683         if(!this.disable.alignments){
25684             tb.add(
25685                 '-',
25686                 btn('justifyleft'),
25687                 btn('justifycenter'),
25688                 btn('justifyright')
25689             );
25690         };
25691
25692         //if(!Roo.isSafari){
25693             if(!this.disable.links){
25694                 tb.add(
25695                     '-',
25696                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25697                 );
25698             };
25699
25700             if(!this.disable.lists){
25701                 tb.add(
25702                     '-',
25703                     btn('insertorderedlist'),
25704                     btn('insertunorderedlist')
25705                 );
25706             }
25707             if(!this.disable.sourceEdit){
25708                 tb.add(
25709                     '-',
25710                     btn('sourceedit', true, function(btn){
25711                         this.toggleSourceEdit(btn.pressed);
25712                     })
25713                 );
25714             }
25715         //}
25716         
25717         var smenu = { };
25718         // special menu.. - needs to be tidied up..
25719         if (!this.disable.special) {
25720             smenu = {
25721                 text: "&#169;",
25722                 cls: 'x-edit-none',
25723                 
25724                 menu : {
25725                     items : []
25726                 }
25727             };
25728             for (var i =0; i < this.specialChars.length; i++) {
25729                 smenu.menu.items.push({
25730                     
25731                     html: this.specialChars[i],
25732                     handler: function(a,b) {
25733                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25734                         
25735                     },
25736                     tabIndex:-1
25737                 });
25738             }
25739             
25740             
25741             tb.add(smenu);
25742             
25743             
25744         }
25745          
25746         if (!this.disable.specialElements) {
25747             var semenu = {
25748                 text: "Other;",
25749                 cls: 'x-edit-none',
25750                 menu : {
25751                     items : []
25752                 }
25753             };
25754             for (var i =0; i < this.specialElements.length; i++) {
25755                 semenu.menu.items.push(
25756                     Roo.apply({ 
25757                         handler: function(a,b) {
25758                             editor.insertAtCursor(this.ihtml);
25759                         }
25760                     }, this.specialElements[i])
25761                 );
25762                     
25763             }
25764             
25765             tb.add(semenu);
25766             
25767             
25768         }
25769          
25770         
25771         if (this.btns) {
25772             for(var i =0; i< this.btns.length;i++) {
25773                 var b = this.btns[i];
25774                 b.cls =  'x-edit-none';
25775                 b.scope = editor;
25776                 tb.add(b);
25777             }
25778         
25779         }
25780         
25781         
25782         
25783         // disable everything...
25784         
25785         this.tb.items.each(function(item){
25786            if(item.id != editor.frameId+ '-sourceedit'){
25787                 item.disable();
25788             }
25789         });
25790         this.rendered = true;
25791         
25792         // the all the btns;
25793         editor.on('editorevent', this.updateToolbar, this);
25794         // other toolbars need to implement this..
25795         //editor.on('editmodechange', this.updateToolbar, this);
25796     },
25797     
25798     
25799     
25800     /**
25801      * Protected method that will not generally be called directly. It triggers
25802      * a toolbar update by reading the markup state of the current selection in the editor.
25803      */
25804     updateToolbar: function(){
25805
25806         if(!this.editor.activated){
25807             this.editor.onFirstFocus();
25808             return;
25809         }
25810
25811         var btns = this.tb.items.map, 
25812             doc = this.editor.doc,
25813             frameId = this.editor.frameId;
25814
25815         if(!this.disable.font && !Roo.isSafari){
25816             /*
25817             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25818             if(name != this.fontSelect.dom.value){
25819                 this.fontSelect.dom.value = name;
25820             }
25821             */
25822         }
25823         if(!this.disable.format){
25824             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25825             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25826             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25827         }
25828         if(!this.disable.alignments){
25829             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25830             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25831             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25832         }
25833         if(!Roo.isSafari && !this.disable.lists){
25834             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25835             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25836         }
25837         
25838         var ans = this.editor.getAllAncestors();
25839         if (this.formatCombo) {
25840             
25841             
25842             var store = this.formatCombo.store;
25843             this.formatCombo.setValue("");
25844             for (var i =0; i < ans.length;i++) {
25845                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25846                     // select it..
25847                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25848                     break;
25849                 }
25850             }
25851         }
25852         
25853         
25854         
25855         // hides menus... - so this cant be on a menu...
25856         Roo.menu.MenuMgr.hideAll();
25857
25858         //this.editorsyncValue();
25859     },
25860    
25861     
25862     createFontOptions : function(){
25863         var buf = [], fs = this.fontFamilies, ff, lc;
25864         for(var i = 0, len = fs.length; i< len; i++){
25865             ff = fs[i];
25866             lc = ff.toLowerCase();
25867             buf.push(
25868                 '<option value="',lc,'" style="font-family:',ff,';"',
25869                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25870                     ff,
25871                 '</option>'
25872             );
25873         }
25874         return buf.join('');
25875     },
25876     
25877     toggleSourceEdit : function(sourceEditMode){
25878         if(sourceEditMode === undefined){
25879             sourceEditMode = !this.sourceEditMode;
25880         }
25881         this.sourceEditMode = sourceEditMode === true;
25882         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25883         // just toggle the button?
25884         if(btn.pressed !== this.editor.sourceEditMode){
25885             btn.toggle(this.editor.sourceEditMode);
25886             return;
25887         }
25888         
25889         if(this.sourceEditMode){
25890             this.tb.items.each(function(item){
25891                 if(item.cmd != 'sourceedit'){
25892                     item.disable();
25893                 }
25894             });
25895           
25896         }else{
25897             if(this.initialized){
25898                 this.tb.items.each(function(item){
25899                     item.enable();
25900                 });
25901             }
25902             
25903         }
25904         // tell the editor that it's been pressed..
25905         this.editor.toggleSourceEdit(sourceEditMode);
25906        
25907     },
25908      /**
25909      * Object collection of toolbar tooltips for the buttons in the editor. The key
25910      * is the command id associated with that button and the value is a valid QuickTips object.
25911      * For example:
25912 <pre><code>
25913 {
25914     bold : {
25915         title: 'Bold (Ctrl+B)',
25916         text: 'Make the selected text bold.',
25917         cls: 'x-html-editor-tip'
25918     },
25919     italic : {
25920         title: 'Italic (Ctrl+I)',
25921         text: 'Make the selected text italic.',
25922         cls: 'x-html-editor-tip'
25923     },
25924     ...
25925 </code></pre>
25926     * @type Object
25927      */
25928     buttonTips : {
25929         bold : {
25930             title: 'Bold (Ctrl+B)',
25931             text: 'Make the selected text bold.',
25932             cls: 'x-html-editor-tip'
25933         },
25934         italic : {
25935             title: 'Italic (Ctrl+I)',
25936             text: 'Make the selected text italic.',
25937             cls: 'x-html-editor-tip'
25938         },
25939         underline : {
25940             title: 'Underline (Ctrl+U)',
25941             text: 'Underline the selected text.',
25942             cls: 'x-html-editor-tip'
25943         },
25944         increasefontsize : {
25945             title: 'Grow Text',
25946             text: 'Increase the font size.',
25947             cls: 'x-html-editor-tip'
25948         },
25949         decreasefontsize : {
25950             title: 'Shrink Text',
25951             text: 'Decrease the font size.',
25952             cls: 'x-html-editor-tip'
25953         },
25954         backcolor : {
25955             title: 'Text Highlight Color',
25956             text: 'Change the background color of the selected text.',
25957             cls: 'x-html-editor-tip'
25958         },
25959         forecolor : {
25960             title: 'Font Color',
25961             text: 'Change the color of the selected text.',
25962             cls: 'x-html-editor-tip'
25963         },
25964         justifyleft : {
25965             title: 'Align Text Left',
25966             text: 'Align text to the left.',
25967             cls: 'x-html-editor-tip'
25968         },
25969         justifycenter : {
25970             title: 'Center Text',
25971             text: 'Center text in the editor.',
25972             cls: 'x-html-editor-tip'
25973         },
25974         justifyright : {
25975             title: 'Align Text Right',
25976             text: 'Align text to the right.',
25977             cls: 'x-html-editor-tip'
25978         },
25979         insertunorderedlist : {
25980             title: 'Bullet List',
25981             text: 'Start a bulleted list.',
25982             cls: 'x-html-editor-tip'
25983         },
25984         insertorderedlist : {
25985             title: 'Numbered List',
25986             text: 'Start a numbered list.',
25987             cls: 'x-html-editor-tip'
25988         },
25989         createlink : {
25990             title: 'Hyperlink',
25991             text: 'Make the selected text a hyperlink.',
25992             cls: 'x-html-editor-tip'
25993         },
25994         sourceedit : {
25995             title: 'Source Edit',
25996             text: 'Switch to source editing mode.',
25997             cls: 'x-html-editor-tip'
25998         }
25999     },
26000     // private
26001     onDestroy : function(){
26002         if(this.rendered){
26003             
26004             this.tb.items.each(function(item){
26005                 if(item.menu){
26006                     item.menu.removeAll();
26007                     if(item.menu.el){
26008                         item.menu.el.destroy();
26009                     }
26010                 }
26011                 item.destroy();
26012             });
26013              
26014         }
26015     },
26016     onFirstFocus: function() {
26017         this.tb.items.each(function(item){
26018            item.enable();
26019         });
26020     }
26021 });
26022
26023
26024
26025
26026 // <script type="text/javascript">
26027 /*
26028  * Based on
26029  * Ext JS Library 1.1.1
26030  * Copyright(c) 2006-2007, Ext JS, LLC.
26031  *  
26032  
26033  */
26034
26035  
26036 /**
26037  * @class Roo.form.HtmlEditor.ToolbarContext
26038  * Context Toolbar
26039  * 
26040  * Usage:
26041  *
26042  new Roo.form.HtmlEditor({
26043     ....
26044     toolbars : [
26045         { xtype: 'ToolbarStandard', styles : {} }
26046         { xtype: 'ToolbarContext', disable : {} }
26047     ]
26048 })
26049
26050      
26051  * 
26052  * @config : {Object} disable List of elements to disable.. (not done yet.)
26053  * @config : {Object} styles  Map of styles available.
26054  * 
26055  */
26056
26057 Roo.form.HtmlEditor.ToolbarContext = function(config)
26058 {
26059     
26060     Roo.apply(this, config);
26061     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26062     // dont call parent... till later.
26063     this.styles = this.styles || {};
26064 }
26065 Roo.form.HtmlEditor.ToolbarContext.types = {
26066     'IMG' : {
26067         width : {
26068             title: "Width",
26069             width: 40
26070         },
26071         height:  {
26072             title: "Height",
26073             width: 40
26074         },
26075         align: {
26076             title: "Align",
26077             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26078             width : 80
26079             
26080         },
26081         border: {
26082             title: "Border",
26083             width: 40
26084         },
26085         alt: {
26086             title: "Alt",
26087             width: 120
26088         },
26089         src : {
26090             title: "Src",
26091             width: 220
26092         }
26093         
26094     },
26095     'A' : {
26096         name : {
26097             title: "Name",
26098             width: 50
26099         },
26100         href:  {
26101             title: "Href",
26102             width: 220
26103         } // border?
26104         
26105     },
26106     'TABLE' : {
26107         rows : {
26108             title: "Rows",
26109             width: 20
26110         },
26111         cols : {
26112             title: "Cols",
26113             width: 20
26114         },
26115         width : {
26116             title: "Width",
26117             width: 40
26118         },
26119         height : {
26120             title: "Height",
26121             width: 40
26122         },
26123         border : {
26124             title: "Border",
26125             width: 20
26126         }
26127     },
26128     'TD' : {
26129         width : {
26130             title: "Width",
26131             width: 40
26132         },
26133         height : {
26134             title: "Height",
26135             width: 40
26136         },   
26137         align: {
26138             title: "Align",
26139             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26140             width: 80
26141         },
26142         valign: {
26143             title: "Valign",
26144             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26145             width: 80
26146         },
26147         colspan: {
26148             title: "Colspan",
26149             width: 20
26150             
26151         }
26152     },
26153     'INPUT' : {
26154         name : {
26155             title: "name",
26156             width: 120
26157         },
26158         value : {
26159             title: "Value",
26160             width: 120
26161         },
26162         width : {
26163             title: "Width",
26164             width: 40
26165         }
26166     },
26167     'LABEL' : {
26168         'for' : {
26169             title: "For",
26170             width: 120
26171         }
26172     },
26173     'TEXTAREA' : {
26174           name : {
26175             title: "name",
26176             width: 120
26177         },
26178         rows : {
26179             title: "Rows",
26180             width: 20
26181         },
26182         cols : {
26183             title: "Cols",
26184             width: 20
26185         }
26186     },
26187     'SELECT' : {
26188         name : {
26189             title: "name",
26190             width: 120
26191         },
26192         selectoptions : {
26193             title: "Options",
26194             width: 200
26195         }
26196     },
26197     
26198     // should we really allow this??
26199     // should this just be 
26200     'BODY' : {
26201         title : {
26202             title: "title",
26203             width: 200,
26204             disabled : true
26205         }
26206     },
26207     '*' : {
26208         // empty..
26209     }
26210 };
26211
26212
26213
26214 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26215     
26216     tb: false,
26217     
26218     rendered: false,
26219     
26220     editor : false,
26221     /**
26222      * @cfg {Object} disable  List of toolbar elements to disable
26223          
26224      */
26225     disable : false,
26226     /**
26227      * @cfg {Object} styles List of styles 
26228      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26229      *
26230      * These must be defined in the page, so they get rendered correctly..
26231      * .headline { }
26232      * TD.underline { }
26233      * 
26234      */
26235     styles : false,
26236     
26237     
26238     
26239     toolbars : false,
26240     
26241     init : function(editor)
26242     {
26243         this.editor = editor;
26244         
26245         
26246         var fid = editor.frameId;
26247         var etb = this;
26248         function btn(id, toggle, handler){
26249             var xid = fid + '-'+ id ;
26250             return {
26251                 id : xid,
26252                 cmd : id,
26253                 cls : 'x-btn-icon x-edit-'+id,
26254                 enableToggle:toggle !== false,
26255                 scope: editor, // was editor...
26256                 handler:handler||editor.relayBtnCmd,
26257                 clickEvent:'mousedown',
26258                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26259                 tabIndex:-1
26260             };
26261         }
26262         // create a new element.
26263         var wdiv = editor.wrap.createChild({
26264                 tag: 'div'
26265             }, editor.wrap.dom.firstChild.nextSibling, true);
26266         
26267         // can we do this more than once??
26268         
26269          // stop form submits
26270       
26271  
26272         // disable everything...
26273         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26274         this.toolbars = {};
26275            
26276         for (var i in  ty) {
26277           
26278             this.toolbars[i] = this.buildToolbar(ty[i],i);
26279         }
26280         this.tb = this.toolbars.BODY;
26281         this.tb.el.show();
26282         this.buildFooter();
26283         this.footer.show();
26284          
26285         this.rendered = true;
26286         
26287         // the all the btns;
26288         editor.on('editorevent', this.updateToolbar, this);
26289         // other toolbars need to implement this..
26290         //editor.on('editmodechange', this.updateToolbar, this);
26291     },
26292     
26293     
26294     
26295     /**
26296      * Protected method that will not generally be called directly. It triggers
26297      * a toolbar update by reading the markup state of the current selection in the editor.
26298      */
26299     updateToolbar: function(ignore_a,ignore_b,sel){
26300
26301         
26302         if(!this.editor.activated){
26303              this.editor.onFirstFocus();
26304             return;
26305         }
26306         var updateFooter = sel ? false : true;
26307         
26308         
26309         var ans = this.editor.getAllAncestors();
26310         
26311         // pick
26312         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26313         
26314         if (!sel) { 
26315             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26316             sel = sel ? sel : this.editor.doc.body;
26317             sel = sel.tagName.length ? sel : this.editor.doc.body;
26318             
26319         }
26320         // pick a menu that exists..
26321         var tn = sel.tagName.toUpperCase();
26322         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26323         
26324         tn = sel.tagName.toUpperCase();
26325         
26326         var lastSel = this.tb.selectedNode
26327         
26328         this.tb.selectedNode = sel;
26329         
26330         // if current menu does not match..
26331         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26332                 
26333             this.tb.el.hide();
26334             ///console.log("show: " + tn);
26335             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26336             this.tb.el.show();
26337             // update name
26338             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26339             
26340             
26341             // update attributes
26342             if (this.tb.fields) {
26343                 this.tb.fields.each(function(e) {
26344                    e.setValue(sel.getAttribute(e.name));
26345                 });
26346             }
26347             
26348             // update styles
26349             var st = this.tb.fields.item(0);
26350             st.store.removeAll();
26351             var cn = sel.className.split(/\s+/);
26352             
26353             var avs = [];
26354             if (this.styles['*']) {
26355                 
26356                 Roo.each(this.styles['*'], function(v) {
26357                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26358                 });
26359             }
26360             if (this.styles[tn]) { 
26361                 Roo.each(this.styles[tn], function(v) {
26362                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26363                 });
26364             }
26365             
26366             st.store.loadData(avs);
26367             st.collapse();
26368             st.setValue(cn);
26369             
26370             // flag our selected Node.
26371             this.tb.selectedNode = sel;
26372            
26373            
26374             Roo.menu.MenuMgr.hideAll();
26375
26376         }
26377         
26378         if (!updateFooter) {
26379             return;
26380         }
26381         // update the footer
26382         //
26383         var html = '';
26384         
26385         this.footerEls = ans.reverse();
26386         Roo.each(this.footerEls, function(a,i) {
26387             if (!a) { return; }
26388             html += html.length ? ' &gt; '  :  '';
26389             
26390             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26391             
26392         });
26393        
26394         // 
26395         var sz = this.footDisp.up('td').getSize();
26396         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26397         this.footDisp.dom.style.marginLeft = '5px';
26398         
26399         this.footDisp.dom.style.overflow = 'hidden';
26400         
26401         this.footDisp.dom.innerHTML = html;
26402             
26403         //this.editorsyncValue();
26404     },
26405    
26406        
26407     // private
26408     onDestroy : function(){
26409         if(this.rendered){
26410             
26411             this.tb.items.each(function(item){
26412                 if(item.menu){
26413                     item.menu.removeAll();
26414                     if(item.menu.el){
26415                         item.menu.el.destroy();
26416                     }
26417                 }
26418                 item.destroy();
26419             });
26420              
26421         }
26422     },
26423     onFirstFocus: function() {
26424         // need to do this for all the toolbars..
26425         this.tb.items.each(function(item){
26426            item.enable();
26427         });
26428     },
26429     buildToolbar: function(tlist, nm)
26430     {
26431         var editor = this.editor;
26432          // create a new element.
26433         var wdiv = editor.wrap.createChild({
26434                 tag: 'div'
26435             }, editor.wrap.dom.firstChild.nextSibling, true);
26436         
26437        
26438         var tb = new Roo.Toolbar(wdiv);
26439         // add the name..
26440         
26441         tb.add(nm+ ":&nbsp;");
26442         
26443         // styles...
26444         if (this.styles) {
26445             
26446             // this needs a multi-select checkbox...
26447             tb.addField( new Roo.form.ComboBox({
26448                 store: new Roo.data.SimpleStore({
26449                     id : 'val',
26450                     fields: ['val', 'selected'],
26451                     data : [] 
26452                 }),
26453                 name : 'className',
26454                 displayField:'val',
26455                 typeAhead: false,
26456                 mode: 'local',
26457                 editable : false,
26458                 triggerAction: 'all',
26459                 emptyText:'Select Style',
26460                 selectOnFocus:true,
26461                 width: 130,
26462                 listeners : {
26463                     'select': function(c, r, i) {
26464                         // initial support only for on class per el..
26465                         tb.selectedNode.className =  r ? r.get('val') : '';
26466                     }
26467                 }
26468     
26469             }));
26470         }
26471             
26472         
26473         
26474         for (var i in tlist) {
26475             
26476             var item = tlist[i];
26477             tb.add(item.title + ":&nbsp;");
26478             
26479             
26480             
26481             
26482             if (item.opts) {
26483                 // opts == pulldown..
26484                 tb.addField( new Roo.form.ComboBox({
26485                     store: new Roo.data.SimpleStore({
26486                         id : 'val',
26487                         fields: ['val'],
26488                         data : item.opts  
26489                     }),
26490                     name : i,
26491                     displayField:'val',
26492                     typeAhead: false,
26493                     mode: 'local',
26494                     editable : false,
26495                     triggerAction: 'all',
26496                     emptyText:'Select',
26497                     selectOnFocus:true,
26498                     width: item.width ? item.width  : 130,
26499                     listeners : {
26500                         'select': function(c, r, i) {
26501                             tb.selectedNode.setAttribute(c.name, r.get('val'));
26502                         }
26503                     }
26504
26505                 }));
26506                 continue;
26507                     
26508                  
26509                 
26510                 tb.addField( new Roo.form.TextField({
26511                     name: i,
26512                     width: 100,
26513                     //allowBlank:false,
26514                     value: ''
26515                 }));
26516                 continue;
26517             }
26518             tb.addField( new Roo.form.TextField({
26519                 name: i,
26520                 width: item.width,
26521                 //allowBlank:true,
26522                 value: '',
26523                 listeners: {
26524                     'change' : function(f, nv, ov) {
26525                         tb.selectedNode.setAttribute(f.name, nv);
26526                     }
26527                 }
26528             }));
26529              
26530         }
26531         tb.el.on('click', function(e){
26532             e.preventDefault(); // what does this do?
26533         });
26534         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
26535         tb.el.hide();
26536         tb.name = nm;
26537         // dont need to disable them... as they will get hidden
26538         return tb;
26539          
26540         
26541     },
26542     buildFooter : function()
26543     {
26544         
26545         var fel = this.editor.wrap.createChild();
26546         this.footer = new Roo.Toolbar(fel);
26547         // toolbar has scrolly on left / right?
26548         var footDisp= new Roo.Toolbar.Fill();
26549         var _t = this;
26550         this.footer.add(
26551             {
26552                 text : '&lt;',
26553                 xtype: 'Button',
26554                 handler : function() {
26555                     _t.footDisp.scrollTo('left',0,true)
26556                 }
26557             }
26558         );
26559         this.footer.add( footDisp );
26560         this.footer.add( 
26561             {
26562                 text : '&gt;',
26563                 xtype: 'Button',
26564                 handler : function() {
26565                     // no animation..
26566                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
26567                 }
26568             }
26569         );
26570         var fel = Roo.get(footDisp.el);
26571         fel.addClass('x-editor-context');
26572         this.footDispWrap = fel; 
26573         this.footDispWrap.overflow  = 'hidden';
26574         
26575         this.footDisp = fel.createChild();
26576         this.footDispWrap.on('click', this.onContextClick, this)
26577         
26578         
26579     },
26580     onContextClick : function (ev,dom)
26581     {
26582         ev.preventDefault();
26583         var  cn = dom.className;
26584         Roo.log(cn);
26585         if (!cn.match(/x-ed-loc-/)) {
26586             return;
26587         }
26588         var n = cn.split('-').pop();
26589         var ans = this.footerEls;
26590         var sel = ans[n];
26591         
26592          // pick
26593         var range = this.editor.createRange();
26594         
26595         range.selectNodeContents(sel);
26596         //range.selectNode(sel);
26597         
26598         
26599         var selection = this.editor.getSelection();
26600         selection.removeAllRanges();
26601         selection.addRange(range);
26602         
26603         
26604         
26605         this.updateToolbar(null, null, sel);
26606         
26607         
26608     }
26609     
26610     
26611     
26612     
26613     
26614 });
26615
26616
26617
26618
26619
26620 /*
26621  * Based on:
26622  * Ext JS Library 1.1.1
26623  * Copyright(c) 2006-2007, Ext JS, LLC.
26624  *
26625  * Originally Released Under LGPL - original licence link has changed is not relivant.
26626  *
26627  * Fork - LGPL
26628  * <script type="text/javascript">
26629  */
26630  
26631 /**
26632  * @class Roo.form.BasicForm
26633  * @extends Roo.util.Observable
26634  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26635  * @constructor
26636  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26637  * @param {Object} config Configuration options
26638  */
26639 Roo.form.BasicForm = function(el, config){
26640     this.allItems = [];
26641     this.childForms = [];
26642     Roo.apply(this, config);
26643     /*
26644      * The Roo.form.Field items in this form.
26645      * @type MixedCollection
26646      */
26647      
26648      
26649     this.items = new Roo.util.MixedCollection(false, function(o){
26650         return o.id || (o.id = Roo.id());
26651     });
26652     this.addEvents({
26653         /**
26654          * @event beforeaction
26655          * Fires before any action is performed. Return false to cancel the action.
26656          * @param {Form} this
26657          * @param {Action} action The action to be performed
26658          */
26659         beforeaction: true,
26660         /**
26661          * @event actionfailed
26662          * Fires when an action fails.
26663          * @param {Form} this
26664          * @param {Action} action The action that failed
26665          */
26666         actionfailed : true,
26667         /**
26668          * @event actioncomplete
26669          * Fires when an action is completed.
26670          * @param {Form} this
26671          * @param {Action} action The action that completed
26672          */
26673         actioncomplete : true
26674     });
26675     if(el){
26676         this.initEl(el);
26677     }
26678     Roo.form.BasicForm.superclass.constructor.call(this);
26679 };
26680
26681 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26682     /**
26683      * @cfg {String} method
26684      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26685      */
26686     /**
26687      * @cfg {DataReader} reader
26688      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26689      * This is optional as there is built-in support for processing JSON.
26690      */
26691     /**
26692      * @cfg {DataReader} errorReader
26693      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26694      * This is completely optional as there is built-in support for processing JSON.
26695      */
26696     /**
26697      * @cfg {String} url
26698      * The URL to use for form actions if one isn't supplied in the action options.
26699      */
26700     /**
26701      * @cfg {Boolean} fileUpload
26702      * Set to true if this form is a file upload.
26703      */
26704      
26705     /**
26706      * @cfg {Object} baseParams
26707      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26708      */
26709      /**
26710      
26711     /**
26712      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26713      */
26714     timeout: 30,
26715
26716     // private
26717     activeAction : null,
26718
26719     /**
26720      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26721      * or setValues() data instead of when the form was first created.
26722      */
26723     trackResetOnLoad : false,
26724     
26725     
26726     /**
26727      * childForms - used for multi-tab forms
26728      * @type {Array}
26729      */
26730     childForms : false,
26731     
26732     /**
26733      * allItems - full list of fields.
26734      * @type {Array}
26735      */
26736     allItems : false,
26737     
26738     /**
26739      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26740      * element by passing it or its id or mask the form itself by passing in true.
26741      * @type Mixed
26742      */
26743     waitMsgTarget : false,
26744
26745     // private
26746     initEl : function(el){
26747         this.el = Roo.get(el);
26748         this.id = this.el.id || Roo.id();
26749         this.el.on('submit', this.onSubmit, this);
26750         this.el.addClass('x-form');
26751     },
26752
26753     // private
26754     onSubmit : function(e){
26755         e.stopEvent();
26756     },
26757
26758     /**
26759      * Returns true if client-side validation on the form is successful.
26760      * @return Boolean
26761      */
26762     isValid : function(){
26763         var valid = true;
26764         this.items.each(function(f){
26765            if(!f.validate()){
26766                valid = false;
26767            }
26768         });
26769         return valid;
26770     },
26771
26772     /**
26773      * Returns true if any fields in this form have changed since their original load.
26774      * @return Boolean
26775      */
26776     isDirty : function(){
26777         var dirty = false;
26778         this.items.each(function(f){
26779            if(f.isDirty()){
26780                dirty = true;
26781                return false;
26782            }
26783         });
26784         return dirty;
26785     },
26786
26787     /**
26788      * Performs a predefined action (submit or load) or custom actions you define on this form.
26789      * @param {String} actionName The name of the action type
26790      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26791      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26792      * accept other config options):
26793      * <pre>
26794 Property          Type             Description
26795 ----------------  ---------------  ----------------------------------------------------------------------------------
26796 url               String           The url for the action (defaults to the form's url)
26797 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26798 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26799 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26800                                    validate the form on the client (defaults to false)
26801      * </pre>
26802      * @return {BasicForm} this
26803      */
26804     doAction : function(action, options){
26805         if(typeof action == 'string'){
26806             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26807         }
26808         if(this.fireEvent('beforeaction', this, action) !== false){
26809             this.beforeAction(action);
26810             action.run.defer(100, action);
26811         }
26812         return this;
26813     },
26814
26815     /**
26816      * Shortcut to do a submit action.
26817      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26818      * @return {BasicForm} this
26819      */
26820     submit : function(options){
26821         this.doAction('submit', options);
26822         return this;
26823     },
26824
26825     /**
26826      * Shortcut to do a load action.
26827      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26828      * @return {BasicForm} this
26829      */
26830     load : function(options){
26831         this.doAction('load', options);
26832         return this;
26833     },
26834
26835     /**
26836      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26837      * @param {Record} record The record to edit
26838      * @return {BasicForm} this
26839      */
26840     updateRecord : function(record){
26841         record.beginEdit();
26842         var fs = record.fields;
26843         fs.each(function(f){
26844             var field = this.findField(f.name);
26845             if(field){
26846                 record.set(f.name, field.getValue());
26847             }
26848         }, this);
26849         record.endEdit();
26850         return this;
26851     },
26852
26853     /**
26854      * Loads an Roo.data.Record into this form.
26855      * @param {Record} record The record to load
26856      * @return {BasicForm} this
26857      */
26858     loadRecord : function(record){
26859         this.setValues(record.data);
26860         return this;
26861     },
26862
26863     // private
26864     beforeAction : function(action){
26865         var o = action.options;
26866         
26867        
26868         if(this.waitMsgTarget === true){
26869             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26870         }else if(this.waitMsgTarget){
26871             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26872             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26873         }else {
26874             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26875         }
26876          
26877     },
26878
26879     // private
26880     afterAction : function(action, success){
26881         this.activeAction = null;
26882         var o = action.options;
26883         
26884         if(this.waitMsgTarget === true){
26885             this.el.unmask();
26886         }else if(this.waitMsgTarget){
26887             this.waitMsgTarget.unmask();
26888         }else{
26889             Roo.MessageBox.updateProgress(1);
26890             Roo.MessageBox.hide();
26891         }
26892          
26893         if(success){
26894             if(o.reset){
26895                 this.reset();
26896             }
26897             Roo.callback(o.success, o.scope, [this, action]);
26898             this.fireEvent('actioncomplete', this, action);
26899             
26900         }else{
26901             Roo.callback(o.failure, o.scope, [this, action]);
26902             // show an error message if no failed handler is set..
26903             if (!this.hasListener('actionfailed')) {
26904                 Roo.MessageBox.alert("Error",
26905                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
26906                         action.result.errorMsg :
26907                         "Saving Failed, please check your entries"
26908                 );
26909             }
26910             
26911             this.fireEvent('actionfailed', this, action);
26912         }
26913         
26914     },
26915
26916     /**
26917      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26918      * @param {String} id The value to search for
26919      * @return Field
26920      */
26921     findField : function(id){
26922         var field = this.items.get(id);
26923         if(!field){
26924             this.items.each(function(f){
26925                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26926                     field = f;
26927                     return false;
26928                 }
26929             });
26930         }
26931         return field || null;
26932     },
26933
26934     /**
26935      * Add a secondary form to this one, 
26936      * Used to provide tabbed forms. One form is primary, with hidden values 
26937      * which mirror the elements from the other forms.
26938      * 
26939      * @param {Roo.form.Form} form to add.
26940      * 
26941      */
26942     addForm : function(form)
26943     {
26944        
26945         if (this.childForms.indexOf(form) > -1) {
26946             // already added..
26947             return;
26948         }
26949         this.childForms.push(form);
26950         var n = '';
26951         Roo.each(form.allItems, function (fe) {
26952             
26953             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26954             if (this.findField(n)) { // already added..
26955                 return;
26956             }
26957             var add = new Roo.form.Hidden({
26958                 name : n
26959             });
26960             add.render(this.el);
26961             
26962             this.add( add );
26963         }, this);
26964         
26965     },
26966     /**
26967      * Mark fields in this form invalid in bulk.
26968      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26969      * @return {BasicForm} this
26970      */
26971     markInvalid : function(errors){
26972         if(errors instanceof Array){
26973             for(var i = 0, len = errors.length; i < len; i++){
26974                 var fieldError = errors[i];
26975                 var f = this.findField(fieldError.id);
26976                 if(f){
26977                     f.markInvalid(fieldError.msg);
26978                 }
26979             }
26980         }else{
26981             var field, id;
26982             for(id in errors){
26983                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26984                     field.markInvalid(errors[id]);
26985                 }
26986             }
26987         }
26988         Roo.each(this.childForms || [], function (f) {
26989             f.markInvalid(errors);
26990         });
26991         
26992         return this;
26993     },
26994
26995     /**
26996      * Set values for fields in this form in bulk.
26997      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26998      * @return {BasicForm} this
26999      */
27000     setValues : function(values){
27001         if(values instanceof Array){ // array of objects
27002             for(var i = 0, len = values.length; i < len; i++){
27003                 var v = values[i];
27004                 var f = this.findField(v.id);
27005                 if(f){
27006                     f.setValue(v.value);
27007                     if(this.trackResetOnLoad){
27008                         f.originalValue = f.getValue();
27009                     }
27010                 }
27011             }
27012         }else{ // object hash
27013             var field, id;
27014             for(id in values){
27015                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27016                     
27017                     if (field.setFromData && 
27018                         field.valueField && 
27019                         field.displayField &&
27020                         // combos' with local stores can 
27021                         // be queried via setValue()
27022                         // to set their value..
27023                         (field.store && !field.store.isLocal)
27024                         ) {
27025                         // it's a combo
27026                         var sd = { };
27027                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27028                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27029                         field.setFromData(sd);
27030                         
27031                     } else {
27032                         field.setValue(values[id]);
27033                     }
27034                     
27035                     
27036                     if(this.trackResetOnLoad){
27037                         field.originalValue = field.getValue();
27038                     }
27039                 }
27040             }
27041         }
27042          
27043         Roo.each(this.childForms || [], function (f) {
27044             f.setValues(values);
27045         });
27046                 
27047         return this;
27048     },
27049
27050     /**
27051      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27052      * they are returned as an array.
27053      * @param {Boolean} asString
27054      * @return {Object}
27055      */
27056     getValues : function(asString){
27057         if (this.childForms) {
27058             // copy values from the child forms
27059             Roo.each(this.childForms, function (f) {
27060                 this.setValues(f.getValues());
27061             }, this);
27062         }
27063         
27064         
27065         
27066         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27067         if(asString === true){
27068             return fs;
27069         }
27070         return Roo.urlDecode(fs);
27071     },
27072     
27073     /**
27074      * Returns the fields in this form as an object with key/value pairs. 
27075      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27076      * @return {Object}
27077      */
27078     getFieldValues : function(with_hidden)
27079     {
27080         if (this.childForms) {
27081             // copy values from the child forms
27082             // should this call getFieldValues - probably not as we do not currently copy
27083             // hidden fields when we generate..
27084             Roo.each(this.childForms, function (f) {
27085                 this.setValues(f.getValues());
27086             }, this);
27087         }
27088         
27089         var ret = {};
27090         this.items.each(function(f){
27091             if (!f.getName()) {
27092                 return;
27093             }
27094             var v = f.getValue();
27095             // not sure if this supported any more..
27096             if ((typeof(v) == 'object') && f.getRawValue) {
27097                 v = f.getRawValue() ; // dates..
27098             }
27099             // combo boxes where name != hiddenName...
27100             if (f.name != f.getName()) {
27101                 ret[f.name] = f.getRawValue();
27102             }
27103             ret[f.getName()] = v;
27104         });
27105         
27106         return ret;
27107     },
27108
27109     /**
27110      * Clears all invalid messages in this form.
27111      * @return {BasicForm} this
27112      */
27113     clearInvalid : function(){
27114         this.items.each(function(f){
27115            f.clearInvalid();
27116         });
27117         
27118         Roo.each(this.childForms || [], function (f) {
27119             f.clearInvalid();
27120         });
27121         
27122         
27123         return this;
27124     },
27125
27126     /**
27127      * Resets this form.
27128      * @return {BasicForm} this
27129      */
27130     reset : function(){
27131         this.items.each(function(f){
27132             f.reset();
27133         });
27134         
27135         Roo.each(this.childForms || [], function (f) {
27136             f.reset();
27137         });
27138        
27139         
27140         return this;
27141     },
27142
27143     /**
27144      * Add Roo.form components to this form.
27145      * @param {Field} field1
27146      * @param {Field} field2 (optional)
27147      * @param {Field} etc (optional)
27148      * @return {BasicForm} this
27149      */
27150     add : function(){
27151         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27152         return this;
27153     },
27154
27155
27156     /**
27157      * Removes a field from the items collection (does NOT remove its markup).
27158      * @param {Field} field
27159      * @return {BasicForm} this
27160      */
27161     remove : function(field){
27162         this.items.remove(field);
27163         return this;
27164     },
27165
27166     /**
27167      * Looks at the fields in this form, checks them for an id attribute,
27168      * and calls applyTo on the existing dom element with that id.
27169      * @return {BasicForm} this
27170      */
27171     render : function(){
27172         this.items.each(function(f){
27173             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27174                 f.applyTo(f.id);
27175             }
27176         });
27177         return this;
27178     },
27179
27180     /**
27181      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27182      * @param {Object} values
27183      * @return {BasicForm} this
27184      */
27185     applyToFields : function(o){
27186         this.items.each(function(f){
27187            Roo.apply(f, o);
27188         });
27189         return this;
27190     },
27191
27192     /**
27193      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27194      * @param {Object} values
27195      * @return {BasicForm} this
27196      */
27197     applyIfToFields : function(o){
27198         this.items.each(function(f){
27199            Roo.applyIf(f, o);
27200         });
27201         return this;
27202     }
27203 });
27204
27205 // back compat
27206 Roo.BasicForm = Roo.form.BasicForm;/*
27207  * Based on:
27208  * Ext JS Library 1.1.1
27209  * Copyright(c) 2006-2007, Ext JS, LLC.
27210  *
27211  * Originally Released Under LGPL - original licence link has changed is not relivant.
27212  *
27213  * Fork - LGPL
27214  * <script type="text/javascript">
27215  */
27216
27217 /**
27218  * @class Roo.form.Form
27219  * @extends Roo.form.BasicForm
27220  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27221  * @constructor
27222  * @param {Object} config Configuration options
27223  */
27224 Roo.form.Form = function(config){
27225     var xitems =  [];
27226     if (config.items) {
27227         xitems = config.items;
27228         delete config.items;
27229     }
27230    
27231     
27232     Roo.form.Form.superclass.constructor.call(this, null, config);
27233     this.url = this.url || this.action;
27234     if(!this.root){
27235         this.root = new Roo.form.Layout(Roo.applyIf({
27236             id: Roo.id()
27237         }, config));
27238     }
27239     this.active = this.root;
27240     /**
27241      * Array of all the buttons that have been added to this form via {@link addButton}
27242      * @type Array
27243      */
27244     this.buttons = [];
27245     this.allItems = [];
27246     this.addEvents({
27247         /**
27248          * @event clientvalidation
27249          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27250          * @param {Form} this
27251          * @param {Boolean} valid true if the form has passed client-side validation
27252          */
27253         clientvalidation: true,
27254         /**
27255          * @event rendered
27256          * Fires when the form is rendered
27257          * @param {Roo.form.Form} form
27258          */
27259         rendered : true
27260     });
27261     
27262     if (this.progressUrl) {
27263             // push a hidden field onto the list of fields..
27264             this.addxtype( {
27265                     xns: Roo.form, 
27266                     xtype : 'Hidden', 
27267                     name : 'UPLOAD_IDENTIFIER' 
27268             });
27269         }
27270         
27271     
27272     Roo.each(xitems, this.addxtype, this);
27273     
27274     
27275     
27276 };
27277
27278 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27279     /**
27280      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27281      */
27282     /**
27283      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27284      */
27285     /**
27286      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27287      */
27288     buttonAlign:'center',
27289
27290     /**
27291      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27292      */
27293     minButtonWidth:75,
27294
27295     /**
27296      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27297      * This property cascades to child containers if not set.
27298      */
27299     labelAlign:'left',
27300
27301     /**
27302      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27303      * fires a looping event with that state. This is required to bind buttons to the valid
27304      * state using the config value formBind:true on the button.
27305      */
27306     monitorValid : false,
27307
27308     /**
27309      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27310      */
27311     monitorPoll : 200,
27312     
27313     /**
27314      * @cfg {String} progressUrl - Url to return progress data 
27315      */
27316     
27317     progressUrl : false,
27318   
27319     /**
27320      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27321      * fields are added and the column is closed. If no fields are passed the column remains open
27322      * until end() is called.
27323      * @param {Object} config The config to pass to the column
27324      * @param {Field} field1 (optional)
27325      * @param {Field} field2 (optional)
27326      * @param {Field} etc (optional)
27327      * @return Column The column container object
27328      */
27329     column : function(c){
27330         var col = new Roo.form.Column(c);
27331         this.start(col);
27332         if(arguments.length > 1){ // duplicate code required because of Opera
27333             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27334             this.end();
27335         }
27336         return col;
27337     },
27338
27339     /**
27340      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27341      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27342      * until end() is called.
27343      * @param {Object} config The config to pass to the fieldset
27344      * @param {Field} field1 (optional)
27345      * @param {Field} field2 (optional)
27346      * @param {Field} etc (optional)
27347      * @return FieldSet The fieldset container object
27348      */
27349     fieldset : function(c){
27350         var fs = new Roo.form.FieldSet(c);
27351         this.start(fs);
27352         if(arguments.length > 1){ // duplicate code required because of Opera
27353             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27354             this.end();
27355         }
27356         return fs;
27357     },
27358
27359     /**
27360      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27361      * fields are added and the container is closed. If no fields are passed the container remains open
27362      * until end() is called.
27363      * @param {Object} config The config to pass to the Layout
27364      * @param {Field} field1 (optional)
27365      * @param {Field} field2 (optional)
27366      * @param {Field} etc (optional)
27367      * @return Layout The container object
27368      */
27369     container : function(c){
27370         var l = new Roo.form.Layout(c);
27371         this.start(l);
27372         if(arguments.length > 1){ // duplicate code required because of Opera
27373             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27374             this.end();
27375         }
27376         return l;
27377     },
27378
27379     /**
27380      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27381      * @param {Object} container A Roo.form.Layout or subclass of Layout
27382      * @return {Form} this
27383      */
27384     start : function(c){
27385         // cascade label info
27386         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27387         this.active.stack.push(c);
27388         c.ownerCt = this.active;
27389         this.active = c;
27390         return this;
27391     },
27392
27393     /**
27394      * Closes the current open container
27395      * @return {Form} this
27396      */
27397     end : function(){
27398         if(this.active == this.root){
27399             return this;
27400         }
27401         this.active = this.active.ownerCt;
27402         return this;
27403     },
27404
27405     /**
27406      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27407      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27408      * as the label of the field.
27409      * @param {Field} field1
27410      * @param {Field} field2 (optional)
27411      * @param {Field} etc. (optional)
27412      * @return {Form} this
27413      */
27414     add : function(){
27415         this.active.stack.push.apply(this.active.stack, arguments);
27416         this.allItems.push.apply(this.allItems,arguments);
27417         var r = [];
27418         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27419             if(a[i].isFormField){
27420                 r.push(a[i]);
27421             }
27422         }
27423         if(r.length > 0){
27424             Roo.form.Form.superclass.add.apply(this, r);
27425         }
27426         return this;
27427     },
27428     
27429
27430     
27431     
27432     
27433      /**
27434      * Find any element that has been added to a form, using it's ID or name
27435      * This can include framesets, columns etc. along with regular fields..
27436      * @param {String} id - id or name to find.
27437      
27438      * @return {Element} e - or false if nothing found.
27439      */
27440     findbyId : function(id)
27441     {
27442         var ret = false;
27443         if (!id) {
27444             return ret;
27445         }
27446         Roo.each(this.allItems, function(f){
27447             if (f.id == id || f.name == id ){
27448                 ret = f;
27449                 return false;
27450             }
27451         });
27452         return ret;
27453     },
27454
27455     
27456     
27457     /**
27458      * Render this form into the passed container. This should only be called once!
27459      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27460      * @return {Form} this
27461      */
27462     render : function(ct)
27463     {
27464         
27465         
27466         
27467         ct = Roo.get(ct);
27468         var o = this.autoCreate || {
27469             tag: 'form',
27470             method : this.method || 'POST',
27471             id : this.id || Roo.id()
27472         };
27473         this.initEl(ct.createChild(o));
27474
27475         this.root.render(this.el);
27476         
27477        
27478              
27479         this.items.each(function(f){
27480             f.render('x-form-el-'+f.id);
27481         });
27482
27483         if(this.buttons.length > 0){
27484             // tables are required to maintain order and for correct IE layout
27485             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27486                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27487                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27488             }}, null, true);
27489             var tr = tb.getElementsByTagName('tr')[0];
27490             for(var i = 0, len = this.buttons.length; i < len; i++) {
27491                 var b = this.buttons[i];
27492                 var td = document.createElement('td');
27493                 td.className = 'x-form-btn-td';
27494                 b.render(tr.appendChild(td));
27495             }
27496         }
27497         if(this.monitorValid){ // initialize after render
27498             this.startMonitoring();
27499         }
27500         this.fireEvent('rendered', this);
27501         return this;
27502     },
27503
27504     /**
27505      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27506      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27507      * object or a valid Roo.DomHelper element config
27508      * @param {Function} handler The function called when the button is clicked
27509      * @param {Object} scope (optional) The scope of the handler function
27510      * @return {Roo.Button}
27511      */
27512     addButton : function(config, handler, scope){
27513         var bc = {
27514             handler: handler,
27515             scope: scope,
27516             minWidth: this.minButtonWidth,
27517             hideParent:true
27518         };
27519         if(typeof config == "string"){
27520             bc.text = config;
27521         }else{
27522             Roo.apply(bc, config);
27523         }
27524         var btn = new Roo.Button(null, bc);
27525         this.buttons.push(btn);
27526         return btn;
27527     },
27528
27529      /**
27530      * Adds a series of form elements (using the xtype property as the factory method.
27531      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27532      * @param {Object} config 
27533      */
27534     
27535     addxtype : function()
27536     {
27537         var ar = Array.prototype.slice.call(arguments, 0);
27538         var ret = false;
27539         for(var i = 0; i < ar.length; i++) {
27540             if (!ar[i]) {
27541                 continue; // skip -- if this happends something invalid got sent, we 
27542                 // should ignore it, as basically that interface element will not show up
27543                 // and that should be pretty obvious!!
27544             }
27545             
27546             if (Roo.form[ar[i].xtype]) {
27547                 ar[i].form = this;
27548                 var fe = Roo.factory(ar[i], Roo.form);
27549                 if (!ret) {
27550                     ret = fe;
27551                 }
27552                 fe.form = this;
27553                 if (fe.store) {
27554                     fe.store.form = this;
27555                 }
27556                 if (fe.isLayout) {  
27557                          
27558                     this.start(fe);
27559                     this.allItems.push(fe);
27560                     if (fe.items && fe.addxtype) {
27561                         fe.addxtype.apply(fe, fe.items);
27562                         delete fe.items;
27563                     }
27564                      this.end();
27565                     continue;
27566                 }
27567                 
27568                 
27569                  
27570                 this.add(fe);
27571               //  console.log('adding ' + ar[i].xtype);
27572             }
27573             if (ar[i].xtype == 'Button') {  
27574                 //console.log('adding button');
27575                 //console.log(ar[i]);
27576                 this.addButton(ar[i]);
27577                 this.allItems.push(fe);
27578                 continue;
27579             }
27580             
27581             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27582                 alert('end is not supported on xtype any more, use items');
27583             //    this.end();
27584             //    //console.log('adding end');
27585             }
27586             
27587         }
27588         return ret;
27589     },
27590     
27591     /**
27592      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27593      * option "monitorValid"
27594      */
27595     startMonitoring : function(){
27596         if(!this.bound){
27597             this.bound = true;
27598             Roo.TaskMgr.start({
27599                 run : this.bindHandler,
27600                 interval : this.monitorPoll || 200,
27601                 scope: this
27602             });
27603         }
27604     },
27605
27606     /**
27607      * Stops monitoring of the valid state of this form
27608      */
27609     stopMonitoring : function(){
27610         this.bound = false;
27611     },
27612
27613     // private
27614     bindHandler : function(){
27615         if(!this.bound){
27616             return false; // stops binding
27617         }
27618         var valid = true;
27619         this.items.each(function(f){
27620             if(!f.isValid(true)){
27621                 valid = false;
27622                 return false;
27623             }
27624         });
27625         for(var i = 0, len = this.buttons.length; i < len; i++){
27626             var btn = this.buttons[i];
27627             if(btn.formBind === true && btn.disabled === valid){
27628                 btn.setDisabled(!valid);
27629             }
27630         }
27631         this.fireEvent('clientvalidation', this, valid);
27632     }
27633     
27634     
27635     
27636     
27637     
27638     
27639     
27640     
27641 });
27642
27643
27644 // back compat
27645 Roo.Form = Roo.form.Form;
27646 /*
27647  * Based on:
27648  * Ext JS Library 1.1.1
27649  * Copyright(c) 2006-2007, Ext JS, LLC.
27650  *
27651  * Originally Released Under LGPL - original licence link has changed is not relivant.
27652  *
27653  * Fork - LGPL
27654  * <script type="text/javascript">
27655  */
27656  
27657  /**
27658  * @class Roo.form.Action
27659  * Internal Class used to handle form actions
27660  * @constructor
27661  * @param {Roo.form.BasicForm} el The form element or its id
27662  * @param {Object} config Configuration options
27663  */
27664  
27665  
27666 // define the action interface
27667 Roo.form.Action = function(form, options){
27668     this.form = form;
27669     this.options = options || {};
27670 };
27671 /**
27672  * Client Validation Failed
27673  * @const 
27674  */
27675 Roo.form.Action.CLIENT_INVALID = 'client';
27676 /**
27677  * Server Validation Failed
27678  * @const 
27679  */
27680  Roo.form.Action.SERVER_INVALID = 'server';
27681  /**
27682  * Connect to Server Failed
27683  * @const 
27684  */
27685 Roo.form.Action.CONNECT_FAILURE = 'connect';
27686 /**
27687  * Reading Data from Server Failed
27688  * @const 
27689  */
27690 Roo.form.Action.LOAD_FAILURE = 'load';
27691
27692 Roo.form.Action.prototype = {
27693     type : 'default',
27694     failureType : undefined,
27695     response : undefined,
27696     result : undefined,
27697
27698     // interface method
27699     run : function(options){
27700
27701     },
27702
27703     // interface method
27704     success : function(response){
27705
27706     },
27707
27708     // interface method
27709     handleResponse : function(response){
27710
27711     },
27712
27713     // default connection failure
27714     failure : function(response){
27715         
27716         this.response = response;
27717         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27718         this.form.afterAction(this, false);
27719     },
27720
27721     processResponse : function(response){
27722         this.response = response;
27723         if(!response.responseText){
27724             return true;
27725         }
27726         this.result = this.handleResponse(response);
27727         return this.result;
27728     },
27729
27730     // utility functions used internally
27731     getUrl : function(appendParams){
27732         var url = this.options.url || this.form.url || this.form.el.dom.action;
27733         if(appendParams){
27734             var p = this.getParams();
27735             if(p){
27736                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27737             }
27738         }
27739         return url;
27740     },
27741
27742     getMethod : function(){
27743         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27744     },
27745
27746     getParams : function(){
27747         var bp = this.form.baseParams;
27748         var p = this.options.params;
27749         if(p){
27750             if(typeof p == "object"){
27751                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27752             }else if(typeof p == 'string' && bp){
27753                 p += '&' + Roo.urlEncode(bp);
27754             }
27755         }else if(bp){
27756             p = Roo.urlEncode(bp);
27757         }
27758         return p;
27759     },
27760
27761     createCallback : function(){
27762         return {
27763             success: this.success,
27764             failure: this.failure,
27765             scope: this,
27766             timeout: (this.form.timeout*1000),
27767             upload: this.form.fileUpload ? this.success : undefined
27768         };
27769     }
27770 };
27771
27772 Roo.form.Action.Submit = function(form, options){
27773     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27774 };
27775
27776 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27777     type : 'submit',
27778
27779     haveProgress : false,
27780     uploadComplete : false,
27781     
27782     // uploadProgress indicator.
27783     uploadProgress : function()
27784     {
27785         if (!this.form.progressUrl) {
27786             return;
27787         }
27788         
27789         if (!this.haveProgress) {
27790             Roo.MessageBox.progress("Uploading", "Uploading");
27791         }
27792         if (this.uploadComplete) {
27793            Roo.MessageBox.hide();
27794            return;
27795         }
27796         
27797         this.haveProgress = true;
27798    
27799         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27800         
27801         var c = new Roo.data.Connection();
27802         c.request({
27803             url : this.form.progressUrl,
27804             params: {
27805                 id : uid
27806             },
27807             method: 'GET',
27808             success : function(req){
27809                //console.log(data);
27810                 var rdata = false;
27811                 var edata;
27812                 try  {
27813                    rdata = Roo.decode(req.responseText)
27814                 } catch (e) {
27815                     Roo.log("Invalid data from server..");
27816                     Roo.log(edata);
27817                     return;
27818                 }
27819                 if (!rdata || !rdata.success) {
27820                     Roo.log(rdata);
27821                     return;
27822                 }
27823                 var data = rdata.data;
27824                 
27825                 if (this.uploadComplete) {
27826                    Roo.MessageBox.hide();
27827                    return;
27828                 }
27829                    
27830                 if (data){
27831                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27832                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27833                     );
27834                 }
27835                 this.uploadProgress.defer(2000,this);
27836             },
27837        
27838             failure: function(data) {
27839                 Roo.log('progress url failed ');
27840                 Roo.log(data);
27841             },
27842             scope : this
27843         });
27844            
27845     },
27846     
27847     
27848     run : function()
27849     {
27850         // run get Values on the form, so it syncs any secondary forms.
27851         this.form.getValues();
27852         
27853         var o = this.options;
27854         var method = this.getMethod();
27855         var isPost = method == 'POST';
27856         if(o.clientValidation === false || this.form.isValid()){
27857             
27858             if (this.form.progressUrl) {
27859                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27860                     (new Date() * 1) + '' + Math.random());
27861                     
27862             } 
27863             
27864             
27865             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27866                 form:this.form.el.dom,
27867                 url:this.getUrl(!isPost),
27868                 method: method,
27869                 params:isPost ? this.getParams() : null,
27870                 isUpload: this.form.fileUpload
27871             }));
27872             
27873             this.uploadProgress();
27874
27875         }else if (o.clientValidation !== false){ // client validation failed
27876             this.failureType = Roo.form.Action.CLIENT_INVALID;
27877             this.form.afterAction(this, false);
27878         }
27879     },
27880
27881     success : function(response)
27882     {
27883         this.uploadComplete= true;
27884         if (this.haveProgress) {
27885             Roo.MessageBox.hide();
27886         }
27887         
27888         
27889         var result = this.processResponse(response);
27890         if(result === true || result.success){
27891             this.form.afterAction(this, true);
27892             return;
27893         }
27894         if(result.errors){
27895             this.form.markInvalid(result.errors);
27896             this.failureType = Roo.form.Action.SERVER_INVALID;
27897         }
27898         this.form.afterAction(this, false);
27899     },
27900     failure : function(response)
27901     {
27902         this.uploadComplete= true;
27903         if (this.haveProgress) {
27904             Roo.MessageBox.hide();
27905         }
27906         
27907         this.response = response;
27908         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27909         this.form.afterAction(this, false);
27910     },
27911     
27912     handleResponse : function(response){
27913         if(this.form.errorReader){
27914             var rs = this.form.errorReader.read(response);
27915             var errors = [];
27916             if(rs.records){
27917                 for(var i = 0, len = rs.records.length; i < len; i++) {
27918                     var r = rs.records[i];
27919                     errors[i] = r.data;
27920                 }
27921             }
27922             if(errors.length < 1){
27923                 errors = null;
27924             }
27925             return {
27926                 success : rs.success,
27927                 errors : errors
27928             };
27929         }
27930         var ret = false;
27931         try {
27932             ret = Roo.decode(response.responseText);
27933         } catch (e) {
27934             ret = {
27935                 success: false,
27936                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27937                 errors : []
27938             };
27939         }
27940         return ret;
27941         
27942     }
27943 });
27944
27945
27946 Roo.form.Action.Load = function(form, options){
27947     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27948     this.reader = this.form.reader;
27949 };
27950
27951 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27952     type : 'load',
27953
27954     run : function(){
27955         
27956         Roo.Ajax.request(Roo.apply(
27957                 this.createCallback(), {
27958                     method:this.getMethod(),
27959                     url:this.getUrl(false),
27960                     params:this.getParams()
27961         }));
27962     },
27963
27964     success : function(response){
27965         
27966         var result = this.processResponse(response);
27967         if(result === true || !result.success || !result.data){
27968             this.failureType = Roo.form.Action.LOAD_FAILURE;
27969             this.form.afterAction(this, false);
27970             return;
27971         }
27972         this.form.clearInvalid();
27973         this.form.setValues(result.data);
27974         this.form.afterAction(this, true);
27975     },
27976
27977     handleResponse : function(response){
27978         if(this.form.reader){
27979             var rs = this.form.reader.read(response);
27980             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27981             return {
27982                 success : rs.success,
27983                 data : data
27984             };
27985         }
27986         return Roo.decode(response.responseText);
27987     }
27988 });
27989
27990 Roo.form.Action.ACTION_TYPES = {
27991     'load' : Roo.form.Action.Load,
27992     'submit' : Roo.form.Action.Submit
27993 };/*
27994  * Based on:
27995  * Ext JS Library 1.1.1
27996  * Copyright(c) 2006-2007, Ext JS, LLC.
27997  *
27998  * Originally Released Under LGPL - original licence link has changed is not relivant.
27999  *
28000  * Fork - LGPL
28001  * <script type="text/javascript">
28002  */
28003  
28004 /**
28005  * @class Roo.form.Layout
28006  * @extends Roo.Component
28007  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28008  * @constructor
28009  * @param {Object} config Configuration options
28010  */
28011 Roo.form.Layout = function(config){
28012     var xitems = [];
28013     if (config.items) {
28014         xitems = config.items;
28015         delete config.items;
28016     }
28017     Roo.form.Layout.superclass.constructor.call(this, config);
28018     this.stack = [];
28019     Roo.each(xitems, this.addxtype, this);
28020      
28021 };
28022
28023 Roo.extend(Roo.form.Layout, Roo.Component, {
28024     /**
28025      * @cfg {String/Object} autoCreate
28026      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28027      */
28028     /**
28029      * @cfg {String/Object/Function} style
28030      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28031      * a function which returns such a specification.
28032      */
28033     /**
28034      * @cfg {String} labelAlign
28035      * Valid values are "left," "top" and "right" (defaults to "left")
28036      */
28037     /**
28038      * @cfg {Number} labelWidth
28039      * Fixed width in pixels of all field labels (defaults to undefined)
28040      */
28041     /**
28042      * @cfg {Boolean} clear
28043      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28044      */
28045     clear : true,
28046     /**
28047      * @cfg {String} labelSeparator
28048      * The separator to use after field labels (defaults to ':')
28049      */
28050     labelSeparator : ':',
28051     /**
28052      * @cfg {Boolean} hideLabels
28053      * True to suppress the display of field labels in this layout (defaults to false)
28054      */
28055     hideLabels : false,
28056
28057     // private
28058     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28059     
28060     isLayout : true,
28061     
28062     // private
28063     onRender : function(ct, position){
28064         if(this.el){ // from markup
28065             this.el = Roo.get(this.el);
28066         }else {  // generate
28067             var cfg = this.getAutoCreate();
28068             this.el = ct.createChild(cfg, position);
28069         }
28070         if(this.style){
28071             this.el.applyStyles(this.style);
28072         }
28073         if(this.labelAlign){
28074             this.el.addClass('x-form-label-'+this.labelAlign);
28075         }
28076         if(this.hideLabels){
28077             this.labelStyle = "display:none";
28078             this.elementStyle = "padding-left:0;";
28079         }else{
28080             if(typeof this.labelWidth == 'number'){
28081                 this.labelStyle = "width:"+this.labelWidth+"px;";
28082                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28083             }
28084             if(this.labelAlign == 'top'){
28085                 this.labelStyle = "width:auto;";
28086                 this.elementStyle = "padding-left:0;";
28087             }
28088         }
28089         var stack = this.stack;
28090         var slen = stack.length;
28091         if(slen > 0){
28092             if(!this.fieldTpl){
28093                 var t = new Roo.Template(
28094                     '<div class="x-form-item {5}">',
28095                         '<label for="{0}" style="{2}">{1}{4}</label>',
28096                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28097                         '</div>',
28098                     '</div><div class="x-form-clear-left"></div>'
28099                 );
28100                 t.disableFormats = true;
28101                 t.compile();
28102                 Roo.form.Layout.prototype.fieldTpl = t;
28103             }
28104             for(var i = 0; i < slen; i++) {
28105                 if(stack[i].isFormField){
28106                     this.renderField(stack[i]);
28107                 }else{
28108                     this.renderComponent(stack[i]);
28109                 }
28110             }
28111         }
28112         if(this.clear){
28113             this.el.createChild({cls:'x-form-clear'});
28114         }
28115     },
28116
28117     // private
28118     renderField : function(f){
28119         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28120                f.id, //0
28121                f.fieldLabel, //1
28122                f.labelStyle||this.labelStyle||'', //2
28123                this.elementStyle||'', //3
28124                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28125                f.itemCls||this.itemCls||''  //5
28126        ], true).getPrevSibling());
28127     },
28128
28129     // private
28130     renderComponent : function(c){
28131         c.render(c.isLayout ? this.el : this.el.createChild());    
28132     },
28133     /**
28134      * Adds a object form elements (using the xtype property as the factory method.)
28135      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28136      * @param {Object} config 
28137      */
28138     addxtype : function(o)
28139     {
28140         // create the lement.
28141         o.form = this.form;
28142         var fe = Roo.factory(o, Roo.form);
28143         this.form.allItems.push(fe);
28144         this.stack.push(fe);
28145         
28146         if (fe.isFormField) {
28147             this.form.items.add(fe);
28148         }
28149          
28150         return fe;
28151     }
28152 });
28153
28154 /**
28155  * @class Roo.form.Column
28156  * @extends Roo.form.Layout
28157  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28158  * @constructor
28159  * @param {Object} config Configuration options
28160  */
28161 Roo.form.Column = function(config){
28162     Roo.form.Column.superclass.constructor.call(this, config);
28163 };
28164
28165 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28166     /**
28167      * @cfg {Number/String} width
28168      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28169      */
28170     /**
28171      * @cfg {String/Object} autoCreate
28172      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28173      */
28174
28175     // private
28176     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28177
28178     // private
28179     onRender : function(ct, position){
28180         Roo.form.Column.superclass.onRender.call(this, ct, position);
28181         if(this.width){
28182             this.el.setWidth(this.width);
28183         }
28184     }
28185 });
28186
28187
28188 /**
28189  * @class Roo.form.Row
28190  * @extends Roo.form.Layout
28191  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28192  * @constructor
28193  * @param {Object} config Configuration options
28194  */
28195
28196  
28197 Roo.form.Row = function(config){
28198     Roo.form.Row.superclass.constructor.call(this, config);
28199 };
28200  
28201 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28202       /**
28203      * @cfg {Number/String} width
28204      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28205      */
28206     /**
28207      * @cfg {Number/String} height
28208      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28209      */
28210     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28211     
28212     padWidth : 20,
28213     // private
28214     onRender : function(ct, position){
28215         //console.log('row render');
28216         if(!this.rowTpl){
28217             var t = new Roo.Template(
28218                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28219                     '<label for="{0}" style="{2}">{1}{4}</label>',
28220                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28221                     '</div>',
28222                 '</div>'
28223             );
28224             t.disableFormats = true;
28225             t.compile();
28226             Roo.form.Layout.prototype.rowTpl = t;
28227         }
28228         this.fieldTpl = this.rowTpl;
28229         
28230         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28231         var labelWidth = 100;
28232         
28233         if ((this.labelAlign != 'top')) {
28234             if (typeof this.labelWidth == 'number') {
28235                 labelWidth = this.labelWidth
28236             }
28237             this.padWidth =  20 + labelWidth;
28238             
28239         }
28240         
28241         Roo.form.Column.superclass.onRender.call(this, ct, position);
28242         if(this.width){
28243             this.el.setWidth(this.width);
28244         }
28245         if(this.height){
28246             this.el.setHeight(this.height);
28247         }
28248     },
28249     
28250     // private
28251     renderField : function(f){
28252         f.fieldEl = this.fieldTpl.append(this.el, [
28253                f.id, f.fieldLabel,
28254                f.labelStyle||this.labelStyle||'',
28255                this.elementStyle||'',
28256                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28257                f.itemCls||this.itemCls||'',
28258                f.width ? f.width + this.padWidth : 160 + this.padWidth
28259        ],true);
28260     }
28261 });
28262  
28263
28264 /**
28265  * @class Roo.form.FieldSet
28266  * @extends Roo.form.Layout
28267  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28268  * @constructor
28269  * @param {Object} config Configuration options
28270  */
28271 Roo.form.FieldSet = function(config){
28272     Roo.form.FieldSet.superclass.constructor.call(this, config);
28273 };
28274
28275 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28276     /**
28277      * @cfg {String} legend
28278      * The text to display as the legend for the FieldSet (defaults to '')
28279      */
28280     /**
28281      * @cfg {String/Object} autoCreate
28282      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28283      */
28284
28285     // private
28286     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28287
28288     // private
28289     onRender : function(ct, position){
28290         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28291         if(this.legend){
28292             this.setLegend(this.legend);
28293         }
28294     },
28295
28296     // private
28297     setLegend : function(text){
28298         if(this.rendered){
28299             this.el.child('legend').update(text);
28300         }
28301     }
28302 });/*
28303  * Based on:
28304  * Ext JS Library 1.1.1
28305  * Copyright(c) 2006-2007, Ext JS, LLC.
28306  *
28307  * Originally Released Under LGPL - original licence link has changed is not relivant.
28308  *
28309  * Fork - LGPL
28310  * <script type="text/javascript">
28311  */
28312 /**
28313  * @class Roo.form.VTypes
28314  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28315  * @singleton
28316  */
28317 Roo.form.VTypes = function(){
28318     // closure these in so they are only created once.
28319     var alpha = /^[a-zA-Z_]+$/;
28320     var alphanum = /^[a-zA-Z0-9_]+$/;
28321     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28322     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28323
28324     // All these messages and functions are configurable
28325     return {
28326         /**
28327          * The function used to validate email addresses
28328          * @param {String} value The email address
28329          */
28330         'email' : function(v){
28331             return email.test(v);
28332         },
28333         /**
28334          * The error text to display when the email validation function returns false
28335          * @type String
28336          */
28337         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28338         /**
28339          * The keystroke filter mask to be applied on email input
28340          * @type RegExp
28341          */
28342         'emailMask' : /[a-z0-9_\.\-@]/i,
28343
28344         /**
28345          * The function used to validate URLs
28346          * @param {String} value The URL
28347          */
28348         'url' : function(v){
28349             return url.test(v);
28350         },
28351         /**
28352          * The error text to display when the url validation function returns false
28353          * @type String
28354          */
28355         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28356         
28357         /**
28358          * The function used to validate alpha values
28359          * @param {String} value The value
28360          */
28361         'alpha' : function(v){
28362             return alpha.test(v);
28363         },
28364         /**
28365          * The error text to display when the alpha validation function returns false
28366          * @type String
28367          */
28368         'alphaText' : 'This field should only contain letters and _',
28369         /**
28370          * The keystroke filter mask to be applied on alpha input
28371          * @type RegExp
28372          */
28373         'alphaMask' : /[a-z_]/i,
28374
28375         /**
28376          * The function used to validate alphanumeric values
28377          * @param {String} value The value
28378          */
28379         'alphanum' : function(v){
28380             return alphanum.test(v);
28381         },
28382         /**
28383          * The error text to display when the alphanumeric validation function returns false
28384          * @type String
28385          */
28386         'alphanumText' : 'This field should only contain letters, numbers and _',
28387         /**
28388          * The keystroke filter mask to be applied on alphanumeric input
28389          * @type RegExp
28390          */
28391         'alphanumMask' : /[a-z0-9_]/i
28392     };
28393 }();//<script type="text/javascript">
28394
28395 /**
28396  * @class Roo.form.FCKeditor
28397  * @extends Roo.form.TextArea
28398  * Wrapper around the FCKEditor http://www.fckeditor.net
28399  * @constructor
28400  * Creates a new FCKeditor
28401  * @param {Object} config Configuration options
28402  */
28403 Roo.form.FCKeditor = function(config){
28404     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28405     this.addEvents({
28406          /**
28407          * @event editorinit
28408          * Fired when the editor is initialized - you can add extra handlers here..
28409          * @param {FCKeditor} this
28410          * @param {Object} the FCK object.
28411          */
28412         editorinit : true
28413     });
28414     
28415     
28416 };
28417 Roo.form.FCKeditor.editors = { };
28418 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28419 {
28420     //defaultAutoCreate : {
28421     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28422     //},
28423     // private
28424     /**
28425      * @cfg {Object} fck options - see fck manual for details.
28426      */
28427     fckconfig : false,
28428     
28429     /**
28430      * @cfg {Object} fck toolbar set (Basic or Default)
28431      */
28432     toolbarSet : 'Basic',
28433     /**
28434      * @cfg {Object} fck BasePath
28435      */ 
28436     basePath : '/fckeditor/',
28437     
28438     
28439     frame : false,
28440     
28441     value : '',
28442     
28443    
28444     onRender : function(ct, position)
28445     {
28446         if(!this.el){
28447             this.defaultAutoCreate = {
28448                 tag: "textarea",
28449                 style:"width:300px;height:60px;",
28450                 autocomplete: "off"
28451             };
28452         }
28453         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28454         /*
28455         if(this.grow){
28456             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28457             if(this.preventScrollbars){
28458                 this.el.setStyle("overflow", "hidden");
28459             }
28460             this.el.setHeight(this.growMin);
28461         }
28462         */
28463         //console.log('onrender' + this.getId() );
28464         Roo.form.FCKeditor.editors[this.getId()] = this;
28465          
28466
28467         this.replaceTextarea() ;
28468         
28469     },
28470     
28471     getEditor : function() {
28472         return this.fckEditor;
28473     },
28474     /**
28475      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28476      * @param {Mixed} value The value to set
28477      */
28478     
28479     
28480     setValue : function(value)
28481     {
28482         //console.log('setValue: ' + value);
28483         
28484         if(typeof(value) == 'undefined') { // not sure why this is happending...
28485             return;
28486         }
28487         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28488         
28489         //if(!this.el || !this.getEditor()) {
28490         //    this.value = value;
28491             //this.setValue.defer(100,this,[value]);    
28492         //    return;
28493         //} 
28494         
28495         if(!this.getEditor()) {
28496             return;
28497         }
28498         
28499         this.getEditor().SetData(value);
28500         
28501         //
28502
28503     },
28504
28505     /**
28506      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28507      * @return {Mixed} value The field value
28508      */
28509     getValue : function()
28510     {
28511         
28512         if (this.frame && this.frame.dom.style.display == 'none') {
28513             return Roo.form.FCKeditor.superclass.getValue.call(this);
28514         }
28515         
28516         if(!this.el || !this.getEditor()) {
28517            
28518            // this.getValue.defer(100,this); 
28519             return this.value;
28520         }
28521        
28522         
28523         var value=this.getEditor().GetData();
28524         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28525         return Roo.form.FCKeditor.superclass.getValue.call(this);
28526         
28527
28528     },
28529
28530     /**
28531      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28532      * @return {Mixed} value The field value
28533      */
28534     getRawValue : function()
28535     {
28536         if (this.frame && this.frame.dom.style.display == 'none') {
28537             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28538         }
28539         
28540         if(!this.el || !this.getEditor()) {
28541             //this.getRawValue.defer(100,this); 
28542             return this.value;
28543             return;
28544         }
28545         
28546         
28547         
28548         var value=this.getEditor().GetData();
28549         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28550         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28551          
28552     },
28553     
28554     setSize : function(w,h) {
28555         
28556         
28557         
28558         //if (this.frame && this.frame.dom.style.display == 'none') {
28559         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28560         //    return;
28561         //}
28562         //if(!this.el || !this.getEditor()) {
28563         //    this.setSize.defer(100,this, [w,h]); 
28564         //    return;
28565         //}
28566         
28567         
28568         
28569         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28570         
28571         this.frame.dom.setAttribute('width', w);
28572         this.frame.dom.setAttribute('height', h);
28573         this.frame.setSize(w,h);
28574         
28575     },
28576     
28577     toggleSourceEdit : function(value) {
28578         
28579       
28580          
28581         this.el.dom.style.display = value ? '' : 'none';
28582         this.frame.dom.style.display = value ?  'none' : '';
28583         
28584     },
28585     
28586     
28587     focus: function(tag)
28588     {
28589         if (this.frame.dom.style.display == 'none') {
28590             return Roo.form.FCKeditor.superclass.focus.call(this);
28591         }
28592         if(!this.el || !this.getEditor()) {
28593             this.focus.defer(100,this, [tag]); 
28594             return;
28595         }
28596         
28597         
28598         
28599         
28600         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28601         this.getEditor().Focus();
28602         if (tgs.length) {
28603             if (!this.getEditor().Selection.GetSelection()) {
28604                 this.focus.defer(100,this, [tag]); 
28605                 return;
28606             }
28607             
28608             
28609             var r = this.getEditor().EditorDocument.createRange();
28610             r.setStart(tgs[0],0);
28611             r.setEnd(tgs[0],0);
28612             this.getEditor().Selection.GetSelection().removeAllRanges();
28613             this.getEditor().Selection.GetSelection().addRange(r);
28614             this.getEditor().Focus();
28615         }
28616         
28617     },
28618     
28619     
28620     
28621     replaceTextarea : function()
28622     {
28623         if ( document.getElementById( this.getId() + '___Frame' ) )
28624             return ;
28625         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28626         //{
28627             // We must check the elements firstly using the Id and then the name.
28628         var oTextarea = document.getElementById( this.getId() );
28629         
28630         var colElementsByName = document.getElementsByName( this.getId() ) ;
28631          
28632         oTextarea.style.display = 'none' ;
28633
28634         if ( oTextarea.tabIndex ) {            
28635             this.TabIndex = oTextarea.tabIndex ;
28636         }
28637         
28638         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28639         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28640         this.frame = Roo.get(this.getId() + '___Frame')
28641     },
28642     
28643     _getConfigHtml : function()
28644     {
28645         var sConfig = '' ;
28646
28647         for ( var o in this.fckconfig ) {
28648             sConfig += sConfig.length > 0  ? '&amp;' : '';
28649             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28650         }
28651
28652         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28653     },
28654     
28655     
28656     _getIFrameHtml : function()
28657     {
28658         var sFile = 'fckeditor.html' ;
28659         /* no idea what this is about..
28660         try
28661         {
28662             if ( (/fcksource=true/i).test( window.top.location.search ) )
28663                 sFile = 'fckeditor.original.html' ;
28664         }
28665         catch (e) { 
28666         */
28667
28668         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28669         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28670         
28671         
28672         var html = '<iframe id="' + this.getId() +
28673             '___Frame" src="' + sLink +
28674             '" width="' + this.width +
28675             '" height="' + this.height + '"' +
28676             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28677             ' frameborder="0" scrolling="no"></iframe>' ;
28678
28679         return html ;
28680     },
28681     
28682     _insertHtmlBefore : function( html, element )
28683     {
28684         if ( element.insertAdjacentHTML )       {
28685             // IE
28686             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28687         } else { // Gecko
28688             var oRange = document.createRange() ;
28689             oRange.setStartBefore( element ) ;
28690             var oFragment = oRange.createContextualFragment( html );
28691             element.parentNode.insertBefore( oFragment, element ) ;
28692         }
28693     }
28694     
28695     
28696   
28697     
28698     
28699     
28700     
28701
28702 });
28703
28704 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28705
28706 function FCKeditor_OnComplete(editorInstance){
28707     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28708     f.fckEditor = editorInstance;
28709     //console.log("loaded");
28710     f.fireEvent('editorinit', f, editorInstance);
28711
28712   
28713
28714  
28715
28716
28717
28718
28719
28720
28721
28722
28723
28724
28725
28726
28727
28728
28729
28730 //<script type="text/javascript">
28731 /**
28732  * @class Roo.form.GridField
28733  * @extends Roo.form.Field
28734  * Embed a grid (or editable grid into a form)
28735  * STATUS ALPHA
28736  * 
28737  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28738  * it needs 
28739  * xgrid.store = Roo.data.Store
28740  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28741  * xgrid.store.reader = Roo.data.JsonReader 
28742  * 
28743  * 
28744  * @constructor
28745  * Creates a new GridField
28746  * @param {Object} config Configuration options
28747  */
28748 Roo.form.GridField = function(config){
28749     Roo.form.GridField.superclass.constructor.call(this, config);
28750      
28751 };
28752
28753 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28754     /**
28755      * @cfg {Number} width  - used to restrict width of grid..
28756      */
28757     width : 100,
28758     /**
28759      * @cfg {Number} height - used to restrict height of grid..
28760      */
28761     height : 50,
28762      /**
28763      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28764          * 
28765          *}
28766      */
28767     xgrid : false, 
28768     /**
28769      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28770      * {tag: "input", type: "checkbox", autocomplete: "off"})
28771      */
28772    // defaultAutoCreate : { tag: 'div' },
28773     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28774     /**
28775      * @cfg {String} addTitle Text to include for adding a title.
28776      */
28777     addTitle : false,
28778     //
28779     onResize : function(){
28780         Roo.form.Field.superclass.onResize.apply(this, arguments);
28781     },
28782
28783     initEvents : function(){
28784         // Roo.form.Checkbox.superclass.initEvents.call(this);
28785         // has no events...
28786        
28787     },
28788
28789
28790     getResizeEl : function(){
28791         return this.wrap;
28792     },
28793
28794     getPositionEl : function(){
28795         return this.wrap;
28796     },
28797
28798     // private
28799     onRender : function(ct, position){
28800         
28801         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28802         var style = this.style;
28803         delete this.style;
28804         
28805         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28806         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28807         this.viewEl = this.wrap.createChild({ tag: 'div' });
28808         if (style) {
28809             this.viewEl.applyStyles(style);
28810         }
28811         if (this.width) {
28812             this.viewEl.setWidth(this.width);
28813         }
28814         if (this.height) {
28815             this.viewEl.setHeight(this.height);
28816         }
28817         //if(this.inputValue !== undefined){
28818         //this.setValue(this.value);
28819         
28820         
28821         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28822         
28823         
28824         this.grid.render();
28825         this.grid.getDataSource().on('remove', this.refreshValue, this);
28826         this.grid.getDataSource().on('update', this.refreshValue, this);
28827         this.grid.on('afteredit', this.refreshValue, this);
28828  
28829     },
28830      
28831     
28832     /**
28833      * Sets the value of the item. 
28834      * @param {String} either an object  or a string..
28835      */
28836     setValue : function(v){
28837         //this.value = v;
28838         v = v || []; // empty set..
28839         // this does not seem smart - it really only affects memoryproxy grids..
28840         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28841             var ds = this.grid.getDataSource();
28842             // assumes a json reader..
28843             var data = {}
28844             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28845             ds.loadData( data);
28846         }
28847         // clear selection so it does not get stale.
28848         if (this.grid.sm) { 
28849             this.grid.sm.clearSelections();
28850         }
28851         
28852         Roo.form.GridField.superclass.setValue.call(this, v);
28853         this.refreshValue();
28854         // should load data in the grid really....
28855     },
28856     
28857     // private
28858     refreshValue: function() {
28859          var val = [];
28860         this.grid.getDataSource().each(function(r) {
28861             val.push(r.data);
28862         });
28863         this.el.dom.value = Roo.encode(val);
28864     }
28865     
28866      
28867     
28868     
28869 });/*
28870  * Based on:
28871  * Ext JS Library 1.1.1
28872  * Copyright(c) 2006-2007, Ext JS, LLC.
28873  *
28874  * Originally Released Under LGPL - original licence link has changed is not relivant.
28875  *
28876  * Fork - LGPL
28877  * <script type="text/javascript">
28878  */
28879 /**
28880  * @class Roo.form.DisplayField
28881  * @extends Roo.form.Field
28882  * A generic Field to display non-editable data.
28883  * @constructor
28884  * Creates a new Display Field item.
28885  * @param {Object} config Configuration options
28886  */
28887 Roo.form.DisplayField = function(config){
28888     Roo.form.DisplayField.superclass.constructor.call(this, config);
28889     
28890 };
28891
28892 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28893     inputType:      'hidden',
28894     allowBlank:     true,
28895     readOnly:         true,
28896     
28897  
28898     /**
28899      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28900      */
28901     focusClass : undefined,
28902     /**
28903      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28904      */
28905     fieldClass: 'x-form-field',
28906     
28907      /**
28908      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28909      */
28910     valueRenderer: undefined,
28911     
28912     width: 100,
28913     /**
28914      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28915      * {tag: "input", type: "checkbox", autocomplete: "off"})
28916      */
28917      
28918  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28919
28920     onResize : function(){
28921         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28922         
28923     },
28924
28925     initEvents : function(){
28926         // Roo.form.Checkbox.superclass.initEvents.call(this);
28927         // has no events...
28928        
28929     },
28930
28931
28932     getResizeEl : function(){
28933         return this.wrap;
28934     },
28935
28936     getPositionEl : function(){
28937         return this.wrap;
28938     },
28939
28940     // private
28941     onRender : function(ct, position){
28942         
28943         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28944         //if(this.inputValue !== undefined){
28945         this.wrap = this.el.wrap();
28946         
28947         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28948         
28949         if (this.bodyStyle) {
28950             this.viewEl.applyStyles(this.bodyStyle);
28951         }
28952         //this.viewEl.setStyle('padding', '2px');
28953         
28954         this.setValue(this.value);
28955         
28956     },
28957 /*
28958     // private
28959     initValue : Roo.emptyFn,
28960
28961   */
28962
28963         // private
28964     onClick : function(){
28965         
28966     },
28967
28968     /**
28969      * Sets the checked state of the checkbox.
28970      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28971      */
28972     setValue : function(v){
28973         this.value = v;
28974         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28975         // this might be called before we have a dom element..
28976         if (!this.viewEl) {
28977             return;
28978         }
28979         this.viewEl.dom.innerHTML = html;
28980         Roo.form.DisplayField.superclass.setValue.call(this, v);
28981
28982     }
28983 });/*
28984  * 
28985  * Licence- LGPL
28986  * 
28987  */
28988
28989 /**
28990  * @class Roo.form.DayPicker
28991  * @extends Roo.form.Field
28992  * A Day picker show [M] [T] [W] ....
28993  * @constructor
28994  * Creates a new Day Picker
28995  * @param {Object} config Configuration options
28996  */
28997 Roo.form.DayPicker= function(config){
28998     Roo.form.DayPicker.superclass.constructor.call(this, config);
28999      
29000 };
29001
29002 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29003     /**
29004      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29005      */
29006     focusClass : undefined,
29007     /**
29008      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29009      */
29010     fieldClass: "x-form-field",
29011    
29012     /**
29013      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29014      * {tag: "input", type: "checkbox", autocomplete: "off"})
29015      */
29016     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29017     
29018    
29019     actionMode : 'viewEl', 
29020     //
29021     // private
29022  
29023     inputType : 'hidden',
29024     
29025      
29026     inputElement: false, // real input element?
29027     basedOn: false, // ????
29028     
29029     isFormField: true, // not sure where this is needed!!!!
29030
29031     onResize : function(){
29032         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29033         if(!this.boxLabel){
29034             this.el.alignTo(this.wrap, 'c-c');
29035         }
29036     },
29037
29038     initEvents : function(){
29039         Roo.form.Checkbox.superclass.initEvents.call(this);
29040         this.el.on("click", this.onClick,  this);
29041         this.el.on("change", this.onClick,  this);
29042     },
29043
29044
29045     getResizeEl : function(){
29046         return this.wrap;
29047     },
29048
29049     getPositionEl : function(){
29050         return this.wrap;
29051     },
29052
29053     
29054     // private
29055     onRender : function(ct, position){
29056         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29057        
29058         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29059         
29060         var r1 = '<table><tr>';
29061         var r2 = '<tr class="x-form-daypick-icons">';
29062         for (var i=0; i < 7; i++) {
29063             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29064             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29065         }
29066         
29067         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29068         viewEl.select('img').on('click', this.onClick, this);
29069         this.viewEl = viewEl;   
29070         
29071         
29072         // this will not work on Chrome!!!
29073         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29074         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29075         
29076         
29077           
29078
29079     },
29080
29081     // private
29082     initValue : Roo.emptyFn,
29083
29084     /**
29085      * Returns the checked state of the checkbox.
29086      * @return {Boolean} True if checked, else false
29087      */
29088     getValue : function(){
29089         return this.el.dom.value;
29090         
29091     },
29092
29093         // private
29094     onClick : function(e){ 
29095         //this.setChecked(!this.checked);
29096         Roo.get(e.target).toggleClass('x-menu-item-checked');
29097         this.refreshValue();
29098         //if(this.el.dom.checked != this.checked){
29099         //    this.setValue(this.el.dom.checked);
29100        // }
29101     },
29102     
29103     // private
29104     refreshValue : function()
29105     {
29106         var val = '';
29107         this.viewEl.select('img',true).each(function(e,i,n)  {
29108             val += e.is(".x-menu-item-checked") ? String(n) : '';
29109         });
29110         this.setValue(val, true);
29111     },
29112
29113     /**
29114      * Sets the checked state of the checkbox.
29115      * On is always based on a string comparison between inputValue and the param.
29116      * @param {Boolean/String} value - the value to set 
29117      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29118      */
29119     setValue : function(v,suppressEvent){
29120         if (!this.el.dom) {
29121             return;
29122         }
29123         var old = this.el.dom.value ;
29124         this.el.dom.value = v;
29125         if (suppressEvent) {
29126             return ;
29127         }
29128          
29129         // update display..
29130         this.viewEl.select('img',true).each(function(e,i,n)  {
29131             
29132             var on = e.is(".x-menu-item-checked");
29133             var newv = v.indexOf(String(n)) > -1;
29134             if (on != newv) {
29135                 e.toggleClass('x-menu-item-checked');
29136             }
29137             
29138         });
29139         
29140         
29141         this.fireEvent('change', this, v, old);
29142         
29143         
29144     },
29145    
29146     // handle setting of hidden value by some other method!!?!?
29147     setFromHidden: function()
29148     {
29149         if(!this.el){
29150             return;
29151         }
29152         //console.log("SET FROM HIDDEN");
29153         //alert('setFrom hidden');
29154         this.setValue(this.el.dom.value);
29155     },
29156     
29157     onDestroy : function()
29158     {
29159         if(this.viewEl){
29160             Roo.get(this.viewEl).remove();
29161         }
29162          
29163         Roo.form.DayPicker.superclass.onDestroy.call(this);
29164     }
29165
29166 });/*
29167  * RooJS Library 1.1.1
29168  * Copyright(c) 2008-2011  Alan Knowles
29169  *
29170  * License - LGPL
29171  */
29172  
29173
29174 /**
29175  * @class Roo.form.ComboCheck
29176  * @extends Roo.form.ComboBox
29177  * A combobox for multiple select items.
29178  *
29179  * FIXME - could do with a reset button..
29180  * 
29181  * @constructor
29182  * Create a new ComboCheck
29183  * @param {Object} config Configuration options
29184  */
29185 Roo.form.ComboCheck = function(config){
29186     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29187     // should verify some data...
29188     // like
29189     // hiddenName = required..
29190     // displayField = required
29191     // valudField == required
29192     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29193     var _t = this;
29194     Roo.each(req, function(e) {
29195         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29196             throw "Roo.form.ComboCheck : missing value for: " + e;
29197         }
29198     });
29199     
29200     
29201 };
29202
29203 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29204      
29205      
29206     editable : false,
29207      
29208     selectedClass: 'x-menu-item-checked', 
29209     
29210     // private
29211     onRender : function(ct, position){
29212         var _t = this;
29213         
29214         
29215         
29216         if(!this.tpl){
29217             var cls = 'x-combo-list';
29218
29219             
29220             this.tpl =  new Roo.Template({
29221                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29222                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29223                    '<span>{' + this.displayField + '}</span>' +
29224                     '</div>' 
29225                 
29226             });
29227         }
29228  
29229         
29230         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29231         this.view.singleSelect = false;
29232         this.view.multiSelect = true;
29233         this.view.toggleSelect = true;
29234         this.pageTb.add(new Roo.Toolbar.Fill(), {
29235             
29236             text: 'Done',
29237             handler: function()
29238             {
29239                 _t.collapse();
29240             }
29241         });
29242     },
29243     
29244     onViewOver : function(e, t){
29245         // do nothing...
29246         return;
29247         
29248     },
29249     
29250     onViewClick : function(doFocus,index){
29251         return;
29252         
29253     },
29254     select: function () {
29255         //Roo.log("SELECT CALLED");
29256     },
29257      
29258     selectByValue : function(xv, scrollIntoView){
29259         var ar = this.getValueArray();
29260         var sels = [];
29261         
29262         Roo.each(ar, function(v) {
29263             if(v === undefined || v === null){
29264                 return;
29265             }
29266             var r = this.findRecord(this.valueField, v);
29267             if(r){
29268                 sels.push(this.store.indexOf(r))
29269                 
29270             }
29271         },this);
29272         this.view.select(sels);
29273         return false;
29274     },
29275     
29276     
29277     
29278     onSelect : function(record, index){
29279        // Roo.log("onselect Called");
29280        // this is only called by the clear button now..
29281         this.view.clearSelections();
29282         this.setValue('[]');
29283         if (this.value != this.valueBefore) {
29284             this.fireEvent('change', this, this.value, this.valueBefore);
29285         }
29286     },
29287     getValueArray : function()
29288     {
29289         var ar = [] ;
29290         
29291         try {
29292             //Roo.log(this.value);
29293             if (typeof(this.value) == 'undefined') {
29294                 return [];
29295             }
29296             var ar = Roo.decode(this.value);
29297             return  ar instanceof Array ? ar : []; //?? valid?
29298             
29299         } catch(e) {
29300             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29301             return [];
29302         }
29303          
29304     },
29305     expand : function ()
29306     {
29307         Roo.form.ComboCheck.superclass.expand.call(this);
29308         this.valueBefore = this.value;
29309         
29310
29311     },
29312     
29313     collapse : function(){
29314         Roo.form.ComboCheck.superclass.collapse.call(this);
29315         var sl = this.view.getSelectedIndexes();
29316         var st = this.store;
29317         var nv = [];
29318         var tv = [];
29319         var r;
29320         Roo.each(sl, function(i) {
29321             r = st.getAt(i);
29322             nv.push(r.get(this.valueField));
29323         },this);
29324         this.setValue(Roo.encode(nv));
29325         if (this.value != this.valueBefore) {
29326
29327             this.fireEvent('change', this, this.value, this.valueBefore);
29328         }
29329         
29330     },
29331     
29332     setValue : function(v){
29333         // Roo.log(v);
29334         this.value = v;
29335         
29336         var vals = this.getValueArray();
29337         var tv = [];
29338         Roo.each(vals, function(k) {
29339             var r = this.findRecord(this.valueField, k);
29340             if(r){
29341                 tv.push(r.data[this.displayField]);
29342             }else if(this.valueNotFoundText !== undefined){
29343                 tv.push( this.valueNotFoundText );
29344             }
29345         },this);
29346        // Roo.log(tv);
29347         
29348         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29349         this.hiddenField.value = v;
29350         this.value = v;
29351     }
29352     
29353 });//<script type="text/javasscript">
29354  
29355
29356 /**
29357  * @class Roo.DDView
29358  * A DnD enabled version of Roo.View.
29359  * @param {Element/String} container The Element in which to create the View.
29360  * @param {String} tpl The template string used to create the markup for each element of the View
29361  * @param {Object} config The configuration properties. These include all the config options of
29362  * {@link Roo.View} plus some specific to this class.<br>
29363  * <p>
29364  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29365  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29366  * <p>
29367  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29368 .x-view-drag-insert-above {
29369         border-top:1px dotted #3366cc;
29370 }
29371 .x-view-drag-insert-below {
29372         border-bottom:1px dotted #3366cc;
29373 }
29374 </code></pre>
29375  * 
29376  */
29377  
29378 Roo.DDView = function(container, tpl, config) {
29379     Roo.DDView.superclass.constructor.apply(this, arguments);
29380     this.getEl().setStyle("outline", "0px none");
29381     this.getEl().unselectable();
29382     if (this.dragGroup) {
29383                 this.setDraggable(this.dragGroup.split(","));
29384     }
29385     if (this.dropGroup) {
29386                 this.setDroppable(this.dropGroup.split(","));
29387     }
29388     if (this.deletable) {
29389         this.setDeletable();
29390     }
29391     this.isDirtyFlag = false;
29392         this.addEvents({
29393                 "drop" : true
29394         });
29395 };
29396
29397 Roo.extend(Roo.DDView, Roo.View, {
29398 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29399 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
29400 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
29401 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
29402
29403         isFormField: true,
29404
29405         reset: Roo.emptyFn,
29406         
29407         clearInvalid: Roo.form.Field.prototype.clearInvalid,
29408
29409         validate: function() {
29410                 return true;
29411         },
29412         
29413         destroy: function() {
29414                 this.purgeListeners();
29415                 this.getEl.removeAllListeners();
29416                 this.getEl().remove();
29417                 if (this.dragZone) {
29418                         if (this.dragZone.destroy) {
29419                                 this.dragZone.destroy();
29420                         }
29421                 }
29422                 if (this.dropZone) {
29423                         if (this.dropZone.destroy) {
29424                                 this.dropZone.destroy();
29425                         }
29426                 }
29427         },
29428
29429 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
29430         getName: function() {
29431                 return this.name;
29432         },
29433
29434 /**     Loads the View from a JSON string representing the Records to put into the Store. */
29435         setValue: function(v) {
29436                 if (!this.store) {
29437                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
29438                 }
29439                 var data = {};
29440                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
29441                 this.store.proxy = new Roo.data.MemoryProxy(data);
29442                 this.store.load();
29443         },
29444
29445 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
29446         getValue: function() {
29447                 var result = '(';
29448                 this.store.each(function(rec) {
29449                         result += rec.id + ',';
29450                 });
29451                 return result.substr(0, result.length - 1) + ')';
29452         },
29453         
29454         getIds: function() {
29455                 var i = 0, result = new Array(this.store.getCount());
29456                 this.store.each(function(rec) {
29457                         result[i++] = rec.id;
29458                 });
29459                 return result;
29460         },
29461         
29462         isDirty: function() {
29463                 return this.isDirtyFlag;
29464         },
29465
29466 /**
29467  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
29468  *      whole Element becomes the target, and this causes the drop gesture to append.
29469  */
29470     getTargetFromEvent : function(e) {
29471                 var target = e.getTarget();
29472                 while ((target !== null) && (target.parentNode != this.el.dom)) {
29473                 target = target.parentNode;
29474                 }
29475                 if (!target) {
29476                         target = this.el.dom.lastChild || this.el.dom;
29477                 }
29478                 return target;
29479     },
29480
29481 /**
29482  *      Create the drag data which consists of an object which has the property "ddel" as
29483  *      the drag proxy element. 
29484  */
29485     getDragData : function(e) {
29486         var target = this.findItemFromChild(e.getTarget());
29487                 if(target) {
29488                         this.handleSelection(e);
29489                         var selNodes = this.getSelectedNodes();
29490             var dragData = {
29491                 source: this,
29492                 copy: this.copy || (this.allowCopy && e.ctrlKey),
29493                 nodes: selNodes,
29494                 records: []
29495                         };
29496                         var selectedIndices = this.getSelectedIndexes();
29497                         for (var i = 0; i < selectedIndices.length; i++) {
29498                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
29499                         }
29500                         if (selNodes.length == 1) {
29501                                 dragData.ddel = target.cloneNode(true); // the div element
29502                         } else {
29503                                 var div = document.createElement('div'); // create the multi element drag "ghost"
29504                                 div.className = 'multi-proxy';
29505                                 for (var i = 0, len = selNodes.length; i < len; i++) {
29506                                         div.appendChild(selNodes[i].cloneNode(true));
29507                                 }
29508                                 dragData.ddel = div;
29509                         }
29510             //console.log(dragData)
29511             //console.log(dragData.ddel.innerHTML)
29512                         return dragData;
29513                 }
29514         //console.log('nodragData')
29515                 return false;
29516     },
29517     
29518 /**     Specify to which ddGroup items in this DDView may be dragged. */
29519     setDraggable: function(ddGroup) {
29520         if (ddGroup instanceof Array) {
29521                 Roo.each(ddGroup, this.setDraggable, this);
29522                 return;
29523         }
29524         if (this.dragZone) {
29525                 this.dragZone.addToGroup(ddGroup);
29526         } else {
29527                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
29528                                 containerScroll: true,
29529                                 ddGroup: ddGroup 
29530
29531                         });
29532 //                      Draggability implies selection. DragZone's mousedown selects the element.
29533                         if (!this.multiSelect) { this.singleSelect = true; }
29534
29535 //                      Wire the DragZone's handlers up to methods in *this*
29536                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
29537                 }
29538     },
29539
29540 /**     Specify from which ddGroup this DDView accepts drops. */
29541     setDroppable: function(ddGroup) {
29542         if (ddGroup instanceof Array) {
29543                 Roo.each(ddGroup, this.setDroppable, this);
29544                 return;
29545         }
29546         if (this.dropZone) {
29547                 this.dropZone.addToGroup(ddGroup);
29548         } else {
29549                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
29550                                 containerScroll: true,
29551                                 ddGroup: ddGroup
29552                         });
29553
29554 //                      Wire the DropZone's handlers up to methods in *this*
29555                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
29556                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
29557                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
29558                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
29559                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
29560                 }
29561     },
29562
29563 /**     Decide whether to drop above or below a View node. */
29564     getDropPoint : function(e, n, dd){
29565         if (n == this.el.dom) { return "above"; }
29566                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29567                 var c = t + (b - t) / 2;
29568                 var y = Roo.lib.Event.getPageY(e);
29569                 if(y <= c) {
29570                         return "above";
29571                 }else{
29572                         return "below";
29573                 }
29574     },
29575
29576     onNodeEnter : function(n, dd, e, data){
29577                 return false;
29578     },
29579     
29580     onNodeOver : function(n, dd, e, data){
29581                 var pt = this.getDropPoint(e, n, dd);
29582                 // set the insert point style on the target node
29583                 var dragElClass = this.dropNotAllowed;
29584                 if (pt) {
29585                         var targetElClass;
29586                         if (pt == "above"){
29587                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29588                                 targetElClass = "x-view-drag-insert-above";
29589                         } else {
29590                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29591                                 targetElClass = "x-view-drag-insert-below";
29592                         }
29593                         if (this.lastInsertClass != targetElClass){
29594                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29595                                 this.lastInsertClass = targetElClass;
29596                         }
29597                 }
29598                 return dragElClass;
29599         },
29600
29601     onNodeOut : function(n, dd, e, data){
29602                 this.removeDropIndicators(n);
29603     },
29604
29605     onNodeDrop : function(n, dd, e, data){
29606         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29607                 return false;
29608         }
29609         var pt = this.getDropPoint(e, n, dd);
29610                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29611                 if (pt == "below") { insertAt++; }
29612                 for (var i = 0; i < data.records.length; i++) {
29613                         var r = data.records[i];
29614                         var dup = this.store.getById(r.id);
29615                         if (dup && (dd != this.dragZone)) {
29616                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29617                         } else {
29618                                 if (data.copy) {
29619                                         this.store.insert(insertAt++, r.copy());
29620                                 } else {
29621                                         data.source.isDirtyFlag = true;
29622                                         r.store.remove(r);
29623                                         this.store.insert(insertAt++, r);
29624                                 }
29625                                 this.isDirtyFlag = true;
29626                         }
29627                 }
29628                 this.dragZone.cachedTarget = null;
29629                 return true;
29630     },
29631
29632     removeDropIndicators : function(n){
29633                 if(n){
29634                         Roo.fly(n).removeClass([
29635                                 "x-view-drag-insert-above",
29636                                 "x-view-drag-insert-below"]);
29637                         this.lastInsertClass = "_noclass";
29638                 }
29639     },
29640
29641 /**
29642  *      Utility method. Add a delete option to the DDView's context menu.
29643  *      @param {String} imageUrl The URL of the "delete" icon image.
29644  */
29645         setDeletable: function(imageUrl) {
29646                 if (!this.singleSelect && !this.multiSelect) {
29647                         this.singleSelect = true;
29648                 }
29649                 var c = this.getContextMenu();
29650                 this.contextMenu.on("itemclick", function(item) {
29651                         switch (item.id) {
29652                                 case "delete":
29653                                         this.remove(this.getSelectedIndexes());
29654                                         break;
29655                         }
29656                 }, this);
29657                 this.contextMenu.add({
29658                         icon: imageUrl,
29659                         id: "delete",
29660                         text: 'Delete'
29661                 });
29662         },
29663         
29664 /**     Return the context menu for this DDView. */
29665         getContextMenu: function() {
29666                 if (!this.contextMenu) {
29667 //                      Create the View's context menu
29668                         this.contextMenu = new Roo.menu.Menu({
29669                                 id: this.id + "-contextmenu"
29670                         });
29671                         this.el.on("contextmenu", this.showContextMenu, this);
29672                 }
29673                 return this.contextMenu;
29674         },
29675         
29676         disableContextMenu: function() {
29677                 if (this.contextMenu) {
29678                         this.el.un("contextmenu", this.showContextMenu, this);
29679                 }
29680         },
29681
29682         showContextMenu: function(e, item) {
29683         item = this.findItemFromChild(e.getTarget());
29684                 if (item) {
29685                         e.stopEvent();
29686                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29687                         this.contextMenu.showAt(e.getXY());
29688             }
29689     },
29690
29691 /**
29692  *      Remove {@link Roo.data.Record}s at the specified indices.
29693  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29694  */
29695     remove: function(selectedIndices) {
29696                 selectedIndices = [].concat(selectedIndices);
29697                 for (var i = 0; i < selectedIndices.length; i++) {
29698                         var rec = this.store.getAt(selectedIndices[i]);
29699                         this.store.remove(rec);
29700                 }
29701     },
29702
29703 /**
29704  *      Double click fires the event, but also, if this is draggable, and there is only one other
29705  *      related DropZone, it transfers the selected node.
29706  */
29707     onDblClick : function(e){
29708         var item = this.findItemFromChild(e.getTarget());
29709         if(item){
29710             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29711                 return false;
29712             }
29713             if (this.dragGroup) {
29714                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29715                     while (targets.indexOf(this.dropZone) > -1) {
29716                             targets.remove(this.dropZone);
29717                                 }
29718                     if (targets.length == 1) {
29719                                         this.dragZone.cachedTarget = null;
29720                         var el = Roo.get(targets[0].getEl());
29721                         var box = el.getBox(true);
29722                         targets[0].onNodeDrop(el.dom, {
29723                                 target: el.dom,
29724                                 xy: [box.x, box.y + box.height - 1]
29725                         }, null, this.getDragData(e));
29726                     }
29727                 }
29728         }
29729     },
29730     
29731     handleSelection: function(e) {
29732                 this.dragZone.cachedTarget = null;
29733         var item = this.findItemFromChild(e.getTarget());
29734         if (!item) {
29735                 this.clearSelections(true);
29736                 return;
29737         }
29738                 if (item && (this.multiSelect || this.singleSelect)){
29739                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29740                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29741                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29742                                 this.unselect(item);
29743                         } else {
29744                                 this.select(item, this.multiSelect && e.ctrlKey);
29745                                 this.lastSelection = item;
29746                         }
29747                 }
29748     },
29749
29750     onItemClick : function(item, index, e){
29751                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29752                         return false;
29753                 }
29754                 return true;
29755     },
29756
29757     unselect : function(nodeInfo, suppressEvent){
29758                 var node = this.getNode(nodeInfo);
29759                 if(node && this.isSelected(node)){
29760                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29761                                 Roo.fly(node).removeClass(this.selectedClass);
29762                                 this.selections.remove(node);
29763                                 if(!suppressEvent){
29764                                         this.fireEvent("selectionchange", this, this.selections);
29765                                 }
29766                         }
29767                 }
29768     }
29769 });
29770 /*
29771  * Based on:
29772  * Ext JS Library 1.1.1
29773  * Copyright(c) 2006-2007, Ext JS, LLC.
29774  *
29775  * Originally Released Under LGPL - original licence link has changed is not relivant.
29776  *
29777  * Fork - LGPL
29778  * <script type="text/javascript">
29779  */
29780  
29781 /**
29782  * @class Roo.LayoutManager
29783  * @extends Roo.util.Observable
29784  * Base class for layout managers.
29785  */
29786 Roo.LayoutManager = function(container, config){
29787     Roo.LayoutManager.superclass.constructor.call(this);
29788     this.el = Roo.get(container);
29789     // ie scrollbar fix
29790     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29791         document.body.scroll = "no";
29792     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29793         this.el.position('relative');
29794     }
29795     this.id = this.el.id;
29796     this.el.addClass("x-layout-container");
29797     /** false to disable window resize monitoring @type Boolean */
29798     this.monitorWindowResize = true;
29799     this.regions = {};
29800     this.addEvents({
29801         /**
29802          * @event layout
29803          * Fires when a layout is performed. 
29804          * @param {Roo.LayoutManager} this
29805          */
29806         "layout" : true,
29807         /**
29808          * @event regionresized
29809          * Fires when the user resizes a region. 
29810          * @param {Roo.LayoutRegion} region The resized region
29811          * @param {Number} newSize The new size (width for east/west, height for north/south)
29812          */
29813         "regionresized" : true,
29814         /**
29815          * @event regioncollapsed
29816          * Fires when a region is collapsed. 
29817          * @param {Roo.LayoutRegion} region The collapsed region
29818          */
29819         "regioncollapsed" : true,
29820         /**
29821          * @event regionexpanded
29822          * Fires when a region is expanded.  
29823          * @param {Roo.LayoutRegion} region The expanded region
29824          */
29825         "regionexpanded" : true
29826     });
29827     this.updating = false;
29828     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29829 };
29830
29831 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29832     /**
29833      * Returns true if this layout is currently being updated
29834      * @return {Boolean}
29835      */
29836     isUpdating : function(){
29837         return this.updating; 
29838     },
29839     
29840     /**
29841      * Suspend the LayoutManager from doing auto-layouts while
29842      * making multiple add or remove calls
29843      */
29844     beginUpdate : function(){
29845         this.updating = true;    
29846     },
29847     
29848     /**
29849      * Restore auto-layouts and optionally disable the manager from performing a layout
29850      * @param {Boolean} noLayout true to disable a layout update 
29851      */
29852     endUpdate : function(noLayout){
29853         this.updating = false;
29854         if(!noLayout){
29855             this.layout();
29856         }    
29857     },
29858     
29859     layout: function(){
29860         
29861     },
29862     
29863     onRegionResized : function(region, newSize){
29864         this.fireEvent("regionresized", region, newSize);
29865         this.layout();
29866     },
29867     
29868     onRegionCollapsed : function(region){
29869         this.fireEvent("regioncollapsed", region);
29870     },
29871     
29872     onRegionExpanded : function(region){
29873         this.fireEvent("regionexpanded", region);
29874     },
29875         
29876     /**
29877      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29878      * performs box-model adjustments.
29879      * @return {Object} The size as an object {width: (the width), height: (the height)}
29880      */
29881     getViewSize : function(){
29882         var size;
29883         if(this.el.dom != document.body){
29884             size = this.el.getSize();
29885         }else{
29886             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29887         }
29888         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29889         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29890         return size;
29891     },
29892     
29893     /**
29894      * Returns the Element this layout is bound to.
29895      * @return {Roo.Element}
29896      */
29897     getEl : function(){
29898         return this.el;
29899     },
29900     
29901     /**
29902      * Returns the specified region.
29903      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29904      * @return {Roo.LayoutRegion}
29905      */
29906     getRegion : function(target){
29907         return this.regions[target.toLowerCase()];
29908     },
29909     
29910     onWindowResize : function(){
29911         if(this.monitorWindowResize){
29912             this.layout();
29913         }
29914     }
29915 });/*
29916  * Based on:
29917  * Ext JS Library 1.1.1
29918  * Copyright(c) 2006-2007, Ext JS, LLC.
29919  *
29920  * Originally Released Under LGPL - original licence link has changed is not relivant.
29921  *
29922  * Fork - LGPL
29923  * <script type="text/javascript">
29924  */
29925 /**
29926  * @class Roo.BorderLayout
29927  * @extends Roo.LayoutManager
29928  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29929  * please see: <br><br>
29930  * <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>
29931  * <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>
29932  * Example:
29933  <pre><code>
29934  var layout = new Roo.BorderLayout(document.body, {
29935     north: {
29936         initialSize: 25,
29937         titlebar: false
29938     },
29939     west: {
29940         split:true,
29941         initialSize: 200,
29942         minSize: 175,
29943         maxSize: 400,
29944         titlebar: true,
29945         collapsible: true
29946     },
29947     east: {
29948         split:true,
29949         initialSize: 202,
29950         minSize: 175,
29951         maxSize: 400,
29952         titlebar: true,
29953         collapsible: true
29954     },
29955     south: {
29956         split:true,
29957         initialSize: 100,
29958         minSize: 100,
29959         maxSize: 200,
29960         titlebar: true,
29961         collapsible: true
29962     },
29963     center: {
29964         titlebar: true,
29965         autoScroll:true,
29966         resizeTabs: true,
29967         minTabWidth: 50,
29968         preferredTabWidth: 150
29969     }
29970 });
29971
29972 // shorthand
29973 var CP = Roo.ContentPanel;
29974
29975 layout.beginUpdate();
29976 layout.add("north", new CP("north", "North"));
29977 layout.add("south", new CP("south", {title: "South", closable: true}));
29978 layout.add("west", new CP("west", {title: "West"}));
29979 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29980 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29981 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29982 layout.getRegion("center").showPanel("center1");
29983 layout.endUpdate();
29984 </code></pre>
29985
29986 <b>The container the layout is rendered into can be either the body element or any other element.
29987 If it is not the body element, the container needs to either be an absolute positioned element,
29988 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29989 the container size if it is not the body element.</b>
29990
29991 * @constructor
29992 * Create a new BorderLayout
29993 * @param {String/HTMLElement/Element} container The container this layout is bound to
29994 * @param {Object} config Configuration options
29995  */
29996 Roo.BorderLayout = function(container, config){
29997     config = config || {};
29998     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29999     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30000     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30001         var target = this.factory.validRegions[i];
30002         if(config[target]){
30003             this.addRegion(target, config[target]);
30004         }
30005     }
30006 };
30007
30008 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30009     /**
30010      * Creates and adds a new region if it doesn't already exist.
30011      * @param {String} target The target region key (north, south, east, west or center).
30012      * @param {Object} config The regions config object
30013      * @return {BorderLayoutRegion} The new region
30014      */
30015     addRegion : function(target, config){
30016         if(!this.regions[target]){
30017             var r = this.factory.create(target, this, config);
30018             this.bindRegion(target, r);
30019         }
30020         return this.regions[target];
30021     },
30022
30023     // private (kinda)
30024     bindRegion : function(name, r){
30025         this.regions[name] = r;
30026         r.on("visibilitychange", this.layout, this);
30027         r.on("paneladded", this.layout, this);
30028         r.on("panelremoved", this.layout, this);
30029         r.on("invalidated", this.layout, this);
30030         r.on("resized", this.onRegionResized, this);
30031         r.on("collapsed", this.onRegionCollapsed, this);
30032         r.on("expanded", this.onRegionExpanded, this);
30033     },
30034
30035     /**
30036      * Performs a layout update.
30037      */
30038     layout : function(){
30039         if(this.updating) return;
30040         var size = this.getViewSize();
30041         var w = size.width;
30042         var h = size.height;
30043         var centerW = w;
30044         var centerH = h;
30045         var centerY = 0;
30046         var centerX = 0;
30047         //var x = 0, y = 0;
30048
30049         var rs = this.regions;
30050         var north = rs["north"];
30051         var south = rs["south"]; 
30052         var west = rs["west"];
30053         var east = rs["east"];
30054         var center = rs["center"];
30055         //if(this.hideOnLayout){ // not supported anymore
30056             //c.el.setStyle("display", "none");
30057         //}
30058         if(north && north.isVisible()){
30059             var b = north.getBox();
30060             var m = north.getMargins();
30061             b.width = w - (m.left+m.right);
30062             b.x = m.left;
30063             b.y = m.top;
30064             centerY = b.height + b.y + m.bottom;
30065             centerH -= centerY;
30066             north.updateBox(this.safeBox(b));
30067         }
30068         if(south && south.isVisible()){
30069             var b = south.getBox();
30070             var m = south.getMargins();
30071             b.width = w - (m.left+m.right);
30072             b.x = m.left;
30073             var totalHeight = (b.height + m.top + m.bottom);
30074             b.y = h - totalHeight + m.top;
30075             centerH -= totalHeight;
30076             south.updateBox(this.safeBox(b));
30077         }
30078         if(west && west.isVisible()){
30079             var b = west.getBox();
30080             var m = west.getMargins();
30081             b.height = centerH - (m.top+m.bottom);
30082             b.x = m.left;
30083             b.y = centerY + m.top;
30084             var totalWidth = (b.width + m.left + m.right);
30085             centerX += totalWidth;
30086             centerW -= totalWidth;
30087             west.updateBox(this.safeBox(b));
30088         }
30089         if(east && east.isVisible()){
30090             var b = east.getBox();
30091             var m = east.getMargins();
30092             b.height = centerH - (m.top+m.bottom);
30093             var totalWidth = (b.width + m.left + m.right);
30094             b.x = w - totalWidth + m.left;
30095             b.y = centerY + m.top;
30096             centerW -= totalWidth;
30097             east.updateBox(this.safeBox(b));
30098         }
30099         if(center){
30100             var m = center.getMargins();
30101             var centerBox = {
30102                 x: centerX + m.left,
30103                 y: centerY + m.top,
30104                 width: centerW - (m.left+m.right),
30105                 height: centerH - (m.top+m.bottom)
30106             };
30107             //if(this.hideOnLayout){
30108                 //center.el.setStyle("display", "block");
30109             //}
30110             center.updateBox(this.safeBox(centerBox));
30111         }
30112         this.el.repaint();
30113         this.fireEvent("layout", this);
30114     },
30115
30116     // private
30117     safeBox : function(box){
30118         box.width = Math.max(0, box.width);
30119         box.height = Math.max(0, box.height);
30120         return box;
30121     },
30122
30123     /**
30124      * Adds a ContentPanel (or subclass) to this layout.
30125      * @param {String} target The target region key (north, south, east, west or center).
30126      * @param {Roo.ContentPanel} panel The panel to add
30127      * @return {Roo.ContentPanel} The added panel
30128      */
30129     add : function(target, panel){
30130          
30131         target = target.toLowerCase();
30132         return this.regions[target].add(panel);
30133     },
30134
30135     /**
30136      * Remove a ContentPanel (or subclass) to this layout.
30137      * @param {String} target The target region key (north, south, east, west or center).
30138      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30139      * @return {Roo.ContentPanel} The removed panel
30140      */
30141     remove : function(target, panel){
30142         target = target.toLowerCase();
30143         return this.regions[target].remove(panel);
30144     },
30145
30146     /**
30147      * Searches all regions for a panel with the specified id
30148      * @param {String} panelId
30149      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30150      */
30151     findPanel : function(panelId){
30152         var rs = this.regions;
30153         for(var target in rs){
30154             if(typeof rs[target] != "function"){
30155                 var p = rs[target].getPanel(panelId);
30156                 if(p){
30157                     return p;
30158                 }
30159             }
30160         }
30161         return null;
30162     },
30163
30164     /**
30165      * Searches all regions for a panel with the specified id and activates (shows) it.
30166      * @param {String/ContentPanel} panelId The panels id or the panel itself
30167      * @return {Roo.ContentPanel} The shown panel or null
30168      */
30169     showPanel : function(panelId) {
30170       var rs = this.regions;
30171       for(var target in rs){
30172          var r = rs[target];
30173          if(typeof r != "function"){
30174             if(r.hasPanel(panelId)){
30175                return r.showPanel(panelId);
30176             }
30177          }
30178       }
30179       return null;
30180    },
30181
30182    /**
30183      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30184      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30185      */
30186     restoreState : function(provider){
30187         if(!provider){
30188             provider = Roo.state.Manager;
30189         }
30190         var sm = new Roo.LayoutStateManager();
30191         sm.init(this, provider);
30192     },
30193
30194     /**
30195      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30196      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30197      * a valid ContentPanel config object.  Example:
30198      * <pre><code>
30199 // Create the main layout
30200 var layout = new Roo.BorderLayout('main-ct', {
30201     west: {
30202         split:true,
30203         minSize: 175,
30204         titlebar: true
30205     },
30206     center: {
30207         title:'Components'
30208     }
30209 }, 'main-ct');
30210
30211 // Create and add multiple ContentPanels at once via configs
30212 layout.batchAdd({
30213    west: {
30214        id: 'source-files',
30215        autoCreate:true,
30216        title:'Ext Source Files',
30217        autoScroll:true,
30218        fitToFrame:true
30219    },
30220    center : {
30221        el: cview,
30222        autoScroll:true,
30223        fitToFrame:true,
30224        toolbar: tb,
30225        resizeEl:'cbody'
30226    }
30227 });
30228 </code></pre>
30229      * @param {Object} regions An object containing ContentPanel configs by region name
30230      */
30231     batchAdd : function(regions){
30232         this.beginUpdate();
30233         for(var rname in regions){
30234             var lr = this.regions[rname];
30235             if(lr){
30236                 this.addTypedPanels(lr, regions[rname]);
30237             }
30238         }
30239         this.endUpdate();
30240     },
30241
30242     // private
30243     addTypedPanels : function(lr, ps){
30244         if(typeof ps == 'string'){
30245             lr.add(new Roo.ContentPanel(ps));
30246         }
30247         else if(ps instanceof Array){
30248             for(var i =0, len = ps.length; i < len; i++){
30249                 this.addTypedPanels(lr, ps[i]);
30250             }
30251         }
30252         else if(!ps.events){ // raw config?
30253             var el = ps.el;
30254             delete ps.el; // prevent conflict
30255             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30256         }
30257         else {  // panel object assumed!
30258             lr.add(ps);
30259         }
30260     },
30261     /**
30262      * Adds a xtype elements to the layout.
30263      * <pre><code>
30264
30265 layout.addxtype({
30266        xtype : 'ContentPanel',
30267        region: 'west',
30268        items: [ .... ]
30269    }
30270 );
30271
30272 layout.addxtype({
30273         xtype : 'NestedLayoutPanel',
30274         region: 'west',
30275         layout: {
30276            center: { },
30277            west: { }   
30278         },
30279         items : [ ... list of content panels or nested layout panels.. ]
30280    }
30281 );
30282 </code></pre>
30283      * @param {Object} cfg Xtype definition of item to add.
30284      */
30285     addxtype : function(cfg)
30286     {
30287         // basically accepts a pannel...
30288         // can accept a layout region..!?!?
30289         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30290         
30291         if (!cfg.xtype.match(/Panel$/)) {
30292             return false;
30293         }
30294         var ret = false;
30295         
30296         if (typeof(cfg.region) == 'undefined') {
30297             Roo.log("Failed to add Panel, region was not set");
30298             Roo.log(cfg);
30299             return false;
30300         }
30301         var region = cfg.region;
30302         delete cfg.region;
30303         
30304           
30305         var xitems = [];
30306         if (cfg.items) {
30307             xitems = cfg.items;
30308             delete cfg.items;
30309         }
30310         var nb = false;
30311         
30312         switch(cfg.xtype) 
30313         {
30314             case 'ContentPanel':  // ContentPanel (el, cfg)
30315             case 'ScrollPanel':  // ContentPanel (el, cfg)
30316                 if(cfg.autoCreate) {
30317                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30318                 } else {
30319                     var el = this.el.createChild();
30320                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30321                 }
30322                 
30323                 this.add(region, ret);
30324                 break;
30325             
30326             
30327             case 'TreePanel': // our new panel!
30328                 cfg.el = this.el.createChild();
30329                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30330                 this.add(region, ret);
30331                 break;
30332             
30333             case 'NestedLayoutPanel': 
30334                 // create a new Layout (which is  a Border Layout...
30335                 var el = this.el.createChild();
30336                 var clayout = cfg.layout;
30337                 delete cfg.layout;
30338                 clayout.items   = clayout.items  || [];
30339                 // replace this exitems with the clayout ones..
30340                 xitems = clayout.items;
30341                  
30342                 
30343                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30344                     cfg.background = false;
30345                 }
30346                 var layout = new Roo.BorderLayout(el, clayout);
30347                 
30348                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30349                 //console.log('adding nested layout panel '  + cfg.toSource());
30350                 this.add(region, ret);
30351                 nb = {}; /// find first...
30352                 break;
30353                 
30354             case 'GridPanel': 
30355             
30356                 // needs grid and region
30357                 
30358                 //var el = this.getRegion(region).el.createChild();
30359                 var el = this.el.createChild();
30360                 // create the grid first...
30361                 
30362                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30363                 delete cfg.grid;
30364                 if (region == 'center' && this.active ) {
30365                     cfg.background = false;
30366                 }
30367                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30368                 
30369                 this.add(region, ret);
30370                 if (cfg.background) {
30371                     ret.on('activate', function(gp) {
30372                         if (!gp.grid.rendered) {
30373                             gp.grid.render();
30374                         }
30375                     });
30376                 } else {
30377                     grid.render();
30378                 }
30379                 break;
30380            
30381                
30382                 
30383                 
30384             default: 
30385                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30386                 return null;
30387              // GridPanel (grid, cfg)
30388             
30389         }
30390         this.beginUpdate();
30391         // add children..
30392         var region = '';
30393         var abn = {};
30394         Roo.each(xitems, function(i)  {
30395             region = nb && i.region ? i.region : false;
30396             
30397             var add = ret.addxtype(i);
30398            
30399             if (region) {
30400                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
30401                 if (!i.background) {
30402                     abn[region] = nb[region] ;
30403                 }
30404             }
30405             
30406         });
30407         this.endUpdate();
30408
30409         // make the last non-background panel active..
30410         //if (nb) { Roo.log(abn); }
30411         if (nb) {
30412             
30413             for(var r in abn) {
30414                 region = this.getRegion(r);
30415                 if (region) {
30416                     // tried using nb[r], but it does not work..
30417                      
30418                     region.showPanel(abn[r]);
30419                    
30420                 }
30421             }
30422         }
30423         return ret;
30424         
30425     }
30426 });
30427
30428 /**
30429  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
30430  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
30431  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
30432  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
30433  * <pre><code>
30434 // shorthand
30435 var CP = Roo.ContentPanel;
30436
30437 var layout = Roo.BorderLayout.create({
30438     north: {
30439         initialSize: 25,
30440         titlebar: false,
30441         panels: [new CP("north", "North")]
30442     },
30443     west: {
30444         split:true,
30445         initialSize: 200,
30446         minSize: 175,
30447         maxSize: 400,
30448         titlebar: true,
30449         collapsible: true,
30450         panels: [new CP("west", {title: "West"})]
30451     },
30452     east: {
30453         split:true,
30454         initialSize: 202,
30455         minSize: 175,
30456         maxSize: 400,
30457         titlebar: true,
30458         collapsible: true,
30459         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
30460     },
30461     south: {
30462         split:true,
30463         initialSize: 100,
30464         minSize: 100,
30465         maxSize: 200,
30466         titlebar: true,
30467         collapsible: true,
30468         panels: [new CP("south", {title: "South", closable: true})]
30469     },
30470     center: {
30471         titlebar: true,
30472         autoScroll:true,
30473         resizeTabs: true,
30474         minTabWidth: 50,
30475         preferredTabWidth: 150,
30476         panels: [
30477             new CP("center1", {title: "Close Me", closable: true}),
30478             new CP("center2", {title: "Center Panel", closable: false})
30479         ]
30480     }
30481 }, document.body);
30482
30483 layout.getRegion("center").showPanel("center1");
30484 </code></pre>
30485  * @param config
30486  * @param targetEl
30487  */
30488 Roo.BorderLayout.create = function(config, targetEl){
30489     var layout = new Roo.BorderLayout(targetEl || document.body, config);
30490     layout.beginUpdate();
30491     var regions = Roo.BorderLayout.RegionFactory.validRegions;
30492     for(var j = 0, jlen = regions.length; j < jlen; j++){
30493         var lr = regions[j];
30494         if(layout.regions[lr] && config[lr].panels){
30495             var r = layout.regions[lr];
30496             var ps = config[lr].panels;
30497             layout.addTypedPanels(r, ps);
30498         }
30499     }
30500     layout.endUpdate();
30501     return layout;
30502 };
30503
30504 // private
30505 Roo.BorderLayout.RegionFactory = {
30506     // private
30507     validRegions : ["north","south","east","west","center"],
30508
30509     // private
30510     create : function(target, mgr, config){
30511         target = target.toLowerCase();
30512         if(config.lightweight || config.basic){
30513             return new Roo.BasicLayoutRegion(mgr, config, target);
30514         }
30515         switch(target){
30516             case "north":
30517                 return new Roo.NorthLayoutRegion(mgr, config);
30518             case "south":
30519                 return new Roo.SouthLayoutRegion(mgr, config);
30520             case "east":
30521                 return new Roo.EastLayoutRegion(mgr, config);
30522             case "west":
30523                 return new Roo.WestLayoutRegion(mgr, config);
30524             case "center":
30525                 return new Roo.CenterLayoutRegion(mgr, config);
30526         }
30527         throw 'Layout region "'+target+'" not supported.';
30528     }
30529 };/*
30530  * Based on:
30531  * Ext JS Library 1.1.1
30532  * Copyright(c) 2006-2007, Ext JS, LLC.
30533  *
30534  * Originally Released Under LGPL - original licence link has changed is not relivant.
30535  *
30536  * Fork - LGPL
30537  * <script type="text/javascript">
30538  */
30539  
30540 /**
30541  * @class Roo.BasicLayoutRegion
30542  * @extends Roo.util.Observable
30543  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
30544  * and does not have a titlebar, tabs or any other features. All it does is size and position 
30545  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
30546  */
30547 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
30548     this.mgr = mgr;
30549     this.position  = pos;
30550     this.events = {
30551         /**
30552          * @scope Roo.BasicLayoutRegion
30553          */
30554         
30555         /**
30556          * @event beforeremove
30557          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30558          * @param {Roo.LayoutRegion} this
30559          * @param {Roo.ContentPanel} panel The panel
30560          * @param {Object} e The cancel event object
30561          */
30562         "beforeremove" : true,
30563         /**
30564          * @event invalidated
30565          * Fires when the layout for this region is changed.
30566          * @param {Roo.LayoutRegion} this
30567          */
30568         "invalidated" : true,
30569         /**
30570          * @event visibilitychange
30571          * Fires when this region is shown or hidden 
30572          * @param {Roo.LayoutRegion} this
30573          * @param {Boolean} visibility true or false
30574          */
30575         "visibilitychange" : true,
30576         /**
30577          * @event paneladded
30578          * Fires when a panel is added. 
30579          * @param {Roo.LayoutRegion} this
30580          * @param {Roo.ContentPanel} panel The panel
30581          */
30582         "paneladded" : true,
30583         /**
30584          * @event panelremoved
30585          * Fires when a panel is removed. 
30586          * @param {Roo.LayoutRegion} this
30587          * @param {Roo.ContentPanel} panel The panel
30588          */
30589         "panelremoved" : true,
30590         /**
30591          * @event collapsed
30592          * Fires when this region is collapsed.
30593          * @param {Roo.LayoutRegion} this
30594          */
30595         "collapsed" : true,
30596         /**
30597          * @event expanded
30598          * Fires when this region is expanded.
30599          * @param {Roo.LayoutRegion} this
30600          */
30601         "expanded" : true,
30602         /**
30603          * @event slideshow
30604          * Fires when this region is slid into view.
30605          * @param {Roo.LayoutRegion} this
30606          */
30607         "slideshow" : true,
30608         /**
30609          * @event slidehide
30610          * Fires when this region slides out of view. 
30611          * @param {Roo.LayoutRegion} this
30612          */
30613         "slidehide" : true,
30614         /**
30615          * @event panelactivated
30616          * Fires when a panel is activated. 
30617          * @param {Roo.LayoutRegion} this
30618          * @param {Roo.ContentPanel} panel The activated panel
30619          */
30620         "panelactivated" : true,
30621         /**
30622          * @event resized
30623          * Fires when the user resizes this region. 
30624          * @param {Roo.LayoutRegion} this
30625          * @param {Number} newSize The new size (width for east/west, height for north/south)
30626          */
30627         "resized" : true
30628     };
30629     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30630     this.panels = new Roo.util.MixedCollection();
30631     this.panels.getKey = this.getPanelId.createDelegate(this);
30632     this.box = null;
30633     this.activePanel = null;
30634     // ensure listeners are added...
30635     
30636     if (config.listeners || config.events) {
30637         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30638             listeners : config.listeners || {},
30639             events : config.events || {}
30640         });
30641     }
30642     
30643     if(skipConfig !== true){
30644         this.applyConfig(config);
30645     }
30646 };
30647
30648 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30649     getPanelId : function(p){
30650         return p.getId();
30651     },
30652     
30653     applyConfig : function(config){
30654         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30655         this.config = config;
30656         
30657     },
30658     
30659     /**
30660      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30661      * the width, for horizontal (north, south) the height.
30662      * @param {Number} newSize The new width or height
30663      */
30664     resizeTo : function(newSize){
30665         var el = this.el ? this.el :
30666                  (this.activePanel ? this.activePanel.getEl() : null);
30667         if(el){
30668             switch(this.position){
30669                 case "east":
30670                 case "west":
30671                     el.setWidth(newSize);
30672                     this.fireEvent("resized", this, newSize);
30673                 break;
30674                 case "north":
30675                 case "south":
30676                     el.setHeight(newSize);
30677                     this.fireEvent("resized", this, newSize);
30678                 break;                
30679             }
30680         }
30681     },
30682     
30683     getBox : function(){
30684         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30685     },
30686     
30687     getMargins : function(){
30688         return this.margins;
30689     },
30690     
30691     updateBox : function(box){
30692         this.box = box;
30693         var el = this.activePanel.getEl();
30694         el.dom.style.left = box.x + "px";
30695         el.dom.style.top = box.y + "px";
30696         this.activePanel.setSize(box.width, box.height);
30697     },
30698     
30699     /**
30700      * Returns the container element for this region.
30701      * @return {Roo.Element}
30702      */
30703     getEl : function(){
30704         return this.activePanel;
30705     },
30706     
30707     /**
30708      * Returns true if this region is currently visible.
30709      * @return {Boolean}
30710      */
30711     isVisible : function(){
30712         return this.activePanel ? true : false;
30713     },
30714     
30715     setActivePanel : function(panel){
30716         panel = this.getPanel(panel);
30717         if(this.activePanel && this.activePanel != panel){
30718             this.activePanel.setActiveState(false);
30719             this.activePanel.getEl().setLeftTop(-10000,-10000);
30720         }
30721         this.activePanel = panel;
30722         panel.setActiveState(true);
30723         if(this.box){
30724             panel.setSize(this.box.width, this.box.height);
30725         }
30726         this.fireEvent("panelactivated", this, panel);
30727         this.fireEvent("invalidated");
30728     },
30729     
30730     /**
30731      * Show the specified panel.
30732      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30733      * @return {Roo.ContentPanel} The shown panel or null
30734      */
30735     showPanel : function(panel){
30736         if(panel = this.getPanel(panel)){
30737             this.setActivePanel(panel);
30738         }
30739         return panel;
30740     },
30741     
30742     /**
30743      * Get the active panel for this region.
30744      * @return {Roo.ContentPanel} The active panel or null
30745      */
30746     getActivePanel : function(){
30747         return this.activePanel;
30748     },
30749     
30750     /**
30751      * Add the passed ContentPanel(s)
30752      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30753      * @return {Roo.ContentPanel} The panel added (if only one was added)
30754      */
30755     add : function(panel){
30756         if(arguments.length > 1){
30757             for(var i = 0, len = arguments.length; i < len; i++) {
30758                 this.add(arguments[i]);
30759             }
30760             return null;
30761         }
30762         if(this.hasPanel(panel)){
30763             this.showPanel(panel);
30764             return panel;
30765         }
30766         var el = panel.getEl();
30767         if(el.dom.parentNode != this.mgr.el.dom){
30768             this.mgr.el.dom.appendChild(el.dom);
30769         }
30770         if(panel.setRegion){
30771             panel.setRegion(this);
30772         }
30773         this.panels.add(panel);
30774         el.setStyle("position", "absolute");
30775         if(!panel.background){
30776             this.setActivePanel(panel);
30777             if(this.config.initialSize && this.panels.getCount()==1){
30778                 this.resizeTo(this.config.initialSize);
30779             }
30780         }
30781         this.fireEvent("paneladded", this, panel);
30782         return panel;
30783     },
30784     
30785     /**
30786      * Returns true if the panel is in this region.
30787      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30788      * @return {Boolean}
30789      */
30790     hasPanel : function(panel){
30791         if(typeof panel == "object"){ // must be panel obj
30792             panel = panel.getId();
30793         }
30794         return this.getPanel(panel) ? true : false;
30795     },
30796     
30797     /**
30798      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30799      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30800      * @param {Boolean} preservePanel Overrides the config preservePanel option
30801      * @return {Roo.ContentPanel} The panel that was removed
30802      */
30803     remove : function(panel, preservePanel){
30804         panel = this.getPanel(panel);
30805         if(!panel){
30806             return null;
30807         }
30808         var e = {};
30809         this.fireEvent("beforeremove", this, panel, e);
30810         if(e.cancel === true){
30811             return null;
30812         }
30813         var panelId = panel.getId();
30814         this.panels.removeKey(panelId);
30815         return panel;
30816     },
30817     
30818     /**
30819      * Returns the panel specified or null if it's not in this region.
30820      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30821      * @return {Roo.ContentPanel}
30822      */
30823     getPanel : function(id){
30824         if(typeof id == "object"){ // must be panel obj
30825             return id;
30826         }
30827         return this.panels.get(id);
30828     },
30829     
30830     /**
30831      * Returns this regions position (north/south/east/west/center).
30832      * @return {String} 
30833      */
30834     getPosition: function(){
30835         return this.position;    
30836     }
30837 });/*
30838  * Based on:
30839  * Ext JS Library 1.1.1
30840  * Copyright(c) 2006-2007, Ext JS, LLC.
30841  *
30842  * Originally Released Under LGPL - original licence link has changed is not relivant.
30843  *
30844  * Fork - LGPL
30845  * <script type="text/javascript">
30846  */
30847  
30848 /**
30849  * @class Roo.LayoutRegion
30850  * @extends Roo.BasicLayoutRegion
30851  * This class represents a region in a layout manager.
30852  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30853  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30854  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30855  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30856  * @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})
30857  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
30858  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30859  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30860  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30861  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30862  * @cfg {String}    title           The title for the region (overrides panel titles)
30863  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30864  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30865  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30866  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30867  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30868  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30869  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30870  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30871  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30872  * @cfg {Boolean}   showPin         True to show a pin button
30873  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30874  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30875  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30876  * @cfg {Number}    width           For East/West panels
30877  * @cfg {Number}    height          For North/South panels
30878  * @cfg {Boolean}   split           To show the splitter
30879  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30880  */
30881 Roo.LayoutRegion = function(mgr, config, pos){
30882     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30883     var dh = Roo.DomHelper;
30884     /** This region's container element 
30885     * @type Roo.Element */
30886     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30887     /** This region's title element 
30888     * @type Roo.Element */
30889
30890     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30891         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30892         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30893     ]}, true);
30894     this.titleEl.enableDisplayMode();
30895     /** This region's title text element 
30896     * @type HTMLElement */
30897     this.titleTextEl = this.titleEl.dom.firstChild;
30898     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30899     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30900     this.closeBtn.enableDisplayMode();
30901     this.closeBtn.on("click", this.closeClicked, this);
30902     this.closeBtn.hide();
30903
30904     this.createBody(config);
30905     this.visible = true;
30906     this.collapsed = false;
30907
30908     if(config.hideWhenEmpty){
30909         this.hide();
30910         this.on("paneladded", this.validateVisibility, this);
30911         this.on("panelremoved", this.validateVisibility, this);
30912     }
30913     this.applyConfig(config);
30914 };
30915
30916 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30917
30918     createBody : function(){
30919         /** This region's body element 
30920         * @type Roo.Element */
30921         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30922     },
30923
30924     applyConfig : function(c){
30925         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30926             var dh = Roo.DomHelper;
30927             if(c.titlebar !== false){
30928                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30929                 this.collapseBtn.on("click", this.collapse, this);
30930                 this.collapseBtn.enableDisplayMode();
30931
30932                 if(c.showPin === true || this.showPin){
30933                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30934                     this.stickBtn.enableDisplayMode();
30935                     this.stickBtn.on("click", this.expand, this);
30936                     this.stickBtn.hide();
30937                 }
30938             }
30939             /** This region's collapsed element
30940             * @type Roo.Element */
30941             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30942                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30943             ]}, true);
30944             if(c.floatable !== false){
30945                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30946                this.collapsedEl.on("click", this.collapseClick, this);
30947             }
30948
30949             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30950                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30951                    id: "message", unselectable: "on", style:{"float":"left"}});
30952                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30953              }
30954             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30955             this.expandBtn.on("click", this.expand, this);
30956         }
30957         if(this.collapseBtn){
30958             this.collapseBtn.setVisible(c.collapsible == true);
30959         }
30960         this.cmargins = c.cmargins || this.cmargins ||
30961                          (this.position == "west" || this.position == "east" ?
30962                              {top: 0, left: 2, right:2, bottom: 0} :
30963                              {top: 2, left: 0, right:0, bottom: 2});
30964         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30965         this.bottomTabs = c.tabPosition != "top";
30966         this.autoScroll = c.autoScroll || false;
30967         if(this.autoScroll){
30968             this.bodyEl.setStyle("overflow", "auto");
30969         }else{
30970             this.bodyEl.setStyle("overflow", "hidden");
30971         }
30972         //if(c.titlebar !== false){
30973             if((!c.titlebar && !c.title) || c.titlebar === false){
30974                 this.titleEl.hide();
30975             }else{
30976                 this.titleEl.show();
30977                 if(c.title){
30978                     this.titleTextEl.innerHTML = c.title;
30979                 }
30980             }
30981         //}
30982         this.duration = c.duration || .30;
30983         this.slideDuration = c.slideDuration || .45;
30984         this.config = c;
30985         if(c.collapsed){
30986             this.collapse(true);
30987         }
30988         if(c.hidden){
30989             this.hide();
30990         }
30991     },
30992     /**
30993      * Returns true if this region is currently visible.
30994      * @return {Boolean}
30995      */
30996     isVisible : function(){
30997         return this.visible;
30998     },
30999
31000     /**
31001      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31002      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31003      */
31004     setCollapsedTitle : function(title){
31005         title = title || "&#160;";
31006         if(this.collapsedTitleTextEl){
31007             this.collapsedTitleTextEl.innerHTML = title;
31008         }
31009     },
31010
31011     getBox : function(){
31012         var b;
31013         if(!this.collapsed){
31014             b = this.el.getBox(false, true);
31015         }else{
31016             b = this.collapsedEl.getBox(false, true);
31017         }
31018         return b;
31019     },
31020
31021     getMargins : function(){
31022         return this.collapsed ? this.cmargins : this.margins;
31023     },
31024
31025     highlight : function(){
31026         this.el.addClass("x-layout-panel-dragover");
31027     },
31028
31029     unhighlight : function(){
31030         this.el.removeClass("x-layout-panel-dragover");
31031     },
31032
31033     updateBox : function(box){
31034         this.box = box;
31035         if(!this.collapsed){
31036             this.el.dom.style.left = box.x + "px";
31037             this.el.dom.style.top = box.y + "px";
31038             this.updateBody(box.width, box.height);
31039         }else{
31040             this.collapsedEl.dom.style.left = box.x + "px";
31041             this.collapsedEl.dom.style.top = box.y + "px";
31042             this.collapsedEl.setSize(box.width, box.height);
31043         }
31044         if(this.tabs){
31045             this.tabs.autoSizeTabs();
31046         }
31047     },
31048
31049     updateBody : function(w, h){
31050         if(w !== null){
31051             this.el.setWidth(w);
31052             w -= this.el.getBorderWidth("rl");
31053             if(this.config.adjustments){
31054                 w += this.config.adjustments[0];
31055             }
31056         }
31057         if(h !== null){
31058             this.el.setHeight(h);
31059             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31060             h -= this.el.getBorderWidth("tb");
31061             if(this.config.adjustments){
31062                 h += this.config.adjustments[1];
31063             }
31064             this.bodyEl.setHeight(h);
31065             if(this.tabs){
31066                 h = this.tabs.syncHeight(h);
31067             }
31068         }
31069         if(this.panelSize){
31070             w = w !== null ? w : this.panelSize.width;
31071             h = h !== null ? h : this.panelSize.height;
31072         }
31073         if(this.activePanel){
31074             var el = this.activePanel.getEl();
31075             w = w !== null ? w : el.getWidth();
31076             h = h !== null ? h : el.getHeight();
31077             this.panelSize = {width: w, height: h};
31078             this.activePanel.setSize(w, h);
31079         }
31080         if(Roo.isIE && this.tabs){
31081             this.tabs.el.repaint();
31082         }
31083     },
31084
31085     /**
31086      * Returns the container element for this region.
31087      * @return {Roo.Element}
31088      */
31089     getEl : function(){
31090         return this.el;
31091     },
31092
31093     /**
31094      * Hides this region.
31095      */
31096     hide : function(){
31097         if(!this.collapsed){
31098             this.el.dom.style.left = "-2000px";
31099             this.el.hide();
31100         }else{
31101             this.collapsedEl.dom.style.left = "-2000px";
31102             this.collapsedEl.hide();
31103         }
31104         this.visible = false;
31105         this.fireEvent("visibilitychange", this, false);
31106     },
31107
31108     /**
31109      * Shows this region if it was previously hidden.
31110      */
31111     show : function(){
31112         if(!this.collapsed){
31113             this.el.show();
31114         }else{
31115             this.collapsedEl.show();
31116         }
31117         this.visible = true;
31118         this.fireEvent("visibilitychange", this, true);
31119     },
31120
31121     closeClicked : function(){
31122         if(this.activePanel){
31123             this.remove(this.activePanel);
31124         }
31125     },
31126
31127     collapseClick : function(e){
31128         if(this.isSlid){
31129            e.stopPropagation();
31130            this.slideIn();
31131         }else{
31132            e.stopPropagation();
31133            this.slideOut();
31134         }
31135     },
31136
31137     /**
31138      * Collapses this region.
31139      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31140      */
31141     collapse : function(skipAnim){
31142         if(this.collapsed) return;
31143         this.collapsed = true;
31144         if(this.split){
31145             this.split.el.hide();
31146         }
31147         if(this.config.animate && skipAnim !== true){
31148             this.fireEvent("invalidated", this);
31149             this.animateCollapse();
31150         }else{
31151             this.el.setLocation(-20000,-20000);
31152             this.el.hide();
31153             this.collapsedEl.show();
31154             this.fireEvent("collapsed", this);
31155             this.fireEvent("invalidated", this);
31156         }
31157     },
31158
31159     animateCollapse : function(){
31160         // overridden
31161     },
31162
31163     /**
31164      * Expands this region if it was previously collapsed.
31165      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31166      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31167      */
31168     expand : function(e, skipAnim){
31169         if(e) e.stopPropagation();
31170         if(!this.collapsed || this.el.hasActiveFx()) return;
31171         if(this.isSlid){
31172             this.afterSlideIn();
31173             skipAnim = true;
31174         }
31175         this.collapsed = false;
31176         if(this.config.animate && skipAnim !== true){
31177             this.animateExpand();
31178         }else{
31179             this.el.show();
31180             if(this.split){
31181                 this.split.el.show();
31182             }
31183             this.collapsedEl.setLocation(-2000,-2000);
31184             this.collapsedEl.hide();
31185             this.fireEvent("invalidated", this);
31186             this.fireEvent("expanded", this);
31187         }
31188     },
31189
31190     animateExpand : function(){
31191         // overridden
31192     },
31193
31194     initTabs : function()
31195     {
31196         this.bodyEl.setStyle("overflow", "hidden");
31197         var ts = new Roo.TabPanel(
31198                 this.bodyEl.dom,
31199                 {
31200                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31201                     disableTooltips: this.config.disableTabTips,
31202                     toolbar : this.config.toolbar
31203                 }
31204         );
31205         if(this.config.hideTabs){
31206             ts.stripWrap.setDisplayed(false);
31207         }
31208         this.tabs = ts;
31209         ts.resizeTabs = this.config.resizeTabs === true;
31210         ts.minTabWidth = this.config.minTabWidth || 40;
31211         ts.maxTabWidth = this.config.maxTabWidth || 250;
31212         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31213         ts.monitorResize = false;
31214         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31215         ts.bodyEl.addClass('x-layout-tabs-body');
31216         this.panels.each(this.initPanelAsTab, this);
31217     },
31218
31219     initPanelAsTab : function(panel){
31220         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31221                     this.config.closeOnTab && panel.isClosable());
31222         if(panel.tabTip !== undefined){
31223             ti.setTooltip(panel.tabTip);
31224         }
31225         ti.on("activate", function(){
31226               this.setActivePanel(panel);
31227         }, this);
31228         if(this.config.closeOnTab){
31229             ti.on("beforeclose", function(t, e){
31230                 e.cancel = true;
31231                 this.remove(panel);
31232             }, this);
31233         }
31234         return ti;
31235     },
31236
31237     updatePanelTitle : function(panel, title){
31238         if(this.activePanel == panel){
31239             this.updateTitle(title);
31240         }
31241         if(this.tabs){
31242             var ti = this.tabs.getTab(panel.getEl().id);
31243             ti.setText(title);
31244             if(panel.tabTip !== undefined){
31245                 ti.setTooltip(panel.tabTip);
31246             }
31247         }
31248     },
31249
31250     updateTitle : function(title){
31251         if(this.titleTextEl && !this.config.title){
31252             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31253         }
31254     },
31255
31256     setActivePanel : function(panel){
31257         panel = this.getPanel(panel);
31258         if(this.activePanel && this.activePanel != panel){
31259             this.activePanel.setActiveState(false);
31260         }
31261         this.activePanel = panel;
31262         panel.setActiveState(true);
31263         if(this.panelSize){
31264             panel.setSize(this.panelSize.width, this.panelSize.height);
31265         }
31266         if(this.closeBtn){
31267             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31268         }
31269         this.updateTitle(panel.getTitle());
31270         if(this.tabs){
31271             this.fireEvent("invalidated", this);
31272         }
31273         this.fireEvent("panelactivated", this, panel);
31274     },
31275
31276     /**
31277      * Shows the specified panel.
31278      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31279      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31280      */
31281     showPanel : function(panel){
31282         if(panel = this.getPanel(panel)){
31283             if(this.tabs){
31284                 var tab = this.tabs.getTab(panel.getEl().id);
31285                 if(tab.isHidden()){
31286                     this.tabs.unhideTab(tab.id);
31287                 }
31288                 tab.activate();
31289             }else{
31290                 this.setActivePanel(panel);
31291             }
31292         }
31293         return panel;
31294     },
31295
31296     /**
31297      * Get the active panel for this region.
31298      * @return {Roo.ContentPanel} The active panel or null
31299      */
31300     getActivePanel : function(){
31301         return this.activePanel;
31302     },
31303
31304     validateVisibility : function(){
31305         if(this.panels.getCount() < 1){
31306             this.updateTitle("&#160;");
31307             this.closeBtn.hide();
31308             this.hide();
31309         }else{
31310             if(!this.isVisible()){
31311                 this.show();
31312             }
31313         }
31314     },
31315
31316     /**
31317      * Adds the passed ContentPanel(s) to this region.
31318      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31319      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31320      */
31321     add : function(panel){
31322         if(arguments.length > 1){
31323             for(var i = 0, len = arguments.length; i < len; i++) {
31324                 this.add(arguments[i]);
31325             }
31326             return null;
31327         }
31328         if(this.hasPanel(panel)){
31329             this.showPanel(panel);
31330             return panel;
31331         }
31332         panel.setRegion(this);
31333         this.panels.add(panel);
31334         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31335             this.bodyEl.dom.appendChild(panel.getEl().dom);
31336             if(panel.background !== true){
31337                 this.setActivePanel(panel);
31338             }
31339             this.fireEvent("paneladded", this, panel);
31340             return panel;
31341         }
31342         if(!this.tabs){
31343             this.initTabs();
31344         }else{
31345             this.initPanelAsTab(panel);
31346         }
31347         if(panel.background !== true){
31348             this.tabs.activate(panel.getEl().id);
31349         }
31350         this.fireEvent("paneladded", this, panel);
31351         return panel;
31352     },
31353
31354     /**
31355      * Hides the tab for the specified panel.
31356      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31357      */
31358     hidePanel : function(panel){
31359         if(this.tabs && (panel = this.getPanel(panel))){
31360             this.tabs.hideTab(panel.getEl().id);
31361         }
31362     },
31363
31364     /**
31365      * Unhides the tab for a previously hidden panel.
31366      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31367      */
31368     unhidePanel : function(panel){
31369         if(this.tabs && (panel = this.getPanel(panel))){
31370             this.tabs.unhideTab(panel.getEl().id);
31371         }
31372     },
31373
31374     clearPanels : function(){
31375         while(this.panels.getCount() > 0){
31376              this.remove(this.panels.first());
31377         }
31378     },
31379
31380     /**
31381      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31382      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31383      * @param {Boolean} preservePanel Overrides the config preservePanel option
31384      * @return {Roo.ContentPanel} The panel that was removed
31385      */
31386     remove : function(panel, preservePanel){
31387         panel = this.getPanel(panel);
31388         if(!panel){
31389             return null;
31390         }
31391         var e = {};
31392         this.fireEvent("beforeremove", this, panel, e);
31393         if(e.cancel === true){
31394             return null;
31395         }
31396         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31397         var panelId = panel.getId();
31398         this.panels.removeKey(panelId);
31399         if(preservePanel){
31400             document.body.appendChild(panel.getEl().dom);
31401         }
31402         if(this.tabs){
31403             this.tabs.removeTab(panel.getEl().id);
31404         }else if (!preservePanel){
31405             this.bodyEl.dom.removeChild(panel.getEl().dom);
31406         }
31407         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
31408             var p = this.panels.first();
31409             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
31410             tempEl.appendChild(p.getEl().dom);
31411             this.bodyEl.update("");
31412             this.bodyEl.dom.appendChild(p.getEl().dom);
31413             tempEl = null;
31414             this.updateTitle(p.getTitle());
31415             this.tabs = null;
31416             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31417             this.setActivePanel(p);
31418         }
31419         panel.setRegion(null);
31420         if(this.activePanel == panel){
31421             this.activePanel = null;
31422         }
31423         if(this.config.autoDestroy !== false && preservePanel !== true){
31424             try{panel.destroy();}catch(e){}
31425         }
31426         this.fireEvent("panelremoved", this, panel);
31427         return panel;
31428     },
31429
31430     /**
31431      * Returns the TabPanel component used by this region
31432      * @return {Roo.TabPanel}
31433      */
31434     getTabs : function(){
31435         return this.tabs;
31436     },
31437
31438     createTool : function(parentEl, className){
31439         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
31440             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
31441         btn.addClassOnOver("x-layout-tools-button-over");
31442         return btn;
31443     }
31444 });/*
31445  * Based on:
31446  * Ext JS Library 1.1.1
31447  * Copyright(c) 2006-2007, Ext JS, LLC.
31448  *
31449  * Originally Released Under LGPL - original licence link has changed is not relivant.
31450  *
31451  * Fork - LGPL
31452  * <script type="text/javascript">
31453  */
31454  
31455
31456
31457 /**
31458  * @class Roo.SplitLayoutRegion
31459  * @extends Roo.LayoutRegion
31460  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
31461  */
31462 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
31463     this.cursor = cursor;
31464     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
31465 };
31466
31467 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
31468     splitTip : "Drag to resize.",
31469     collapsibleSplitTip : "Drag to resize. Double click to hide.",
31470     useSplitTips : false,
31471
31472     applyConfig : function(config){
31473         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
31474         if(config.split){
31475             if(!this.split){
31476                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
31477                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
31478                 /** The SplitBar for this region 
31479                 * @type Roo.SplitBar */
31480                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
31481                 this.split.on("moved", this.onSplitMove, this);
31482                 this.split.useShim = config.useShim === true;
31483                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
31484                 if(this.useSplitTips){
31485                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
31486                 }
31487                 if(config.collapsible){
31488                     this.split.el.on("dblclick", this.collapse,  this);
31489                 }
31490             }
31491             if(typeof config.minSize != "undefined"){
31492                 this.split.minSize = config.minSize;
31493             }
31494             if(typeof config.maxSize != "undefined"){
31495                 this.split.maxSize = config.maxSize;
31496             }
31497             if(config.hideWhenEmpty || config.hidden || config.collapsed){
31498                 this.hideSplitter();
31499             }
31500         }
31501     },
31502
31503     getHMaxSize : function(){
31504          var cmax = this.config.maxSize || 10000;
31505          var center = this.mgr.getRegion("center");
31506          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
31507     },
31508
31509     getVMaxSize : function(){
31510          var cmax = this.config.maxSize || 10000;
31511          var center = this.mgr.getRegion("center");
31512          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
31513     },
31514
31515     onSplitMove : function(split, newSize){
31516         this.fireEvent("resized", this, newSize);
31517     },
31518     
31519     /** 
31520      * Returns the {@link Roo.SplitBar} for this region.
31521      * @return {Roo.SplitBar}
31522      */
31523     getSplitBar : function(){
31524         return this.split;
31525     },
31526     
31527     hide : function(){
31528         this.hideSplitter();
31529         Roo.SplitLayoutRegion.superclass.hide.call(this);
31530     },
31531
31532     hideSplitter : function(){
31533         if(this.split){
31534             this.split.el.setLocation(-2000,-2000);
31535             this.split.el.hide();
31536         }
31537     },
31538
31539     show : function(){
31540         if(this.split){
31541             this.split.el.show();
31542         }
31543         Roo.SplitLayoutRegion.superclass.show.call(this);
31544     },
31545     
31546     beforeSlide: function(){
31547         if(Roo.isGecko){// firefox overflow auto bug workaround
31548             this.bodyEl.clip();
31549             if(this.tabs) this.tabs.bodyEl.clip();
31550             if(this.activePanel){
31551                 this.activePanel.getEl().clip();
31552                 
31553                 if(this.activePanel.beforeSlide){
31554                     this.activePanel.beforeSlide();
31555                 }
31556             }
31557         }
31558     },
31559     
31560     afterSlide : function(){
31561         if(Roo.isGecko){// firefox overflow auto bug workaround
31562             this.bodyEl.unclip();
31563             if(this.tabs) this.tabs.bodyEl.unclip();
31564             if(this.activePanel){
31565                 this.activePanel.getEl().unclip();
31566                 if(this.activePanel.afterSlide){
31567                     this.activePanel.afterSlide();
31568                 }
31569             }
31570         }
31571     },
31572
31573     initAutoHide : function(){
31574         if(this.autoHide !== false){
31575             if(!this.autoHideHd){
31576                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31577                 this.autoHideHd = {
31578                     "mouseout": function(e){
31579                         if(!e.within(this.el, true)){
31580                             st.delay(500);
31581                         }
31582                     },
31583                     "mouseover" : function(e){
31584                         st.cancel();
31585                     },
31586                     scope : this
31587                 };
31588             }
31589             this.el.on(this.autoHideHd);
31590         }
31591     },
31592
31593     clearAutoHide : function(){
31594         if(this.autoHide !== false){
31595             this.el.un("mouseout", this.autoHideHd.mouseout);
31596             this.el.un("mouseover", this.autoHideHd.mouseover);
31597         }
31598     },
31599
31600     clearMonitor : function(){
31601         Roo.get(document).un("click", this.slideInIf, this);
31602     },
31603
31604     // these names are backwards but not changed for compat
31605     slideOut : function(){
31606         if(this.isSlid || this.el.hasActiveFx()){
31607             return;
31608         }
31609         this.isSlid = true;
31610         if(this.collapseBtn){
31611             this.collapseBtn.hide();
31612         }
31613         this.closeBtnState = this.closeBtn.getStyle('display');
31614         this.closeBtn.hide();
31615         if(this.stickBtn){
31616             this.stickBtn.show();
31617         }
31618         this.el.show();
31619         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31620         this.beforeSlide();
31621         this.el.setStyle("z-index", 10001);
31622         this.el.slideIn(this.getSlideAnchor(), {
31623             callback: function(){
31624                 this.afterSlide();
31625                 this.initAutoHide();
31626                 Roo.get(document).on("click", this.slideInIf, this);
31627                 this.fireEvent("slideshow", this);
31628             },
31629             scope: this,
31630             block: true
31631         });
31632     },
31633
31634     afterSlideIn : function(){
31635         this.clearAutoHide();
31636         this.isSlid = false;
31637         this.clearMonitor();
31638         this.el.setStyle("z-index", "");
31639         if(this.collapseBtn){
31640             this.collapseBtn.show();
31641         }
31642         this.closeBtn.setStyle('display', this.closeBtnState);
31643         if(this.stickBtn){
31644             this.stickBtn.hide();
31645         }
31646         this.fireEvent("slidehide", this);
31647     },
31648
31649     slideIn : function(cb){
31650         if(!this.isSlid || this.el.hasActiveFx()){
31651             Roo.callback(cb);
31652             return;
31653         }
31654         this.isSlid = false;
31655         this.beforeSlide();
31656         this.el.slideOut(this.getSlideAnchor(), {
31657             callback: function(){
31658                 this.el.setLeftTop(-10000, -10000);
31659                 this.afterSlide();
31660                 this.afterSlideIn();
31661                 Roo.callback(cb);
31662             },
31663             scope: this,
31664             block: true
31665         });
31666     },
31667     
31668     slideInIf : function(e){
31669         if(!e.within(this.el)){
31670             this.slideIn();
31671         }
31672     },
31673
31674     animateCollapse : function(){
31675         this.beforeSlide();
31676         this.el.setStyle("z-index", 20000);
31677         var anchor = this.getSlideAnchor();
31678         this.el.slideOut(anchor, {
31679             callback : function(){
31680                 this.el.setStyle("z-index", "");
31681                 this.collapsedEl.slideIn(anchor, {duration:.3});
31682                 this.afterSlide();
31683                 this.el.setLocation(-10000,-10000);
31684                 this.el.hide();
31685                 this.fireEvent("collapsed", this);
31686             },
31687             scope: this,
31688             block: true
31689         });
31690     },
31691
31692     animateExpand : function(){
31693         this.beforeSlide();
31694         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31695         this.el.setStyle("z-index", 20000);
31696         this.collapsedEl.hide({
31697             duration:.1
31698         });
31699         this.el.slideIn(this.getSlideAnchor(), {
31700             callback : function(){
31701                 this.el.setStyle("z-index", "");
31702                 this.afterSlide();
31703                 if(this.split){
31704                     this.split.el.show();
31705                 }
31706                 this.fireEvent("invalidated", this);
31707                 this.fireEvent("expanded", this);
31708             },
31709             scope: this,
31710             block: true
31711         });
31712     },
31713
31714     anchors : {
31715         "west" : "left",
31716         "east" : "right",
31717         "north" : "top",
31718         "south" : "bottom"
31719     },
31720
31721     sanchors : {
31722         "west" : "l",
31723         "east" : "r",
31724         "north" : "t",
31725         "south" : "b"
31726     },
31727
31728     canchors : {
31729         "west" : "tl-tr",
31730         "east" : "tr-tl",
31731         "north" : "tl-bl",
31732         "south" : "bl-tl"
31733     },
31734
31735     getAnchor : function(){
31736         return this.anchors[this.position];
31737     },
31738
31739     getCollapseAnchor : function(){
31740         return this.canchors[this.position];
31741     },
31742
31743     getSlideAnchor : function(){
31744         return this.sanchors[this.position];
31745     },
31746
31747     getAlignAdj : function(){
31748         var cm = this.cmargins;
31749         switch(this.position){
31750             case "west":
31751                 return [0, 0];
31752             break;
31753             case "east":
31754                 return [0, 0];
31755             break;
31756             case "north":
31757                 return [0, 0];
31758             break;
31759             case "south":
31760                 return [0, 0];
31761             break;
31762         }
31763     },
31764
31765     getExpandAdj : function(){
31766         var c = this.collapsedEl, cm = this.cmargins;
31767         switch(this.position){
31768             case "west":
31769                 return [-(cm.right+c.getWidth()+cm.left), 0];
31770             break;
31771             case "east":
31772                 return [cm.right+c.getWidth()+cm.left, 0];
31773             break;
31774             case "north":
31775                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31776             break;
31777             case "south":
31778                 return [0, cm.top+cm.bottom+c.getHeight()];
31779             break;
31780         }
31781     }
31782 });/*
31783  * Based on:
31784  * Ext JS Library 1.1.1
31785  * Copyright(c) 2006-2007, Ext JS, LLC.
31786  *
31787  * Originally Released Under LGPL - original licence link has changed is not relivant.
31788  *
31789  * Fork - LGPL
31790  * <script type="text/javascript">
31791  */
31792 /*
31793  * These classes are private internal classes
31794  */
31795 Roo.CenterLayoutRegion = function(mgr, config){
31796     Roo.LayoutRegion.call(this, mgr, config, "center");
31797     this.visible = true;
31798     this.minWidth = config.minWidth || 20;
31799     this.minHeight = config.minHeight || 20;
31800 };
31801
31802 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31803     hide : function(){
31804         // center panel can't be hidden
31805     },
31806     
31807     show : function(){
31808         // center panel can't be hidden
31809     },
31810     
31811     getMinWidth: function(){
31812         return this.minWidth;
31813     },
31814     
31815     getMinHeight: function(){
31816         return this.minHeight;
31817     }
31818 });
31819
31820
31821 Roo.NorthLayoutRegion = function(mgr, config){
31822     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31823     if(this.split){
31824         this.split.placement = Roo.SplitBar.TOP;
31825         this.split.orientation = Roo.SplitBar.VERTICAL;
31826         this.split.el.addClass("x-layout-split-v");
31827     }
31828     var size = config.initialSize || config.height;
31829     if(typeof size != "undefined"){
31830         this.el.setHeight(size);
31831     }
31832 };
31833 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31834     orientation: Roo.SplitBar.VERTICAL,
31835     getBox : function(){
31836         if(this.collapsed){
31837             return this.collapsedEl.getBox();
31838         }
31839         var box = this.el.getBox();
31840         if(this.split){
31841             box.height += this.split.el.getHeight();
31842         }
31843         return box;
31844     },
31845     
31846     updateBox : function(box){
31847         if(this.split && !this.collapsed){
31848             box.height -= this.split.el.getHeight();
31849             this.split.el.setLeft(box.x);
31850             this.split.el.setTop(box.y+box.height);
31851             this.split.el.setWidth(box.width);
31852         }
31853         if(this.collapsed){
31854             this.updateBody(box.width, null);
31855         }
31856         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31857     }
31858 });
31859
31860 Roo.SouthLayoutRegion = function(mgr, config){
31861     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31862     if(this.split){
31863         this.split.placement = Roo.SplitBar.BOTTOM;
31864         this.split.orientation = Roo.SplitBar.VERTICAL;
31865         this.split.el.addClass("x-layout-split-v");
31866     }
31867     var size = config.initialSize || config.height;
31868     if(typeof size != "undefined"){
31869         this.el.setHeight(size);
31870     }
31871 };
31872 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31873     orientation: Roo.SplitBar.VERTICAL,
31874     getBox : function(){
31875         if(this.collapsed){
31876             return this.collapsedEl.getBox();
31877         }
31878         var box = this.el.getBox();
31879         if(this.split){
31880             var sh = this.split.el.getHeight();
31881             box.height += sh;
31882             box.y -= sh;
31883         }
31884         return box;
31885     },
31886     
31887     updateBox : function(box){
31888         if(this.split && !this.collapsed){
31889             var sh = this.split.el.getHeight();
31890             box.height -= sh;
31891             box.y += sh;
31892             this.split.el.setLeft(box.x);
31893             this.split.el.setTop(box.y-sh);
31894             this.split.el.setWidth(box.width);
31895         }
31896         if(this.collapsed){
31897             this.updateBody(box.width, null);
31898         }
31899         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31900     }
31901 });
31902
31903 Roo.EastLayoutRegion = function(mgr, config){
31904     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31905     if(this.split){
31906         this.split.placement = Roo.SplitBar.RIGHT;
31907         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31908         this.split.el.addClass("x-layout-split-h");
31909     }
31910     var size = config.initialSize || config.width;
31911     if(typeof size != "undefined"){
31912         this.el.setWidth(size);
31913     }
31914 };
31915 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31916     orientation: Roo.SplitBar.HORIZONTAL,
31917     getBox : function(){
31918         if(this.collapsed){
31919             return this.collapsedEl.getBox();
31920         }
31921         var box = this.el.getBox();
31922         if(this.split){
31923             var sw = this.split.el.getWidth();
31924             box.width += sw;
31925             box.x -= sw;
31926         }
31927         return box;
31928     },
31929
31930     updateBox : function(box){
31931         if(this.split && !this.collapsed){
31932             var sw = this.split.el.getWidth();
31933             box.width -= sw;
31934             this.split.el.setLeft(box.x);
31935             this.split.el.setTop(box.y);
31936             this.split.el.setHeight(box.height);
31937             box.x += sw;
31938         }
31939         if(this.collapsed){
31940             this.updateBody(null, box.height);
31941         }
31942         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31943     }
31944 });
31945
31946 Roo.WestLayoutRegion = function(mgr, config){
31947     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31948     if(this.split){
31949         this.split.placement = Roo.SplitBar.LEFT;
31950         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31951         this.split.el.addClass("x-layout-split-h");
31952     }
31953     var size = config.initialSize || config.width;
31954     if(typeof size != "undefined"){
31955         this.el.setWidth(size);
31956     }
31957 };
31958 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31959     orientation: Roo.SplitBar.HORIZONTAL,
31960     getBox : function(){
31961         if(this.collapsed){
31962             return this.collapsedEl.getBox();
31963         }
31964         var box = this.el.getBox();
31965         if(this.split){
31966             box.width += this.split.el.getWidth();
31967         }
31968         return box;
31969     },
31970     
31971     updateBox : function(box){
31972         if(this.split && !this.collapsed){
31973             var sw = this.split.el.getWidth();
31974             box.width -= sw;
31975             this.split.el.setLeft(box.x+box.width);
31976             this.split.el.setTop(box.y);
31977             this.split.el.setHeight(box.height);
31978         }
31979         if(this.collapsed){
31980             this.updateBody(null, box.height);
31981         }
31982         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31983     }
31984 });
31985 /*
31986  * Based on:
31987  * Ext JS Library 1.1.1
31988  * Copyright(c) 2006-2007, Ext JS, LLC.
31989  *
31990  * Originally Released Under LGPL - original licence link has changed is not relivant.
31991  *
31992  * Fork - LGPL
31993  * <script type="text/javascript">
31994  */
31995  
31996  
31997 /*
31998  * Private internal class for reading and applying state
31999  */
32000 Roo.LayoutStateManager = function(layout){
32001      // default empty state
32002      this.state = {
32003         north: {},
32004         south: {},
32005         east: {},
32006         west: {}       
32007     };
32008 };
32009
32010 Roo.LayoutStateManager.prototype = {
32011     init : function(layout, provider){
32012         this.provider = provider;
32013         var state = provider.get(layout.id+"-layout-state");
32014         if(state){
32015             var wasUpdating = layout.isUpdating();
32016             if(!wasUpdating){
32017                 layout.beginUpdate();
32018             }
32019             for(var key in state){
32020                 if(typeof state[key] != "function"){
32021                     var rstate = state[key];
32022                     var r = layout.getRegion(key);
32023                     if(r && rstate){
32024                         if(rstate.size){
32025                             r.resizeTo(rstate.size);
32026                         }
32027                         if(rstate.collapsed == true){
32028                             r.collapse(true);
32029                         }else{
32030                             r.expand(null, true);
32031                         }
32032                     }
32033                 }
32034             }
32035             if(!wasUpdating){
32036                 layout.endUpdate();
32037             }
32038             this.state = state; 
32039         }
32040         this.layout = layout;
32041         layout.on("regionresized", this.onRegionResized, this);
32042         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32043         layout.on("regionexpanded", this.onRegionExpanded, this);
32044     },
32045     
32046     storeState : function(){
32047         this.provider.set(this.layout.id+"-layout-state", this.state);
32048     },
32049     
32050     onRegionResized : function(region, newSize){
32051         this.state[region.getPosition()].size = newSize;
32052         this.storeState();
32053     },
32054     
32055     onRegionCollapsed : function(region){
32056         this.state[region.getPosition()].collapsed = true;
32057         this.storeState();
32058     },
32059     
32060     onRegionExpanded : function(region){
32061         this.state[region.getPosition()].collapsed = false;
32062         this.storeState();
32063     }
32064 };/*
32065  * Based on:
32066  * Ext JS Library 1.1.1
32067  * Copyright(c) 2006-2007, Ext JS, LLC.
32068  *
32069  * Originally Released Under LGPL - original licence link has changed is not relivant.
32070  *
32071  * Fork - LGPL
32072  * <script type="text/javascript">
32073  */
32074 /**
32075  * @class Roo.ContentPanel
32076  * @extends Roo.util.Observable
32077  * A basic ContentPanel element.
32078  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32079  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32080  * @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
32081  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32082  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32083  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32084  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32085  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32086  * @cfg {String} title          The title for this panel
32087  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32088  * @cfg {String} url            Calls {@link #setUrl} with this value
32089  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32090  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32091  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32092  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32093
32094  * @constructor
32095  * Create a new ContentPanel.
32096  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32097  * @param {String/Object} config A string to set only the title or a config object
32098  * @param {String} content (optional) Set the HTML content for this panel
32099  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32100  */
32101 Roo.ContentPanel = function(el, config, content){
32102     
32103      
32104     /*
32105     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32106         config = el;
32107         el = Roo.id();
32108     }
32109     if (config && config.parentLayout) { 
32110         el = config.parentLayout.el.createChild(); 
32111     }
32112     */
32113     if(el.autoCreate){ // xtype is available if this is called from factory
32114         config = el;
32115         el = Roo.id();
32116     }
32117     this.el = Roo.get(el);
32118     if(!this.el && config && config.autoCreate){
32119         if(typeof config.autoCreate == "object"){
32120             if(!config.autoCreate.id){
32121                 config.autoCreate.id = config.id||el;
32122             }
32123             this.el = Roo.DomHelper.append(document.body,
32124                         config.autoCreate, true);
32125         }else{
32126             this.el = Roo.DomHelper.append(document.body,
32127                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32128         }
32129     }
32130     this.closable = false;
32131     this.loaded = false;
32132     this.active = false;
32133     if(typeof config == "string"){
32134         this.title = config;
32135     }else{
32136         Roo.apply(this, config);
32137     }
32138     
32139     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32140         this.wrapEl = this.el.wrap();
32141         this.toolbar.container = this.el.insertSibling(false, 'before');
32142         this.toolbar = new Roo.Toolbar(this.toolbar);
32143     }
32144     
32145     
32146     
32147     if(this.resizeEl){
32148         this.resizeEl = Roo.get(this.resizeEl, true);
32149     }else{
32150         this.resizeEl = this.el;
32151     }
32152     this.addEvents({
32153         /**
32154          * @event activate
32155          * Fires when this panel is activated. 
32156          * @param {Roo.ContentPanel} this
32157          */
32158         "activate" : true,
32159         /**
32160          * @event deactivate
32161          * Fires when this panel is activated. 
32162          * @param {Roo.ContentPanel} this
32163          */
32164         "deactivate" : true,
32165
32166         /**
32167          * @event resize
32168          * Fires when this panel is resized if fitToFrame is true.
32169          * @param {Roo.ContentPanel} this
32170          * @param {Number} width The width after any component adjustments
32171          * @param {Number} height The height after any component adjustments
32172          */
32173         "resize" : true,
32174         
32175          /**
32176          * @event render
32177          * Fires when this tab is created
32178          * @param {Roo.ContentPanel} this
32179          */
32180         "render" : true
32181         
32182         
32183         
32184     });
32185     if(this.autoScroll){
32186         this.resizeEl.setStyle("overflow", "auto");
32187     } else {
32188         // fix randome scrolling
32189         this.el.on('scroll', function() {
32190             Roo.log('fix random scolling');
32191             this.scrollTo('top',0); 
32192         });
32193     }
32194     content = content || this.content;
32195     if(content){
32196         this.setContent(content);
32197     }
32198     if(config && config.url){
32199         this.setUrl(this.url, this.params, this.loadOnce);
32200     }
32201     
32202     
32203     
32204     Roo.ContentPanel.superclass.constructor.call(this);
32205     
32206     this.fireEvent('render', this);
32207 };
32208
32209 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32210     tabTip:'',
32211     setRegion : function(region){
32212         this.region = region;
32213         if(region){
32214            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32215         }else{
32216            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32217         } 
32218     },
32219     
32220     /**
32221      * Returns the toolbar for this Panel if one was configured. 
32222      * @return {Roo.Toolbar} 
32223      */
32224     getToolbar : function(){
32225         return this.toolbar;
32226     },
32227     
32228     setActiveState : function(active){
32229         this.active = active;
32230         if(!active){
32231             this.fireEvent("deactivate", this);
32232         }else{
32233             this.fireEvent("activate", this);
32234         }
32235     },
32236     /**
32237      * Updates this panel's element
32238      * @param {String} content The new content
32239      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32240     */
32241     setContent : function(content, loadScripts){
32242         this.el.update(content, loadScripts);
32243     },
32244
32245     ignoreResize : function(w, h){
32246         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32247             return true;
32248         }else{
32249             this.lastSize = {width: w, height: h};
32250             return false;
32251         }
32252     },
32253     /**
32254      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32255      * @return {Roo.UpdateManager} The UpdateManager
32256      */
32257     getUpdateManager : function(){
32258         return this.el.getUpdateManager();
32259     },
32260      /**
32261      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32262      * @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:
32263 <pre><code>
32264 panel.load({
32265     url: "your-url.php",
32266     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32267     callback: yourFunction,
32268     scope: yourObject, //(optional scope)
32269     discardUrl: false,
32270     nocache: false,
32271     text: "Loading...",
32272     timeout: 30,
32273     scripts: false
32274 });
32275 </code></pre>
32276      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32277      * 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.
32278      * @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}
32279      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32280      * @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.
32281      * @return {Roo.ContentPanel} this
32282      */
32283     load : function(){
32284         var um = this.el.getUpdateManager();
32285         um.update.apply(um, arguments);
32286         return this;
32287     },
32288
32289
32290     /**
32291      * 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.
32292      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32293      * @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)
32294      * @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)
32295      * @return {Roo.UpdateManager} The UpdateManager
32296      */
32297     setUrl : function(url, params, loadOnce){
32298         if(this.refreshDelegate){
32299             this.removeListener("activate", this.refreshDelegate);
32300         }
32301         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32302         this.on("activate", this.refreshDelegate);
32303         return this.el.getUpdateManager();
32304     },
32305     
32306     _handleRefresh : function(url, params, loadOnce){
32307         if(!loadOnce || !this.loaded){
32308             var updater = this.el.getUpdateManager();
32309             updater.update(url, params, this._setLoaded.createDelegate(this));
32310         }
32311     },
32312     
32313     _setLoaded : function(){
32314         this.loaded = true;
32315     }, 
32316     
32317     /**
32318      * Returns this panel's id
32319      * @return {String} 
32320      */
32321     getId : function(){
32322         return this.el.id;
32323     },
32324     
32325     /** 
32326      * Returns this panel's element - used by regiosn to add.
32327      * @return {Roo.Element} 
32328      */
32329     getEl : function(){
32330         return this.wrapEl || this.el;
32331     },
32332     
32333     adjustForComponents : function(width, height){
32334         if(this.resizeEl != this.el){
32335             width -= this.el.getFrameWidth('lr');
32336             height -= this.el.getFrameWidth('tb');
32337         }
32338         if(this.toolbar){
32339             var te = this.toolbar.getEl();
32340             height -= te.getHeight();
32341             te.setWidth(width);
32342         }
32343         if(this.adjustments){
32344             width += this.adjustments[0];
32345             height += this.adjustments[1];
32346         }
32347         return {"width": width, "height": height};
32348     },
32349     
32350     setSize : function(width, height){
32351         if(this.fitToFrame && !this.ignoreResize(width, height)){
32352             if(this.fitContainer && this.resizeEl != this.el){
32353                 this.el.setSize(width, height);
32354             }
32355             var size = this.adjustForComponents(width, height);
32356             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32357             this.fireEvent('resize', this, size.width, size.height);
32358         }
32359     },
32360     
32361     /**
32362      * Returns this panel's title
32363      * @return {String} 
32364      */
32365     getTitle : function(){
32366         return this.title;
32367     },
32368     
32369     /**
32370      * Set this panel's title
32371      * @param {String} title
32372      */
32373     setTitle : function(title){
32374         this.title = title;
32375         if(this.region){
32376             this.region.updatePanelTitle(this, title);
32377         }
32378     },
32379     
32380     /**
32381      * Returns true is this panel was configured to be closable
32382      * @return {Boolean} 
32383      */
32384     isClosable : function(){
32385         return this.closable;
32386     },
32387     
32388     beforeSlide : function(){
32389         this.el.clip();
32390         this.resizeEl.clip();
32391     },
32392     
32393     afterSlide : function(){
32394         this.el.unclip();
32395         this.resizeEl.unclip();
32396     },
32397     
32398     /**
32399      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
32400      *   Will fail silently if the {@link #setUrl} method has not been called.
32401      *   This does not activate the panel, just updates its content.
32402      */
32403     refresh : function(){
32404         if(this.refreshDelegate){
32405            this.loaded = false;
32406            this.refreshDelegate();
32407         }
32408     },
32409     
32410     /**
32411      * Destroys this panel
32412      */
32413     destroy : function(){
32414         this.el.removeAllListeners();
32415         var tempEl = document.createElement("span");
32416         tempEl.appendChild(this.el.dom);
32417         tempEl.innerHTML = "";
32418         this.el.remove();
32419         this.el = null;
32420     },
32421     
32422     /**
32423      * form - if the content panel contains a form - this is a reference to it.
32424      * @type {Roo.form.Form}
32425      */
32426     form : false,
32427     /**
32428      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
32429      *    This contains a reference to it.
32430      * @type {Roo.View}
32431      */
32432     view : false,
32433     
32434       /**
32435      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
32436      * <pre><code>
32437
32438 layout.addxtype({
32439        xtype : 'Form',
32440        items: [ .... ]
32441    }
32442 );
32443
32444 </code></pre>
32445      * @param {Object} cfg Xtype definition of item to add.
32446      */
32447     
32448     addxtype : function(cfg) {
32449         // add form..
32450         if (cfg.xtype.match(/^Form$/)) {
32451             var el = this.el.createChild();
32452
32453             this.form = new  Roo.form.Form(cfg);
32454             
32455             
32456             if ( this.form.allItems.length) this.form.render(el.dom);
32457             return this.form;
32458         }
32459         // should only have one of theses..
32460         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
32461             // views..
32462             cfg.el = this.el.appendChild(document.createElement("div"));
32463             // factory?
32464             
32465             var ret = new Roo.factory(cfg);
32466             ret.render && ret.render(false, ''); // render blank..
32467             this.view = ret;
32468             return ret;
32469         }
32470         return false;
32471     }
32472 });
32473
32474 /**
32475  * @class Roo.GridPanel
32476  * @extends Roo.ContentPanel
32477  * @constructor
32478  * Create a new GridPanel.
32479  * @param {Roo.grid.Grid} grid The grid for this panel
32480  * @param {String/Object} config A string to set only the panel's title, or a config object
32481  */
32482 Roo.GridPanel = function(grid, config){
32483     
32484   
32485     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32486         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32487         
32488     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32489     
32490     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32491     
32492     if(this.toolbar){
32493         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32494     }
32495     // xtype created footer. - not sure if will work as we normally have to render first..
32496     if (this.footer && !this.footer.el && this.footer.xtype) {
32497         
32498         this.footer.container = this.grid.getView().getFooterPanel(true);
32499         this.footer.dataSource = this.grid.dataSource;
32500         this.footer = Roo.factory(this.footer, Roo);
32501         
32502     }
32503     
32504     grid.monitorWindowResize = false; // turn off autosizing
32505     grid.autoHeight = false;
32506     grid.autoWidth = false;
32507     this.grid = grid;
32508     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32509 };
32510
32511 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32512     getId : function(){
32513         return this.grid.id;
32514     },
32515     
32516     /**
32517      * Returns the grid for this panel
32518      * @return {Roo.grid.Grid} 
32519      */
32520     getGrid : function(){
32521         return this.grid;    
32522     },
32523     
32524     setSize : function(width, height){
32525         if(!this.ignoreResize(width, height)){
32526             var grid = this.grid;
32527             var size = this.adjustForComponents(width, height);
32528             grid.getGridEl().setSize(size.width, size.height);
32529             grid.autoSize();
32530         }
32531     },
32532     
32533     beforeSlide : function(){
32534         this.grid.getView().scroller.clip();
32535     },
32536     
32537     afterSlide : function(){
32538         this.grid.getView().scroller.unclip();
32539     },
32540     
32541     destroy : function(){
32542         this.grid.destroy();
32543         delete this.grid;
32544         Roo.GridPanel.superclass.destroy.call(this); 
32545     }
32546 });
32547
32548
32549 /**
32550  * @class Roo.NestedLayoutPanel
32551  * @extends Roo.ContentPanel
32552  * @constructor
32553  * Create a new NestedLayoutPanel.
32554  * 
32555  * 
32556  * @param {Roo.BorderLayout} layout The layout for this panel
32557  * @param {String/Object} config A string to set only the title or a config object
32558  */
32559 Roo.NestedLayoutPanel = function(layout, config)
32560 {
32561     // construct with only one argument..
32562     /* FIXME - implement nicer consturctors
32563     if (layout.layout) {
32564         config = layout;
32565         layout = config.layout;
32566         delete config.layout;
32567     }
32568     if (layout.xtype && !layout.getEl) {
32569         // then layout needs constructing..
32570         layout = Roo.factory(layout, Roo);
32571     }
32572     */
32573     
32574     
32575     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32576     
32577     layout.monitorWindowResize = false; // turn off autosizing
32578     this.layout = layout;
32579     this.layout.getEl().addClass("x-layout-nested-layout");
32580     
32581     
32582     
32583     
32584 };
32585
32586 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32587
32588     setSize : function(width, height){
32589         if(!this.ignoreResize(width, height)){
32590             var size = this.adjustForComponents(width, height);
32591             var el = this.layout.getEl();
32592             el.setSize(size.width, size.height);
32593             var touch = el.dom.offsetWidth;
32594             this.layout.layout();
32595             // ie requires a double layout on the first pass
32596             if(Roo.isIE && !this.initialized){
32597                 this.initialized = true;
32598                 this.layout.layout();
32599             }
32600         }
32601     },
32602     
32603     // activate all subpanels if not currently active..
32604     
32605     setActiveState : function(active){
32606         this.active = active;
32607         if(!active){
32608             this.fireEvent("deactivate", this);
32609             return;
32610         }
32611         
32612         this.fireEvent("activate", this);
32613         // not sure if this should happen before or after..
32614         if (!this.layout) {
32615             return; // should not happen..
32616         }
32617         var reg = false;
32618         for (var r in this.layout.regions) {
32619             reg = this.layout.getRegion(r);
32620             if (reg.getActivePanel()) {
32621                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32622                 reg.setActivePanel(reg.getActivePanel());
32623                 continue;
32624             }
32625             if (!reg.panels.length) {
32626                 continue;
32627             }
32628             reg.showPanel(reg.getPanel(0));
32629         }
32630         
32631         
32632         
32633         
32634     },
32635     
32636     /**
32637      * Returns the nested BorderLayout for this panel
32638      * @return {Roo.BorderLayout} 
32639      */
32640     getLayout : function(){
32641         return this.layout;
32642     },
32643     
32644      /**
32645      * Adds a xtype elements to the layout of the nested panel
32646      * <pre><code>
32647
32648 panel.addxtype({
32649        xtype : 'ContentPanel',
32650        region: 'west',
32651        items: [ .... ]
32652    }
32653 );
32654
32655 panel.addxtype({
32656         xtype : 'NestedLayoutPanel',
32657         region: 'west',
32658         layout: {
32659            center: { },
32660            west: { }   
32661         },
32662         items : [ ... list of content panels or nested layout panels.. ]
32663    }
32664 );
32665 </code></pre>
32666      * @param {Object} cfg Xtype definition of item to add.
32667      */
32668     addxtype : function(cfg) {
32669         return this.layout.addxtype(cfg);
32670     
32671     }
32672 });
32673
32674 Roo.ScrollPanel = function(el, config, content){
32675     config = config || {};
32676     config.fitToFrame = true;
32677     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32678     
32679     this.el.dom.style.overflow = "hidden";
32680     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32681     this.el.removeClass("x-layout-inactive-content");
32682     this.el.on("mousewheel", this.onWheel, this);
32683
32684     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32685     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32686     up.unselectable(); down.unselectable();
32687     up.on("click", this.scrollUp, this);
32688     down.on("click", this.scrollDown, this);
32689     up.addClassOnOver("x-scroller-btn-over");
32690     down.addClassOnOver("x-scroller-btn-over");
32691     up.addClassOnClick("x-scroller-btn-click");
32692     down.addClassOnClick("x-scroller-btn-click");
32693     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32694
32695     this.resizeEl = this.el;
32696     this.el = wrap; this.up = up; this.down = down;
32697 };
32698
32699 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32700     increment : 100,
32701     wheelIncrement : 5,
32702     scrollUp : function(){
32703         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32704     },
32705
32706     scrollDown : function(){
32707         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32708     },
32709
32710     afterScroll : function(){
32711         var el = this.resizeEl;
32712         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32713         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32714         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32715     },
32716
32717     setSize : function(){
32718         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32719         this.afterScroll();
32720     },
32721
32722     onWheel : function(e){
32723         var d = e.getWheelDelta();
32724         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32725         this.afterScroll();
32726         e.stopEvent();
32727     },
32728
32729     setContent : function(content, loadScripts){
32730         this.resizeEl.update(content, loadScripts);
32731     }
32732
32733 });
32734
32735
32736
32737
32738
32739
32740
32741
32742
32743 /**
32744  * @class Roo.TreePanel
32745  * @extends Roo.ContentPanel
32746  * @constructor
32747  * Create a new TreePanel. - defaults to fit/scoll contents.
32748  * @param {String/Object} config A string to set only the panel's title, or a config object
32749  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32750  */
32751 Roo.TreePanel = function(config){
32752     var el = config.el;
32753     var tree = config.tree;
32754     delete config.tree; 
32755     delete config.el; // hopefull!
32756     
32757     // wrapper for IE7 strict & safari scroll issue
32758     
32759     var treeEl = el.createChild();
32760     config.resizeEl = treeEl;
32761     
32762     
32763     
32764     Roo.TreePanel.superclass.constructor.call(this, el, config);
32765  
32766  
32767     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32768     //console.log(tree);
32769     this.on('activate', function()
32770     {
32771         if (this.tree.rendered) {
32772             return;
32773         }
32774         //console.log('render tree');
32775         this.tree.render();
32776     });
32777     
32778     this.on('resize',  function (cp, w, h) {
32779             this.tree.innerCt.setWidth(w);
32780             this.tree.innerCt.setHeight(h);
32781             this.tree.innerCt.setStyle('overflow-y', 'auto');
32782     });
32783
32784         
32785     
32786 };
32787
32788 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32789     fitToFrame : true,
32790     autoScroll : true
32791 });
32792
32793
32794
32795
32796
32797
32798
32799
32800
32801
32802
32803 /*
32804  * Based on:
32805  * Ext JS Library 1.1.1
32806  * Copyright(c) 2006-2007, Ext JS, LLC.
32807  *
32808  * Originally Released Under LGPL - original licence link has changed is not relivant.
32809  *
32810  * Fork - LGPL
32811  * <script type="text/javascript">
32812  */
32813  
32814
32815 /**
32816  * @class Roo.ReaderLayout
32817  * @extends Roo.BorderLayout
32818  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32819  * center region containing two nested regions (a top one for a list view and one for item preview below),
32820  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32821  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32822  * expedites the setup of the overall layout and regions for this common application style.
32823  * Example:
32824  <pre><code>
32825 var reader = new Roo.ReaderLayout();
32826 var CP = Roo.ContentPanel;  // shortcut for adding
32827
32828 reader.beginUpdate();
32829 reader.add("north", new CP("north", "North"));
32830 reader.add("west", new CP("west", {title: "West"}));
32831 reader.add("east", new CP("east", {title: "East"}));
32832
32833 reader.regions.listView.add(new CP("listView", "List"));
32834 reader.regions.preview.add(new CP("preview", "Preview"));
32835 reader.endUpdate();
32836 </code></pre>
32837 * @constructor
32838 * Create a new ReaderLayout
32839 * @param {Object} config Configuration options
32840 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32841 * document.body if omitted)
32842 */
32843 Roo.ReaderLayout = function(config, renderTo){
32844     var c = config || {size:{}};
32845     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32846         north: c.north !== false ? Roo.apply({
32847             split:false,
32848             initialSize: 32,
32849             titlebar: false
32850         }, c.north) : false,
32851         west: c.west !== false ? Roo.apply({
32852             split:true,
32853             initialSize: 200,
32854             minSize: 175,
32855             maxSize: 400,
32856             titlebar: true,
32857             collapsible: true,
32858             animate: true,
32859             margins:{left:5,right:0,bottom:5,top:5},
32860             cmargins:{left:5,right:5,bottom:5,top:5}
32861         }, c.west) : false,
32862         east: c.east !== false ? Roo.apply({
32863             split:true,
32864             initialSize: 200,
32865             minSize: 175,
32866             maxSize: 400,
32867             titlebar: true,
32868             collapsible: true,
32869             animate: true,
32870             margins:{left:0,right:5,bottom:5,top:5},
32871             cmargins:{left:5,right:5,bottom:5,top:5}
32872         }, c.east) : false,
32873         center: Roo.apply({
32874             tabPosition: 'top',
32875             autoScroll:false,
32876             closeOnTab: true,
32877             titlebar:false,
32878             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32879         }, c.center)
32880     });
32881
32882     this.el.addClass('x-reader');
32883
32884     this.beginUpdate();
32885
32886     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32887         south: c.preview !== false ? Roo.apply({
32888             split:true,
32889             initialSize: 200,
32890             minSize: 100,
32891             autoScroll:true,
32892             collapsible:true,
32893             titlebar: true,
32894             cmargins:{top:5,left:0, right:0, bottom:0}
32895         }, c.preview) : false,
32896         center: Roo.apply({
32897             autoScroll:false,
32898             titlebar:false,
32899             minHeight:200
32900         }, c.listView)
32901     });
32902     this.add('center', new Roo.NestedLayoutPanel(inner,
32903             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32904
32905     this.endUpdate();
32906
32907     this.regions.preview = inner.getRegion('south');
32908     this.regions.listView = inner.getRegion('center');
32909 };
32910
32911 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32912  * Based on:
32913  * Ext JS Library 1.1.1
32914  * Copyright(c) 2006-2007, Ext JS, LLC.
32915  *
32916  * Originally Released Under LGPL - original licence link has changed is not relivant.
32917  *
32918  * Fork - LGPL
32919  * <script type="text/javascript">
32920  */
32921  
32922 /**
32923  * @class Roo.grid.Grid
32924  * @extends Roo.util.Observable
32925  * This class represents the primary interface of a component based grid control.
32926  * <br><br>Usage:<pre><code>
32927  var grid = new Roo.grid.Grid("my-container-id", {
32928      ds: myDataStore,
32929      cm: myColModel,
32930      selModel: mySelectionModel,
32931      autoSizeColumns: true,
32932      monitorWindowResize: false,
32933      trackMouseOver: true
32934  });
32935  // set any options
32936  grid.render();
32937  * </code></pre>
32938  * <b>Common Problems:</b><br/>
32939  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32940  * element will correct this<br/>
32941  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32942  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32943  * are unpredictable.<br/>
32944  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32945  * grid to calculate dimensions/offsets.<br/>
32946   * @constructor
32947  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32948  * The container MUST have some type of size defined for the grid to fill. The container will be
32949  * automatically set to position relative if it isn't already.
32950  * @param {Object} config A config object that sets properties on this grid.
32951  */
32952 Roo.grid.Grid = function(container, config){
32953         // initialize the container
32954         this.container = Roo.get(container);
32955         this.container.update("");
32956         this.container.setStyle("overflow", "hidden");
32957     this.container.addClass('x-grid-container');
32958
32959     this.id = this.container.id;
32960
32961     Roo.apply(this, config);
32962     // check and correct shorthanded configs
32963     if(this.ds){
32964         this.dataSource = this.ds;
32965         delete this.ds;
32966     }
32967     if(this.cm){
32968         this.colModel = this.cm;
32969         delete this.cm;
32970     }
32971     if(this.sm){
32972         this.selModel = this.sm;
32973         delete this.sm;
32974     }
32975
32976     if (this.selModel) {
32977         this.selModel = Roo.factory(this.selModel, Roo.grid);
32978         this.sm = this.selModel;
32979         this.sm.xmodule = this.xmodule || false;
32980     }
32981     if (typeof(this.colModel.config) == 'undefined') {
32982         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32983         this.cm = this.colModel;
32984         this.cm.xmodule = this.xmodule || false;
32985     }
32986     if (this.dataSource) {
32987         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32988         this.ds = this.dataSource;
32989         this.ds.xmodule = this.xmodule || false;
32990          
32991     }
32992     
32993     
32994     
32995     if(this.width){
32996         this.container.setWidth(this.width);
32997     }
32998
32999     if(this.height){
33000         this.container.setHeight(this.height);
33001     }
33002     /** @private */
33003         this.addEvents({
33004         // raw events
33005         /**
33006          * @event click
33007          * The raw click event for the entire grid.
33008          * @param {Roo.EventObject} e
33009          */
33010         "click" : true,
33011         /**
33012          * @event dblclick
33013          * The raw dblclick event for the entire grid.
33014          * @param {Roo.EventObject} e
33015          */
33016         "dblclick" : true,
33017         /**
33018          * @event contextmenu
33019          * The raw contextmenu event for the entire grid.
33020          * @param {Roo.EventObject} e
33021          */
33022         "contextmenu" : true,
33023         /**
33024          * @event mousedown
33025          * The raw mousedown event for the entire grid.
33026          * @param {Roo.EventObject} e
33027          */
33028         "mousedown" : true,
33029         /**
33030          * @event mouseup
33031          * The raw mouseup event for the entire grid.
33032          * @param {Roo.EventObject} e
33033          */
33034         "mouseup" : true,
33035         /**
33036          * @event mouseover
33037          * The raw mouseover event for the entire grid.
33038          * @param {Roo.EventObject} e
33039          */
33040         "mouseover" : true,
33041         /**
33042          * @event mouseout
33043          * The raw mouseout event for the entire grid.
33044          * @param {Roo.EventObject} e
33045          */
33046         "mouseout" : true,
33047         /**
33048          * @event keypress
33049          * The raw keypress event for the entire grid.
33050          * @param {Roo.EventObject} e
33051          */
33052         "keypress" : true,
33053         /**
33054          * @event keydown
33055          * The raw keydown event for the entire grid.
33056          * @param {Roo.EventObject} e
33057          */
33058         "keydown" : true,
33059
33060         // custom events
33061
33062         /**
33063          * @event cellclick
33064          * Fires when a cell is clicked
33065          * @param {Grid} this
33066          * @param {Number} rowIndex
33067          * @param {Number} columnIndex
33068          * @param {Roo.EventObject} e
33069          */
33070         "cellclick" : true,
33071         /**
33072          * @event celldblclick
33073          * Fires when a cell is double clicked
33074          * @param {Grid} this
33075          * @param {Number} rowIndex
33076          * @param {Number} columnIndex
33077          * @param {Roo.EventObject} e
33078          */
33079         "celldblclick" : true,
33080         /**
33081          * @event rowclick
33082          * Fires when a row is clicked
33083          * @param {Grid} this
33084          * @param {Number} rowIndex
33085          * @param {Roo.EventObject} e
33086          */
33087         "rowclick" : true,
33088         /**
33089          * @event rowdblclick
33090          * Fires when a row is double clicked
33091          * @param {Grid} this
33092          * @param {Number} rowIndex
33093          * @param {Roo.EventObject} e
33094          */
33095         "rowdblclick" : true,
33096         /**
33097          * @event headerclick
33098          * Fires when a header is clicked
33099          * @param {Grid} this
33100          * @param {Number} columnIndex
33101          * @param {Roo.EventObject} e
33102          */
33103         "headerclick" : true,
33104         /**
33105          * @event headerdblclick
33106          * Fires when a header cell is double clicked
33107          * @param {Grid} this
33108          * @param {Number} columnIndex
33109          * @param {Roo.EventObject} e
33110          */
33111         "headerdblclick" : true,
33112         /**
33113          * @event rowcontextmenu
33114          * Fires when a row is right clicked
33115          * @param {Grid} this
33116          * @param {Number} rowIndex
33117          * @param {Roo.EventObject} e
33118          */
33119         "rowcontextmenu" : true,
33120         /**
33121          * @event cellcontextmenu
33122          * Fires when a cell is right clicked
33123          * @param {Grid} this
33124          * @param {Number} rowIndex
33125          * @param {Number} cellIndex
33126          * @param {Roo.EventObject} e
33127          */
33128          "cellcontextmenu" : true,
33129         /**
33130          * @event headercontextmenu
33131          * Fires when a header is right clicked
33132          * @param {Grid} this
33133          * @param {Number} columnIndex
33134          * @param {Roo.EventObject} e
33135          */
33136         "headercontextmenu" : true,
33137         /**
33138          * @event bodyscroll
33139          * Fires when the body element is scrolled
33140          * @param {Number} scrollLeft
33141          * @param {Number} scrollTop
33142          */
33143         "bodyscroll" : true,
33144         /**
33145          * @event columnresize
33146          * Fires when the user resizes a column
33147          * @param {Number} columnIndex
33148          * @param {Number} newSize
33149          */
33150         "columnresize" : true,
33151         /**
33152          * @event columnmove
33153          * Fires when the user moves a column
33154          * @param {Number} oldIndex
33155          * @param {Number} newIndex
33156          */
33157         "columnmove" : true,
33158         /**
33159          * @event startdrag
33160          * Fires when row(s) start being dragged
33161          * @param {Grid} this
33162          * @param {Roo.GridDD} dd The drag drop object
33163          * @param {event} e The raw browser event
33164          */
33165         "startdrag" : true,
33166         /**
33167          * @event enddrag
33168          * Fires when a drag operation is complete
33169          * @param {Grid} this
33170          * @param {Roo.GridDD} dd The drag drop object
33171          * @param {event} e The raw browser event
33172          */
33173         "enddrag" : true,
33174         /**
33175          * @event dragdrop
33176          * Fires when dragged row(s) are dropped on a valid DD target
33177          * @param {Grid} this
33178          * @param {Roo.GridDD} dd The drag drop object
33179          * @param {String} targetId The target drag drop object
33180          * @param {event} e The raw browser event
33181          */
33182         "dragdrop" : true,
33183         /**
33184          * @event dragover
33185          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33186          * @param {Grid} this
33187          * @param {Roo.GridDD} dd The drag drop object
33188          * @param {String} targetId The target drag drop object
33189          * @param {event} e The raw browser event
33190          */
33191         "dragover" : true,
33192         /**
33193          * @event dragenter
33194          *  Fires when the dragged row(s) first cross another DD target while being dragged
33195          * @param {Grid} this
33196          * @param {Roo.GridDD} dd The drag drop object
33197          * @param {String} targetId The target drag drop object
33198          * @param {event} e The raw browser event
33199          */
33200         "dragenter" : true,
33201         /**
33202          * @event dragout
33203          * Fires when the dragged row(s) leave another DD target while being dragged
33204          * @param {Grid} this
33205          * @param {Roo.GridDD} dd The drag drop object
33206          * @param {String} targetId The target drag drop object
33207          * @param {event} e The raw browser event
33208          */
33209         "dragout" : true,
33210         /**
33211          * @event rowclass
33212          * Fires when a row is rendered, so you can change add a style to it.
33213          * @param {GridView} gridview   The grid view
33214          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33215          */
33216         'rowclass' : true,
33217
33218         /**
33219          * @event render
33220          * Fires when the grid is rendered
33221          * @param {Grid} grid
33222          */
33223         'render' : true
33224     });
33225
33226     Roo.grid.Grid.superclass.constructor.call(this);
33227 };
33228 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33229     
33230     /**
33231      * @cfg {String} ddGroup - drag drop group.
33232      */
33233
33234     /**
33235      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33236      */
33237     minColumnWidth : 25,
33238
33239     /**
33240      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33241      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33242      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33243      */
33244     autoSizeColumns : false,
33245
33246     /**
33247      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33248      */
33249     autoSizeHeaders : true,
33250
33251     /**
33252      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33253      */
33254     monitorWindowResize : true,
33255
33256     /**
33257      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33258      * rows measured to get a columns size. Default is 0 (all rows).
33259      */
33260     maxRowsToMeasure : 0,
33261
33262     /**
33263      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33264      */
33265     trackMouseOver : true,
33266
33267     /**
33268     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33269     */
33270     
33271     /**
33272     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33273     */
33274     enableDragDrop : false,
33275     
33276     /**
33277     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33278     */
33279     enableColumnMove : true,
33280     
33281     /**
33282     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33283     */
33284     enableColumnHide : true,
33285     
33286     /**
33287     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33288     */
33289     enableRowHeightSync : false,
33290     
33291     /**
33292     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33293     */
33294     stripeRows : true,
33295     
33296     /**
33297     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33298     */
33299     autoHeight : false,
33300
33301     /**
33302      * @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.
33303      */
33304     autoExpandColumn : false,
33305
33306     /**
33307     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33308     * Default is 50.
33309     */
33310     autoExpandMin : 50,
33311
33312     /**
33313     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33314     */
33315     autoExpandMax : 1000,
33316
33317     /**
33318     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33319     */
33320     view : null,
33321
33322     /**
33323     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33324     */
33325     loadMask : false,
33326     /**
33327     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33328     */
33329     dropTarget: false,
33330     
33331    
33332     
33333     // private
33334     rendered : false,
33335
33336     /**
33337     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33338     * of a fixed width. Default is false.
33339     */
33340     /**
33341     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33342     */
33343     /**
33344      * Called once after all setup has been completed and the grid is ready to be rendered.
33345      * @return {Roo.grid.Grid} this
33346      */
33347     render : function()
33348     {
33349         var c = this.container;
33350         // try to detect autoHeight/width mode
33351         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33352             this.autoHeight = true;
33353         }
33354         var view = this.getView();
33355         view.init(this);
33356
33357         c.on("click", this.onClick, this);
33358         c.on("dblclick", this.onDblClick, this);
33359         c.on("contextmenu", this.onContextMenu, this);
33360         c.on("keydown", this.onKeyDown, this);
33361
33362         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33363
33364         this.getSelectionModel().init(this);
33365
33366         view.render();
33367
33368         if(this.loadMask){
33369             this.loadMask = new Roo.LoadMask(this.container,
33370                     Roo.apply({store:this.dataSource}, this.loadMask));
33371         }
33372         
33373         
33374         if (this.toolbar && this.toolbar.xtype) {
33375             this.toolbar.container = this.getView().getHeaderPanel(true);
33376             this.toolbar = new Roo.Toolbar(this.toolbar);
33377         }
33378         if (this.footer && this.footer.xtype) {
33379             this.footer.dataSource = this.getDataSource();
33380             this.footer.container = this.getView().getFooterPanel(true);
33381             this.footer = Roo.factory(this.footer, Roo);
33382         }
33383         if (this.dropTarget && this.dropTarget.xtype) {
33384             delete this.dropTarget.xtype;
33385             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33386         }
33387         
33388         
33389         this.rendered = true;
33390         this.fireEvent('render', this);
33391         return this;
33392     },
33393
33394         /**
33395          * Reconfigures the grid to use a different Store and Column Model.
33396          * The View will be bound to the new objects and refreshed.
33397          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33398          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33399          */
33400     reconfigure : function(dataSource, colModel){
33401         if(this.loadMask){
33402             this.loadMask.destroy();
33403             this.loadMask = new Roo.LoadMask(this.container,
33404                     Roo.apply({store:dataSource}, this.loadMask));
33405         }
33406         this.view.bind(dataSource, colModel);
33407         this.dataSource = dataSource;
33408         this.colModel = colModel;
33409         this.view.refresh(true);
33410     },
33411
33412     // private
33413     onKeyDown : function(e){
33414         this.fireEvent("keydown", e);
33415     },
33416
33417     /**
33418      * Destroy this grid.
33419      * @param {Boolean} removeEl True to remove the element
33420      */
33421     destroy : function(removeEl, keepListeners){
33422         if(this.loadMask){
33423             this.loadMask.destroy();
33424         }
33425         var c = this.container;
33426         c.removeAllListeners();
33427         this.view.destroy();
33428         this.colModel.purgeListeners();
33429         if(!keepListeners){
33430             this.purgeListeners();
33431         }
33432         c.update("");
33433         if(removeEl === true){
33434             c.remove();
33435         }
33436     },
33437
33438     // private
33439     processEvent : function(name, e){
33440         this.fireEvent(name, e);
33441         var t = e.getTarget();
33442         var v = this.view;
33443         var header = v.findHeaderIndex(t);
33444         if(header !== false){
33445             this.fireEvent("header" + name, this, header, e);
33446         }else{
33447             var row = v.findRowIndex(t);
33448             var cell = v.findCellIndex(t);
33449             if(row !== false){
33450                 this.fireEvent("row" + name, this, row, e);
33451                 if(cell !== false){
33452                     this.fireEvent("cell" + name, this, row, cell, e);
33453                 }
33454             }
33455         }
33456     },
33457
33458     // private
33459     onClick : function(e){
33460         this.processEvent("click", e);
33461     },
33462
33463     // private
33464     onContextMenu : function(e, t){
33465         this.processEvent("contextmenu", e);
33466     },
33467
33468     // private
33469     onDblClick : function(e){
33470         this.processEvent("dblclick", e);
33471     },
33472
33473     // private
33474     walkCells : function(row, col, step, fn, scope){
33475         var cm = this.colModel, clen = cm.getColumnCount();
33476         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33477         if(step < 0){
33478             if(col < 0){
33479                 row--;
33480                 first = false;
33481             }
33482             while(row >= 0){
33483                 if(!first){
33484                     col = clen-1;
33485                 }
33486                 first = false;
33487                 while(col >= 0){
33488                     if(fn.call(scope || this, row, col, cm) === true){
33489                         return [row, col];
33490                     }
33491                     col--;
33492                 }
33493                 row--;
33494             }
33495         } else {
33496             if(col >= clen){
33497                 row++;
33498                 first = false;
33499             }
33500             while(row < rlen){
33501                 if(!first){
33502                     col = 0;
33503                 }
33504                 first = false;
33505                 while(col < clen){
33506                     if(fn.call(scope || this, row, col, cm) === true){
33507                         return [row, col];
33508                     }
33509                     col++;
33510                 }
33511                 row++;
33512             }
33513         }
33514         return null;
33515     },
33516
33517     // private
33518     getSelections : function(){
33519         return this.selModel.getSelections();
33520     },
33521
33522     /**
33523      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33524      * but if manual update is required this method will initiate it.
33525      */
33526     autoSize : function(){
33527         if(this.rendered){
33528             this.view.layout();
33529             if(this.view.adjustForScroll){
33530                 this.view.adjustForScroll();
33531             }
33532         }
33533     },
33534
33535     /**
33536      * Returns the grid's underlying element.
33537      * @return {Element} The element
33538      */
33539     getGridEl : function(){
33540         return this.container;
33541     },
33542
33543     // private for compatibility, overridden by editor grid
33544     stopEditing : function(){},
33545
33546     /**
33547      * Returns the grid's SelectionModel.
33548      * @return {SelectionModel}
33549      */
33550     getSelectionModel : function(){
33551         if(!this.selModel){
33552             this.selModel = new Roo.grid.RowSelectionModel();
33553         }
33554         return this.selModel;
33555     },
33556
33557     /**
33558      * Returns the grid's DataSource.
33559      * @return {DataSource}
33560      */
33561     getDataSource : function(){
33562         return this.dataSource;
33563     },
33564
33565     /**
33566      * Returns the grid's ColumnModel.
33567      * @return {ColumnModel}
33568      */
33569     getColumnModel : function(){
33570         return this.colModel;
33571     },
33572
33573     /**
33574      * Returns the grid's GridView object.
33575      * @return {GridView}
33576      */
33577     getView : function(){
33578         if(!this.view){
33579             this.view = new Roo.grid.GridView(this.viewConfig);
33580         }
33581         return this.view;
33582     },
33583     /**
33584      * Called to get grid's drag proxy text, by default returns this.ddText.
33585      * @return {String}
33586      */
33587     getDragDropText : function(){
33588         var count = this.selModel.getCount();
33589         return String.format(this.ddText, count, count == 1 ? '' : 's');
33590     }
33591 });
33592 /**
33593  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33594  * %0 is replaced with the number of selected rows.
33595  * @type String
33596  */
33597 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33598  * Based on:
33599  * Ext JS Library 1.1.1
33600  * Copyright(c) 2006-2007, Ext JS, LLC.
33601  *
33602  * Originally Released Under LGPL - original licence link has changed is not relivant.
33603  *
33604  * Fork - LGPL
33605  * <script type="text/javascript">
33606  */
33607  
33608 Roo.grid.AbstractGridView = function(){
33609         this.grid = null;
33610         
33611         this.events = {
33612             "beforerowremoved" : true,
33613             "beforerowsinserted" : true,
33614             "beforerefresh" : true,
33615             "rowremoved" : true,
33616             "rowsinserted" : true,
33617             "rowupdated" : true,
33618             "refresh" : true
33619         };
33620     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33621 };
33622
33623 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33624     rowClass : "x-grid-row",
33625     cellClass : "x-grid-cell",
33626     tdClass : "x-grid-td",
33627     hdClass : "x-grid-hd",
33628     splitClass : "x-grid-hd-split",
33629     
33630         init: function(grid){
33631         this.grid = grid;
33632                 var cid = this.grid.getGridEl().id;
33633         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33634         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33635         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33636         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33637         },
33638         
33639         getColumnRenderers : function(){
33640         var renderers = [];
33641         var cm = this.grid.colModel;
33642         var colCount = cm.getColumnCount();
33643         for(var i = 0; i < colCount; i++){
33644             renderers[i] = cm.getRenderer(i);
33645         }
33646         return renderers;
33647     },
33648     
33649     getColumnIds : function(){
33650         var ids = [];
33651         var cm = this.grid.colModel;
33652         var colCount = cm.getColumnCount();
33653         for(var i = 0; i < colCount; i++){
33654             ids[i] = cm.getColumnId(i);
33655         }
33656         return ids;
33657     },
33658     
33659     getDataIndexes : function(){
33660         if(!this.indexMap){
33661             this.indexMap = this.buildIndexMap();
33662         }
33663         return this.indexMap.colToData;
33664     },
33665     
33666     getColumnIndexByDataIndex : function(dataIndex){
33667         if(!this.indexMap){
33668             this.indexMap = this.buildIndexMap();
33669         }
33670         return this.indexMap.dataToCol[dataIndex];
33671     },
33672     
33673     /**
33674      * Set a css style for a column dynamically. 
33675      * @param {Number} colIndex The index of the column
33676      * @param {String} name The css property name
33677      * @param {String} value The css value
33678      */
33679     setCSSStyle : function(colIndex, name, value){
33680         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33681         Roo.util.CSS.updateRule(selector, name, value);
33682     },
33683     
33684     generateRules : function(cm){
33685         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33686         Roo.util.CSS.removeStyleSheet(rulesId);
33687         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33688             var cid = cm.getColumnId(i);
33689             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33690                          this.tdSelector, cid, " {\n}\n",
33691                          this.hdSelector, cid, " {\n}\n",
33692                          this.splitSelector, cid, " {\n}\n");
33693         }
33694         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33695     }
33696 });/*
33697  * Based on:
33698  * Ext JS Library 1.1.1
33699  * Copyright(c) 2006-2007, Ext JS, LLC.
33700  *
33701  * Originally Released Under LGPL - original licence link has changed is not relivant.
33702  *
33703  * Fork - LGPL
33704  * <script type="text/javascript">
33705  */
33706
33707 // private
33708 // This is a support class used internally by the Grid components
33709 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33710     this.grid = grid;
33711     this.view = grid.getView();
33712     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33713     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33714     if(hd2){
33715         this.setHandleElId(Roo.id(hd));
33716         this.setOuterHandleElId(Roo.id(hd2));
33717     }
33718     this.scroll = false;
33719 };
33720 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33721     maxDragWidth: 120,
33722     getDragData : function(e){
33723         var t = Roo.lib.Event.getTarget(e);
33724         var h = this.view.findHeaderCell(t);
33725         if(h){
33726             return {ddel: h.firstChild, header:h};
33727         }
33728         return false;
33729     },
33730
33731     onInitDrag : function(e){
33732         this.view.headersDisabled = true;
33733         var clone = this.dragData.ddel.cloneNode(true);
33734         clone.id = Roo.id();
33735         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33736         this.proxy.update(clone);
33737         return true;
33738     },
33739
33740     afterValidDrop : function(){
33741         var v = this.view;
33742         setTimeout(function(){
33743             v.headersDisabled = false;
33744         }, 50);
33745     },
33746
33747     afterInvalidDrop : function(){
33748         var v = this.view;
33749         setTimeout(function(){
33750             v.headersDisabled = false;
33751         }, 50);
33752     }
33753 });
33754 /*
33755  * Based on:
33756  * Ext JS Library 1.1.1
33757  * Copyright(c) 2006-2007, Ext JS, LLC.
33758  *
33759  * Originally Released Under LGPL - original licence link has changed is not relivant.
33760  *
33761  * Fork - LGPL
33762  * <script type="text/javascript">
33763  */
33764 // private
33765 // This is a support class used internally by the Grid components
33766 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33767     this.grid = grid;
33768     this.view = grid.getView();
33769     // split the proxies so they don't interfere with mouse events
33770     this.proxyTop = Roo.DomHelper.append(document.body, {
33771         cls:"col-move-top", html:"&#160;"
33772     }, true);
33773     this.proxyBottom = Roo.DomHelper.append(document.body, {
33774         cls:"col-move-bottom", html:"&#160;"
33775     }, true);
33776     this.proxyTop.hide = this.proxyBottom.hide = function(){
33777         this.setLeftTop(-100,-100);
33778         this.setStyle("visibility", "hidden");
33779     };
33780     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33781     // temporarily disabled
33782     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33783     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33784 };
33785 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33786     proxyOffsets : [-4, -9],
33787     fly: Roo.Element.fly,
33788
33789     getTargetFromEvent : function(e){
33790         var t = Roo.lib.Event.getTarget(e);
33791         var cindex = this.view.findCellIndex(t);
33792         if(cindex !== false){
33793             return this.view.getHeaderCell(cindex);
33794         }
33795         return null;
33796     },
33797
33798     nextVisible : function(h){
33799         var v = this.view, cm = this.grid.colModel;
33800         h = h.nextSibling;
33801         while(h){
33802             if(!cm.isHidden(v.getCellIndex(h))){
33803                 return h;
33804             }
33805             h = h.nextSibling;
33806         }
33807         return null;
33808     },
33809
33810     prevVisible : function(h){
33811         var v = this.view, cm = this.grid.colModel;
33812         h = h.prevSibling;
33813         while(h){
33814             if(!cm.isHidden(v.getCellIndex(h))){
33815                 return h;
33816             }
33817             h = h.prevSibling;
33818         }
33819         return null;
33820     },
33821
33822     positionIndicator : function(h, n, e){
33823         var x = Roo.lib.Event.getPageX(e);
33824         var r = Roo.lib.Dom.getRegion(n.firstChild);
33825         var px, pt, py = r.top + this.proxyOffsets[1];
33826         if((r.right - x) <= (r.right-r.left)/2){
33827             px = r.right+this.view.borderWidth;
33828             pt = "after";
33829         }else{
33830             px = r.left;
33831             pt = "before";
33832         }
33833         var oldIndex = this.view.getCellIndex(h);
33834         var newIndex = this.view.getCellIndex(n);
33835
33836         if(this.grid.colModel.isFixed(newIndex)){
33837             return false;
33838         }
33839
33840         var locked = this.grid.colModel.isLocked(newIndex);
33841
33842         if(pt == "after"){
33843             newIndex++;
33844         }
33845         if(oldIndex < newIndex){
33846             newIndex--;
33847         }
33848         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33849             return false;
33850         }
33851         px +=  this.proxyOffsets[0];
33852         this.proxyTop.setLeftTop(px, py);
33853         this.proxyTop.show();
33854         if(!this.bottomOffset){
33855             this.bottomOffset = this.view.mainHd.getHeight();
33856         }
33857         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33858         this.proxyBottom.show();
33859         return pt;
33860     },
33861
33862     onNodeEnter : function(n, dd, e, data){
33863         if(data.header != n){
33864             this.positionIndicator(data.header, n, e);
33865         }
33866     },
33867
33868     onNodeOver : function(n, dd, e, data){
33869         var result = false;
33870         if(data.header != n){
33871             result = this.positionIndicator(data.header, n, e);
33872         }
33873         if(!result){
33874             this.proxyTop.hide();
33875             this.proxyBottom.hide();
33876         }
33877         return result ? this.dropAllowed : this.dropNotAllowed;
33878     },
33879
33880     onNodeOut : function(n, dd, e, data){
33881         this.proxyTop.hide();
33882         this.proxyBottom.hide();
33883     },
33884
33885     onNodeDrop : function(n, dd, e, data){
33886         var h = data.header;
33887         if(h != n){
33888             var cm = this.grid.colModel;
33889             var x = Roo.lib.Event.getPageX(e);
33890             var r = Roo.lib.Dom.getRegion(n.firstChild);
33891             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33892             var oldIndex = this.view.getCellIndex(h);
33893             var newIndex = this.view.getCellIndex(n);
33894             var locked = cm.isLocked(newIndex);
33895             if(pt == "after"){
33896                 newIndex++;
33897             }
33898             if(oldIndex < newIndex){
33899                 newIndex--;
33900             }
33901             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33902                 return false;
33903             }
33904             cm.setLocked(oldIndex, locked, true);
33905             cm.moveColumn(oldIndex, newIndex);
33906             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33907             return true;
33908         }
33909         return false;
33910     }
33911 });
33912 /*
33913  * Based on:
33914  * Ext JS Library 1.1.1
33915  * Copyright(c) 2006-2007, Ext JS, LLC.
33916  *
33917  * Originally Released Under LGPL - original licence link has changed is not relivant.
33918  *
33919  * Fork - LGPL
33920  * <script type="text/javascript">
33921  */
33922   
33923 /**
33924  * @class Roo.grid.GridView
33925  * @extends Roo.util.Observable
33926  *
33927  * @constructor
33928  * @param {Object} config
33929  */
33930 Roo.grid.GridView = function(config){
33931     Roo.grid.GridView.superclass.constructor.call(this);
33932     this.el = null;
33933
33934     Roo.apply(this, config);
33935 };
33936
33937 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33938
33939     /**
33940      * Override this function to apply custom css classes to rows during rendering
33941      * @param {Record} record The record
33942      * @param {Number} index
33943      * @method getRowClass
33944      */
33945     rowClass : "x-grid-row",
33946
33947     cellClass : "x-grid-col",
33948
33949     tdClass : "x-grid-td",
33950
33951     hdClass : "x-grid-hd",
33952
33953     splitClass : "x-grid-split",
33954
33955     sortClasses : ["sort-asc", "sort-desc"],
33956
33957     enableMoveAnim : false,
33958
33959     hlColor: "C3DAF9",
33960
33961     dh : Roo.DomHelper,
33962
33963     fly : Roo.Element.fly,
33964
33965     css : Roo.util.CSS,
33966
33967     borderWidth: 1,
33968
33969     splitOffset: 3,
33970
33971     scrollIncrement : 22,
33972
33973     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33974
33975     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33976
33977     bind : function(ds, cm){
33978         if(this.ds){
33979             this.ds.un("load", this.onLoad, this);
33980             this.ds.un("datachanged", this.onDataChange, this);
33981             this.ds.un("add", this.onAdd, this);
33982             this.ds.un("remove", this.onRemove, this);
33983             this.ds.un("update", this.onUpdate, this);
33984             this.ds.un("clear", this.onClear, this);
33985         }
33986         if(ds){
33987             ds.on("load", this.onLoad, this);
33988             ds.on("datachanged", this.onDataChange, this);
33989             ds.on("add", this.onAdd, this);
33990             ds.on("remove", this.onRemove, this);
33991             ds.on("update", this.onUpdate, this);
33992             ds.on("clear", this.onClear, this);
33993         }
33994         this.ds = ds;
33995
33996         if(this.cm){
33997             this.cm.un("widthchange", this.onColWidthChange, this);
33998             this.cm.un("headerchange", this.onHeaderChange, this);
33999             this.cm.un("hiddenchange", this.onHiddenChange, this);
34000             this.cm.un("columnmoved", this.onColumnMove, this);
34001             this.cm.un("columnlockchange", this.onColumnLock, this);
34002         }
34003         if(cm){
34004             this.generateRules(cm);
34005             cm.on("widthchange", this.onColWidthChange, this);
34006             cm.on("headerchange", this.onHeaderChange, this);
34007             cm.on("hiddenchange", this.onHiddenChange, this);
34008             cm.on("columnmoved", this.onColumnMove, this);
34009             cm.on("columnlockchange", this.onColumnLock, this);
34010         }
34011         this.cm = cm;
34012     },
34013
34014     init: function(grid){
34015         Roo.grid.GridView.superclass.init.call(this, grid);
34016
34017         this.bind(grid.dataSource, grid.colModel);
34018
34019         grid.on("headerclick", this.handleHeaderClick, this);
34020
34021         if(grid.trackMouseOver){
34022             grid.on("mouseover", this.onRowOver, this);
34023             grid.on("mouseout", this.onRowOut, this);
34024         }
34025         grid.cancelTextSelection = function(){};
34026         this.gridId = grid.id;
34027
34028         var tpls = this.templates || {};
34029
34030         if(!tpls.master){
34031             tpls.master = new Roo.Template(
34032                '<div class="x-grid" hidefocus="true">',
34033                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34034                   '<div class="x-grid-topbar"></div>',
34035                   '<div class="x-grid-scroller"><div></div></div>',
34036                   '<div class="x-grid-locked">',
34037                       '<div class="x-grid-header">{lockedHeader}</div>',
34038                       '<div class="x-grid-body">{lockedBody}</div>',
34039                   "</div>",
34040                   '<div class="x-grid-viewport">',
34041                       '<div class="x-grid-header">{header}</div>',
34042                       '<div class="x-grid-body">{body}</div>',
34043                   "</div>",
34044                   '<div class="x-grid-bottombar"></div>',
34045                  
34046                   '<div class="x-grid-resize-proxy">&#160;</div>',
34047                "</div>"
34048             );
34049             tpls.master.disableformats = true;
34050         }
34051
34052         if(!tpls.header){
34053             tpls.header = new Roo.Template(
34054                '<table border="0" cellspacing="0" cellpadding="0">',
34055                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34056                "</table>{splits}"
34057             );
34058             tpls.header.disableformats = true;
34059         }
34060         tpls.header.compile();
34061
34062         if(!tpls.hcell){
34063             tpls.hcell = new Roo.Template(
34064                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34065                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34066                 "</div></td>"
34067              );
34068              tpls.hcell.disableFormats = true;
34069         }
34070         tpls.hcell.compile();
34071
34072         if(!tpls.hsplit){
34073             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34074             tpls.hsplit.disableFormats = true;
34075         }
34076         tpls.hsplit.compile();
34077
34078         if(!tpls.body){
34079             tpls.body = new Roo.Template(
34080                '<table border="0" cellspacing="0" cellpadding="0">',
34081                "<tbody>{rows}</tbody>",
34082                "</table>"
34083             );
34084             tpls.body.disableFormats = true;
34085         }
34086         tpls.body.compile();
34087
34088         if(!tpls.row){
34089             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34090             tpls.row.disableFormats = true;
34091         }
34092         tpls.row.compile();
34093
34094         if(!tpls.cell){
34095             tpls.cell = new Roo.Template(
34096                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34097                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34098                 "</td>"
34099             );
34100             tpls.cell.disableFormats = true;
34101         }
34102         tpls.cell.compile();
34103
34104         this.templates = tpls;
34105     },
34106
34107     // remap these for backwards compat
34108     onColWidthChange : function(){
34109         this.updateColumns.apply(this, arguments);
34110     },
34111     onHeaderChange : function(){
34112         this.updateHeaders.apply(this, arguments);
34113     }, 
34114     onHiddenChange : function(){
34115         this.handleHiddenChange.apply(this, arguments);
34116     },
34117     onColumnMove : function(){
34118         this.handleColumnMove.apply(this, arguments);
34119     },
34120     onColumnLock : function(){
34121         this.handleLockChange.apply(this, arguments);
34122     },
34123
34124     onDataChange : function(){
34125         this.refresh();
34126         this.updateHeaderSortState();
34127     },
34128
34129     onClear : function(){
34130         this.refresh();
34131     },
34132
34133     onUpdate : function(ds, record){
34134         this.refreshRow(record);
34135     },
34136
34137     refreshRow : function(record){
34138         var ds = this.ds, index;
34139         if(typeof record == 'number'){
34140             index = record;
34141             record = ds.getAt(index);
34142         }else{
34143             index = ds.indexOf(record);
34144         }
34145         this.insertRows(ds, index, index, true);
34146         this.onRemove(ds, record, index+1, true);
34147         this.syncRowHeights(index, index);
34148         this.layout();
34149         this.fireEvent("rowupdated", this, index, record);
34150     },
34151
34152     onAdd : function(ds, records, index){
34153         this.insertRows(ds, index, index + (records.length-1));
34154     },
34155
34156     onRemove : function(ds, record, index, isUpdate){
34157         if(isUpdate !== true){
34158             this.fireEvent("beforerowremoved", this, index, record);
34159         }
34160         var bt = this.getBodyTable(), lt = this.getLockedTable();
34161         if(bt.rows[index]){
34162             bt.firstChild.removeChild(bt.rows[index]);
34163         }
34164         if(lt.rows[index]){
34165             lt.firstChild.removeChild(lt.rows[index]);
34166         }
34167         if(isUpdate !== true){
34168             this.stripeRows(index);
34169             this.syncRowHeights(index, index);
34170             this.layout();
34171             this.fireEvent("rowremoved", this, index, record);
34172         }
34173     },
34174
34175     onLoad : function(){
34176         this.scrollToTop();
34177     },
34178
34179     /**
34180      * Scrolls the grid to the top
34181      */
34182     scrollToTop : function(){
34183         if(this.scroller){
34184             this.scroller.dom.scrollTop = 0;
34185             this.syncScroll();
34186         }
34187     },
34188
34189     /**
34190      * Gets a panel in the header of the grid that can be used for toolbars etc.
34191      * After modifying the contents of this panel a call to grid.autoSize() may be
34192      * required to register any changes in size.
34193      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34194      * @return Roo.Element
34195      */
34196     getHeaderPanel : function(doShow){
34197         if(doShow){
34198             this.headerPanel.show();
34199         }
34200         return this.headerPanel;
34201     },
34202
34203     /**
34204      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34205      * After modifying the contents of this panel a call to grid.autoSize() may be
34206      * required to register any changes in size.
34207      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34208      * @return Roo.Element
34209      */
34210     getFooterPanel : function(doShow){
34211         if(doShow){
34212             this.footerPanel.show();
34213         }
34214         return this.footerPanel;
34215     },
34216
34217     initElements : function(){
34218         var E = Roo.Element;
34219         var el = this.grid.getGridEl().dom.firstChild;
34220         var cs = el.childNodes;
34221
34222         this.el = new E(el);
34223         
34224          this.focusEl = new E(el.firstChild);
34225         this.focusEl.swallowEvent("click", true);
34226         
34227         this.headerPanel = new E(cs[1]);
34228         this.headerPanel.enableDisplayMode("block");
34229
34230         this.scroller = new E(cs[2]);
34231         this.scrollSizer = new E(this.scroller.dom.firstChild);
34232
34233         this.lockedWrap = new E(cs[3]);
34234         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34235         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34236
34237         this.mainWrap = new E(cs[4]);
34238         this.mainHd = new E(this.mainWrap.dom.firstChild);
34239         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34240
34241         this.footerPanel = new E(cs[5]);
34242         this.footerPanel.enableDisplayMode("block");
34243
34244         this.resizeProxy = new E(cs[6]);
34245
34246         this.headerSelector = String.format(
34247            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34248            this.lockedHd.id, this.mainHd.id
34249         );
34250
34251         this.splitterSelector = String.format(
34252            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34253            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34254         );
34255     },
34256     idToCssName : function(s)
34257     {
34258         return s.replace(/[^a-z0-9]+/ig, '-');
34259     },
34260
34261     getHeaderCell : function(index){
34262         return Roo.DomQuery.select(this.headerSelector)[index];
34263     },
34264
34265     getHeaderCellMeasure : function(index){
34266         return this.getHeaderCell(index).firstChild;
34267     },
34268
34269     getHeaderCellText : function(index){
34270         return this.getHeaderCell(index).firstChild.firstChild;
34271     },
34272
34273     getLockedTable : function(){
34274         return this.lockedBody.dom.firstChild;
34275     },
34276
34277     getBodyTable : function(){
34278         return this.mainBody.dom.firstChild;
34279     },
34280
34281     getLockedRow : function(index){
34282         return this.getLockedTable().rows[index];
34283     },
34284
34285     getRow : function(index){
34286         return this.getBodyTable().rows[index];
34287     },
34288
34289     getRowComposite : function(index){
34290         if(!this.rowEl){
34291             this.rowEl = new Roo.CompositeElementLite();
34292         }
34293         var els = [], lrow, mrow;
34294         if(lrow = this.getLockedRow(index)){
34295             els.push(lrow);
34296         }
34297         if(mrow = this.getRow(index)){
34298             els.push(mrow);
34299         }
34300         this.rowEl.elements = els;
34301         return this.rowEl;
34302     },
34303     /**
34304      * Gets the 'td' of the cell
34305      * 
34306      * @param {Integer} rowIndex row to select
34307      * @param {Integer} colIndex column to select
34308      * 
34309      * @return {Object} 
34310      */
34311     getCell : function(rowIndex, colIndex){
34312         var locked = this.cm.getLockedCount();
34313         var source;
34314         if(colIndex < locked){
34315             source = this.lockedBody.dom.firstChild;
34316         }else{
34317             source = this.mainBody.dom.firstChild;
34318             colIndex -= locked;
34319         }
34320         return source.rows[rowIndex].childNodes[colIndex];
34321     },
34322
34323     getCellText : function(rowIndex, colIndex){
34324         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34325     },
34326
34327     getCellBox : function(cell){
34328         var b = this.fly(cell).getBox();
34329         if(Roo.isOpera){ // opera fails to report the Y
34330             b.y = cell.offsetTop + this.mainBody.getY();
34331         }
34332         return b;
34333     },
34334
34335     getCellIndex : function(cell){
34336         var id = String(cell.className).match(this.cellRE);
34337         if(id){
34338             return parseInt(id[1], 10);
34339         }
34340         return 0;
34341     },
34342
34343     findHeaderIndex : function(n){
34344         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34345         return r ? this.getCellIndex(r) : false;
34346     },
34347
34348     findHeaderCell : function(n){
34349         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34350         return r ? r : false;
34351     },
34352
34353     findRowIndex : function(n){
34354         if(!n){
34355             return false;
34356         }
34357         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34358         return r ? r.rowIndex : false;
34359     },
34360
34361     findCellIndex : function(node){
34362         var stop = this.el.dom;
34363         while(node && node != stop){
34364             if(this.findRE.test(node.className)){
34365                 return this.getCellIndex(node);
34366             }
34367             node = node.parentNode;
34368         }
34369         return false;
34370     },
34371
34372     getColumnId : function(index){
34373         return this.cm.getColumnId(index);
34374     },
34375
34376     getSplitters : function()
34377     {
34378         if(this.splitterSelector){
34379            return Roo.DomQuery.select(this.splitterSelector);
34380         }else{
34381             return null;
34382       }
34383     },
34384
34385     getSplitter : function(index){
34386         return this.getSplitters()[index];
34387     },
34388
34389     onRowOver : function(e, t){
34390         var row;
34391         if((row = this.findRowIndex(t)) !== false){
34392             this.getRowComposite(row).addClass("x-grid-row-over");
34393         }
34394     },
34395
34396     onRowOut : function(e, t){
34397         var row;
34398         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34399             this.getRowComposite(row).removeClass("x-grid-row-over");
34400         }
34401     },
34402
34403     renderHeaders : function(){
34404         var cm = this.cm;
34405         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
34406         var cb = [], lb = [], sb = [], lsb = [], p = {};
34407         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34408             p.cellId = "x-grid-hd-0-" + i;
34409             p.splitId = "x-grid-csplit-0-" + i;
34410             p.id = cm.getColumnId(i);
34411             p.title = cm.getColumnTooltip(i) || "";
34412             p.value = cm.getColumnHeader(i) || "";
34413             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
34414             if(!cm.isLocked(i)){
34415                 cb[cb.length] = ct.apply(p);
34416                 sb[sb.length] = st.apply(p);
34417             }else{
34418                 lb[lb.length] = ct.apply(p);
34419                 lsb[lsb.length] = st.apply(p);
34420             }
34421         }
34422         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
34423                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
34424     },
34425
34426     updateHeaders : function(){
34427         var html = this.renderHeaders();
34428         this.lockedHd.update(html[0]);
34429         this.mainHd.update(html[1]);
34430     },
34431
34432     /**
34433      * Focuses the specified row.
34434      * @param {Number} row The row index
34435      */
34436     focusRow : function(row)
34437     {
34438         //Roo.log('GridView.focusRow');
34439         var x = this.scroller.dom.scrollLeft;
34440         this.focusCell(row, 0, false);
34441         this.scroller.dom.scrollLeft = x;
34442     },
34443
34444     /**
34445      * Focuses the specified cell.
34446      * @param {Number} row The row index
34447      * @param {Number} col The column index
34448      * @param {Boolean} hscroll false to disable horizontal scrolling
34449      */
34450     focusCell : function(row, col, hscroll)
34451     {
34452         //Roo.log('GridView.focusCell');
34453         var el = this.ensureVisible(row, col, hscroll);
34454         this.focusEl.alignTo(el, "tl-tl");
34455         if(Roo.isGecko){
34456             this.focusEl.focus();
34457         }else{
34458             this.focusEl.focus.defer(1, this.focusEl);
34459         }
34460     },
34461
34462     /**
34463      * Scrolls the specified cell into view
34464      * @param {Number} row The row index
34465      * @param {Number} col The column index
34466      * @param {Boolean} hscroll false to disable horizontal scrolling
34467      */
34468     ensureVisible : function(row, col, hscroll)
34469     {
34470         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34471         //return null; //disable for testing.
34472         if(typeof row != "number"){
34473             row = row.rowIndex;
34474         }
34475         if(row < 0 && row >= this.ds.getCount()){
34476             return  null;
34477         }
34478         col = (col !== undefined ? col : 0);
34479         var cm = this.grid.colModel;
34480         while(cm.isHidden(col)){
34481             col++;
34482         }
34483
34484         var el = this.getCell(row, col);
34485         if(!el){
34486             return null;
34487         }
34488         var c = this.scroller.dom;
34489
34490         var ctop = parseInt(el.offsetTop, 10);
34491         var cleft = parseInt(el.offsetLeft, 10);
34492         var cbot = ctop + el.offsetHeight;
34493         var cright = cleft + el.offsetWidth;
34494         
34495         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34496         var stop = parseInt(c.scrollTop, 10);
34497         var sleft = parseInt(c.scrollLeft, 10);
34498         var sbot = stop + ch;
34499         var sright = sleft + c.clientWidth;
34500         /*
34501         Roo.log('GridView.ensureVisible:' +
34502                 ' ctop:' + ctop +
34503                 ' c.clientHeight:' + c.clientHeight +
34504                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34505                 ' stop:' + stop +
34506                 ' cbot:' + cbot +
34507                 ' sbot:' + sbot +
34508                 ' ch:' + ch  
34509                 );
34510         */
34511         if(ctop < stop){
34512              c.scrollTop = ctop;
34513             //Roo.log("set scrolltop to ctop DISABLE?");
34514         }else if(cbot > sbot){
34515             //Roo.log("set scrolltop to cbot-ch");
34516             c.scrollTop = cbot-ch;
34517         }
34518         
34519         if(hscroll !== false){
34520             if(cleft < sleft){
34521                 c.scrollLeft = cleft;
34522             }else if(cright > sright){
34523                 c.scrollLeft = cright-c.clientWidth;
34524             }
34525         }
34526          
34527         return el;
34528     },
34529
34530     updateColumns : function(){
34531         this.grid.stopEditing();
34532         var cm = this.grid.colModel, colIds = this.getColumnIds();
34533         //var totalWidth = cm.getTotalWidth();
34534         var pos = 0;
34535         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34536             //if(cm.isHidden(i)) continue;
34537             var w = cm.getColumnWidth(i);
34538             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34539             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34540         }
34541         this.updateSplitters();
34542     },
34543
34544     generateRules : function(cm){
34545         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34546         Roo.util.CSS.removeStyleSheet(rulesId);
34547         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34548             var cid = cm.getColumnId(i);
34549             var align = '';
34550             if(cm.config[i].align){
34551                 align = 'text-align:'+cm.config[i].align+';';
34552             }
34553             var hidden = '';
34554             if(cm.isHidden(i)){
34555                 hidden = 'display:none;';
34556             }
34557             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34558             ruleBuf.push(
34559                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34560                     this.hdSelector, cid, " {\n", align, width, "}\n",
34561                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34562                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34563         }
34564         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34565     },
34566
34567     updateSplitters : function(){
34568         var cm = this.cm, s = this.getSplitters();
34569         if(s){ // splitters not created yet
34570             var pos = 0, locked = true;
34571             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34572                 if(cm.isHidden(i)) continue;
34573                 var w = cm.getColumnWidth(i); // make sure it's a number
34574                 if(!cm.isLocked(i) && locked){
34575                     pos = 0;
34576                     locked = false;
34577                 }
34578                 pos += w;
34579                 s[i].style.left = (pos-this.splitOffset) + "px";
34580             }
34581         }
34582     },
34583
34584     handleHiddenChange : function(colModel, colIndex, hidden){
34585         if(hidden){
34586             this.hideColumn(colIndex);
34587         }else{
34588             this.unhideColumn(colIndex);
34589         }
34590     },
34591
34592     hideColumn : function(colIndex){
34593         var cid = this.getColumnId(colIndex);
34594         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34595         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34596         if(Roo.isSafari){
34597             this.updateHeaders();
34598         }
34599         this.updateSplitters();
34600         this.layout();
34601     },
34602
34603     unhideColumn : function(colIndex){
34604         var cid = this.getColumnId(colIndex);
34605         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34606         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34607
34608         if(Roo.isSafari){
34609             this.updateHeaders();
34610         }
34611         this.updateSplitters();
34612         this.layout();
34613     },
34614
34615     insertRows : function(dm, firstRow, lastRow, isUpdate){
34616         if(firstRow == 0 && lastRow == dm.getCount()-1){
34617             this.refresh();
34618         }else{
34619             if(!isUpdate){
34620                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34621             }
34622             var s = this.getScrollState();
34623             var markup = this.renderRows(firstRow, lastRow);
34624             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34625             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34626             this.restoreScroll(s);
34627             if(!isUpdate){
34628                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34629                 this.syncRowHeights(firstRow, lastRow);
34630                 this.stripeRows(firstRow);
34631                 this.layout();
34632             }
34633         }
34634     },
34635
34636     bufferRows : function(markup, target, index){
34637         var before = null, trows = target.rows, tbody = target.tBodies[0];
34638         if(index < trows.length){
34639             before = trows[index];
34640         }
34641         var b = document.createElement("div");
34642         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34643         var rows = b.firstChild.rows;
34644         for(var i = 0, len = rows.length; i < len; i++){
34645             if(before){
34646                 tbody.insertBefore(rows[0], before);
34647             }else{
34648                 tbody.appendChild(rows[0]);
34649             }
34650         }
34651         b.innerHTML = "";
34652         b = null;
34653     },
34654
34655     deleteRows : function(dm, firstRow, lastRow){
34656         if(dm.getRowCount()<1){
34657             this.fireEvent("beforerefresh", this);
34658             this.mainBody.update("");
34659             this.lockedBody.update("");
34660             this.fireEvent("refresh", this);
34661         }else{
34662             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34663             var bt = this.getBodyTable();
34664             var tbody = bt.firstChild;
34665             var rows = bt.rows;
34666             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34667                 tbody.removeChild(rows[firstRow]);
34668             }
34669             this.stripeRows(firstRow);
34670             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34671         }
34672     },
34673
34674     updateRows : function(dataSource, firstRow, lastRow){
34675         var s = this.getScrollState();
34676         this.refresh();
34677         this.restoreScroll(s);
34678     },
34679
34680     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34681         if(!noRefresh){
34682            this.refresh();
34683         }
34684         this.updateHeaderSortState();
34685     },
34686
34687     getScrollState : function(){
34688         
34689         var sb = this.scroller.dom;
34690         return {left: sb.scrollLeft, top: sb.scrollTop};
34691     },
34692
34693     stripeRows : function(startRow){
34694         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34695             return;
34696         }
34697         startRow = startRow || 0;
34698         var rows = this.getBodyTable().rows;
34699         var lrows = this.getLockedTable().rows;
34700         var cls = ' x-grid-row-alt ';
34701         for(var i = startRow, len = rows.length; i < len; i++){
34702             var row = rows[i], lrow = lrows[i];
34703             var isAlt = ((i+1) % 2 == 0);
34704             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34705             if(isAlt == hasAlt){
34706                 continue;
34707             }
34708             if(isAlt){
34709                 row.className += " x-grid-row-alt";
34710             }else{
34711                 row.className = row.className.replace("x-grid-row-alt", "");
34712             }
34713             if(lrow){
34714                 lrow.className = row.className;
34715             }
34716         }
34717     },
34718
34719     restoreScroll : function(state){
34720         //Roo.log('GridView.restoreScroll');
34721         var sb = this.scroller.dom;
34722         sb.scrollLeft = state.left;
34723         sb.scrollTop = state.top;
34724         this.syncScroll();
34725     },
34726
34727     syncScroll : function(){
34728         //Roo.log('GridView.syncScroll');
34729         var sb = this.scroller.dom;
34730         var sh = this.mainHd.dom;
34731         var bs = this.mainBody.dom;
34732         var lv = this.lockedBody.dom;
34733         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34734         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34735     },
34736
34737     handleScroll : function(e){
34738         this.syncScroll();
34739         var sb = this.scroller.dom;
34740         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34741         e.stopEvent();
34742     },
34743
34744     handleWheel : function(e){
34745         var d = e.getWheelDelta();
34746         this.scroller.dom.scrollTop -= d*22;
34747         // set this here to prevent jumpy scrolling on large tables
34748         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34749         e.stopEvent();
34750     },
34751
34752     renderRows : function(startRow, endRow){
34753         // pull in all the crap needed to render rows
34754         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34755         var colCount = cm.getColumnCount();
34756
34757         if(ds.getCount() < 1){
34758             return ["", ""];
34759         }
34760
34761         // build a map for all the columns
34762         var cs = [];
34763         for(var i = 0; i < colCount; i++){
34764             var name = cm.getDataIndex(i);
34765             cs[i] = {
34766                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34767                 renderer : cm.getRenderer(i),
34768                 id : cm.getColumnId(i),
34769                 locked : cm.isLocked(i)
34770             };
34771         }
34772
34773         startRow = startRow || 0;
34774         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34775
34776         // records to render
34777         var rs = ds.getRange(startRow, endRow);
34778
34779         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34780     },
34781
34782     // As much as I hate to duplicate code, this was branched because FireFox really hates
34783     // [].join("") on strings. The performance difference was substantial enough to
34784     // branch this function
34785     doRender : Roo.isGecko ?
34786             function(cs, rs, ds, startRow, colCount, stripe){
34787                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34788                 // buffers
34789                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34790                 
34791                 var hasListener = this.grid.hasListener('rowclass');
34792                 var rowcfg = {};
34793                 for(var j = 0, len = rs.length; j < len; j++){
34794                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34795                     for(var i = 0; i < colCount; i++){
34796                         c = cs[i];
34797                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34798                         p.id = c.id;
34799                         p.css = p.attr = "";
34800                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34801                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34802                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34803                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34804                         }
34805                         var markup = ct.apply(p);
34806                         if(!c.locked){
34807                             cb+= markup;
34808                         }else{
34809                             lcb+= markup;
34810                         }
34811                     }
34812                     var alt = [];
34813                     if(stripe && ((rowIndex+1) % 2 == 0)){
34814                         alt.push("x-grid-row-alt")
34815                     }
34816                     if(r.dirty){
34817                         alt.push(  " x-grid-dirty-row");
34818                     }
34819                     rp.cells = lcb;
34820                     if(this.getRowClass){
34821                         alt.push(this.getRowClass(r, rowIndex));
34822                     }
34823                     if (hasListener) {
34824                         rowcfg = {
34825                              
34826                             record: r,
34827                             rowIndex : rowIndex,
34828                             rowClass : ''
34829                         }
34830                         this.grid.fireEvent('rowclass', this, rowcfg);
34831                         alt.push(rowcfg.rowClass);
34832                     }
34833                     rp.alt = alt.join(" ");
34834                     lbuf+= rt.apply(rp);
34835                     rp.cells = cb;
34836                     buf+=  rt.apply(rp);
34837                 }
34838                 return [lbuf, buf];
34839             } :
34840             function(cs, rs, ds, startRow, colCount, stripe){
34841                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34842                 // buffers
34843                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34844                 var hasListener = this.grid.hasListener('rowclass');
34845                 var rowcfg = {};
34846                 for(var j = 0, len = rs.length; j < len; j++){
34847                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34848                     for(var i = 0; i < colCount; i++){
34849                         c = cs[i];
34850                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34851                         p.id = c.id;
34852                         p.css = p.attr = "";
34853                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34854                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34855                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34856                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34857                         }
34858                         var markup = ct.apply(p);
34859                         if(!c.locked){
34860                             cb[cb.length] = markup;
34861                         }else{
34862                             lcb[lcb.length] = markup;
34863                         }
34864                     }
34865                     var alt = [];
34866                     if(stripe && ((rowIndex+1) % 2 == 0)){
34867                         alt.push( "x-grid-row-alt");
34868                     }
34869                     if(r.dirty){
34870                         alt.push(" x-grid-dirty-row");
34871                     }
34872                     rp.cells = lcb;
34873                     if(this.getRowClass){
34874                         alt.push( this.getRowClass(r, rowIndex));
34875                     }
34876                     if (hasListener) {
34877                         rowcfg = {
34878                              
34879                             record: r,
34880                             rowIndex : rowIndex,
34881                             rowClass : ''
34882                         }
34883                         this.grid.fireEvent('rowclass', this, rowcfg);
34884                         alt.push(rowcfg.rowClass);
34885                     }
34886                     rp.alt = alt.join(" ");
34887                     rp.cells = lcb.join("");
34888                     lbuf[lbuf.length] = rt.apply(rp);
34889                     rp.cells = cb.join("");
34890                     buf[buf.length] =  rt.apply(rp);
34891                 }
34892                 return [lbuf.join(""), buf.join("")];
34893             },
34894
34895     renderBody : function(){
34896         var markup = this.renderRows();
34897         var bt = this.templates.body;
34898         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34899     },
34900
34901     /**
34902      * Refreshes the grid
34903      * @param {Boolean} headersToo
34904      */
34905     refresh : function(headersToo){
34906         this.fireEvent("beforerefresh", this);
34907         this.grid.stopEditing();
34908         var result = this.renderBody();
34909         this.lockedBody.update(result[0]);
34910         this.mainBody.update(result[1]);
34911         if(headersToo === true){
34912             this.updateHeaders();
34913             this.updateColumns();
34914             this.updateSplitters();
34915             this.updateHeaderSortState();
34916         }
34917         this.syncRowHeights();
34918         this.layout();
34919         this.fireEvent("refresh", this);
34920     },
34921
34922     handleColumnMove : function(cm, oldIndex, newIndex){
34923         this.indexMap = null;
34924         var s = this.getScrollState();
34925         this.refresh(true);
34926         this.restoreScroll(s);
34927         this.afterMove(newIndex);
34928     },
34929
34930     afterMove : function(colIndex){
34931         if(this.enableMoveAnim && Roo.enableFx){
34932             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34933         }
34934         // if multisort - fix sortOrder, and reload..
34935         if (this.grid.dataSource.multiSort) {
34936             // the we can call sort again..
34937             var dm = this.grid.dataSource;
34938             var cm = this.grid.colModel;
34939             var so = [];
34940             for(var i = 0; i < cm.config.length; i++ ) {
34941                 
34942                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34943                     continue; // dont' bother, it's not in sort list or being set.
34944                 }
34945                 
34946                 so.push(cm.config[i].dataIndex);
34947             };
34948             dm.sortOrder = so;
34949             dm.load(dm.lastOptions);
34950             
34951             
34952         }
34953         
34954     },
34955
34956     updateCell : function(dm, rowIndex, dataIndex){
34957         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34958         if(typeof colIndex == "undefined"){ // not present in grid
34959             return;
34960         }
34961         var cm = this.grid.colModel;
34962         var cell = this.getCell(rowIndex, colIndex);
34963         var cellText = this.getCellText(rowIndex, colIndex);
34964
34965         var p = {
34966             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34967             id : cm.getColumnId(colIndex),
34968             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34969         };
34970         var renderer = cm.getRenderer(colIndex);
34971         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34972         if(typeof val == "undefined" || val === "") val = "&#160;";
34973         cellText.innerHTML = val;
34974         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34975         this.syncRowHeights(rowIndex, rowIndex);
34976     },
34977
34978     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34979         var maxWidth = 0;
34980         if(this.grid.autoSizeHeaders){
34981             var h = this.getHeaderCellMeasure(colIndex);
34982             maxWidth = Math.max(maxWidth, h.scrollWidth);
34983         }
34984         var tb, index;
34985         if(this.cm.isLocked(colIndex)){
34986             tb = this.getLockedTable();
34987             index = colIndex;
34988         }else{
34989             tb = this.getBodyTable();
34990             index = colIndex - this.cm.getLockedCount();
34991         }
34992         if(tb && tb.rows){
34993             var rows = tb.rows;
34994             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34995             for(var i = 0; i < stopIndex; i++){
34996                 var cell = rows[i].childNodes[index].firstChild;
34997                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34998             }
34999         }
35000         return maxWidth + /*margin for error in IE*/ 5;
35001     },
35002     /**
35003      * Autofit a column to its content.
35004      * @param {Number} colIndex
35005      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35006      */
35007      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35008          if(this.cm.isHidden(colIndex)){
35009              return; // can't calc a hidden column
35010          }
35011         if(forceMinSize){
35012             var cid = this.cm.getColumnId(colIndex);
35013             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35014            if(this.grid.autoSizeHeaders){
35015                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35016            }
35017         }
35018         var newWidth = this.calcColumnWidth(colIndex);
35019         this.cm.setColumnWidth(colIndex,
35020             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35021         if(!suppressEvent){
35022             this.grid.fireEvent("columnresize", colIndex, newWidth);
35023         }
35024     },
35025
35026     /**
35027      * Autofits all columns to their content and then expands to fit any extra space in the grid
35028      */
35029      autoSizeColumns : function(){
35030         var cm = this.grid.colModel;
35031         var colCount = cm.getColumnCount();
35032         for(var i = 0; i < colCount; i++){
35033             this.autoSizeColumn(i, true, true);
35034         }
35035         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35036             this.fitColumns();
35037         }else{
35038             this.updateColumns();
35039             this.layout();
35040         }
35041     },
35042
35043     /**
35044      * Autofits all columns to the grid's width proportionate with their current size
35045      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35046      */
35047     fitColumns : function(reserveScrollSpace){
35048         var cm = this.grid.colModel;
35049         var colCount = cm.getColumnCount();
35050         var cols = [];
35051         var width = 0;
35052         var i, w;
35053         for (i = 0; i < colCount; i++){
35054             if(!cm.isHidden(i) && !cm.isFixed(i)){
35055                 w = cm.getColumnWidth(i);
35056                 cols.push(i);
35057                 cols.push(w);
35058                 width += w;
35059             }
35060         }
35061         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35062         if(reserveScrollSpace){
35063             avail -= 17;
35064         }
35065         var frac = (avail - cm.getTotalWidth())/width;
35066         while (cols.length){
35067             w = cols.pop();
35068             i = cols.pop();
35069             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35070         }
35071         this.updateColumns();
35072         this.layout();
35073     },
35074
35075     onRowSelect : function(rowIndex){
35076         var row = this.getRowComposite(rowIndex);
35077         row.addClass("x-grid-row-selected");
35078     },
35079
35080     onRowDeselect : function(rowIndex){
35081         var row = this.getRowComposite(rowIndex);
35082         row.removeClass("x-grid-row-selected");
35083     },
35084
35085     onCellSelect : function(row, col){
35086         var cell = this.getCell(row, col);
35087         if(cell){
35088             Roo.fly(cell).addClass("x-grid-cell-selected");
35089         }
35090     },
35091
35092     onCellDeselect : function(row, col){
35093         var cell = this.getCell(row, col);
35094         if(cell){
35095             Roo.fly(cell).removeClass("x-grid-cell-selected");
35096         }
35097     },
35098
35099     updateHeaderSortState : function(){
35100         
35101         // sort state can be single { field: xxx, direction : yyy}
35102         // or   { xxx=>ASC , yyy : DESC ..... }
35103         
35104         var mstate = {};
35105         if (!this.ds.multiSort) { 
35106             var state = this.ds.getSortState();
35107             if(!state){
35108                 return;
35109             }
35110             mstate[state.field] = state.direction;
35111             // FIXME... - this is not used here.. but might be elsewhere..
35112             this.sortState = state;
35113             
35114         } else {
35115             mstate = this.ds.sortToggle;
35116         }
35117         //remove existing sort classes..
35118         
35119         var sc = this.sortClasses;
35120         var hds = this.el.select(this.headerSelector).removeClass(sc);
35121         
35122         for(var f in mstate) {
35123         
35124             var sortColumn = this.cm.findColumnIndex(f);
35125             
35126             if(sortColumn != -1){
35127                 var sortDir = mstate[f];        
35128                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35129             }
35130         }
35131         
35132          
35133         
35134     },
35135
35136
35137     handleHeaderClick : function(g, index){
35138         if(this.headersDisabled){
35139             return;
35140         }
35141         var dm = g.dataSource, cm = g.colModel;
35142         if(!cm.isSortable(index)){
35143             return;
35144         }
35145         g.stopEditing();
35146         
35147         if (dm.multiSort) {
35148             // update the sortOrder
35149             var so = [];
35150             for(var i = 0; i < cm.config.length; i++ ) {
35151                 
35152                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35153                     continue; // dont' bother, it's not in sort list or being set.
35154                 }
35155                 
35156                 so.push(cm.config[i].dataIndex);
35157             };
35158             dm.sortOrder = so;
35159         }
35160         
35161         
35162         dm.sort(cm.getDataIndex(index));
35163     },
35164
35165
35166     destroy : function(){
35167         if(this.colMenu){
35168             this.colMenu.removeAll();
35169             Roo.menu.MenuMgr.unregister(this.colMenu);
35170             this.colMenu.getEl().remove();
35171             delete this.colMenu;
35172         }
35173         if(this.hmenu){
35174             this.hmenu.removeAll();
35175             Roo.menu.MenuMgr.unregister(this.hmenu);
35176             this.hmenu.getEl().remove();
35177             delete this.hmenu;
35178         }
35179         if(this.grid.enableColumnMove){
35180             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35181             if(dds){
35182                 for(var dd in dds){
35183                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35184                         var elid = dds[dd].dragElId;
35185                         dds[dd].unreg();
35186                         Roo.get(elid).remove();
35187                     } else if(dds[dd].config.isTarget){
35188                         dds[dd].proxyTop.remove();
35189                         dds[dd].proxyBottom.remove();
35190                         dds[dd].unreg();
35191                     }
35192                     if(Roo.dd.DDM.locationCache[dd]){
35193                         delete Roo.dd.DDM.locationCache[dd];
35194                     }
35195                 }
35196                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35197             }
35198         }
35199         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35200         this.bind(null, null);
35201         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35202     },
35203
35204     handleLockChange : function(){
35205         this.refresh(true);
35206     },
35207
35208     onDenyColumnLock : function(){
35209
35210     },
35211
35212     onDenyColumnHide : function(){
35213
35214     },
35215
35216     handleHdMenuClick : function(item){
35217         var index = this.hdCtxIndex;
35218         var cm = this.cm, ds = this.ds;
35219         switch(item.id){
35220             case "asc":
35221                 ds.sort(cm.getDataIndex(index), "ASC");
35222                 break;
35223             case "desc":
35224                 ds.sort(cm.getDataIndex(index), "DESC");
35225                 break;
35226             case "lock":
35227                 var lc = cm.getLockedCount();
35228                 if(cm.getColumnCount(true) <= lc+1){
35229                     this.onDenyColumnLock();
35230                     return;
35231                 }
35232                 if(lc != index){
35233                     cm.setLocked(index, true, true);
35234                     cm.moveColumn(index, lc);
35235                     this.grid.fireEvent("columnmove", index, lc);
35236                 }else{
35237                     cm.setLocked(index, true);
35238                 }
35239             break;
35240             case "unlock":
35241                 var lc = cm.getLockedCount();
35242                 if((lc-1) != index){
35243                     cm.setLocked(index, false, true);
35244                     cm.moveColumn(index, lc-1);
35245                     this.grid.fireEvent("columnmove", index, lc-1);
35246                 }else{
35247                     cm.setLocked(index, false);
35248                 }
35249             break;
35250             default:
35251                 index = cm.getIndexById(item.id.substr(4));
35252                 if(index != -1){
35253                     if(item.checked && cm.getColumnCount(true) <= 1){
35254                         this.onDenyColumnHide();
35255                         return false;
35256                     }
35257                     cm.setHidden(index, item.checked);
35258                 }
35259         }
35260         return true;
35261     },
35262
35263     beforeColMenuShow : function(){
35264         var cm = this.cm,  colCount = cm.getColumnCount();
35265         this.colMenu.removeAll();
35266         for(var i = 0; i < colCount; i++){
35267             this.colMenu.add(new Roo.menu.CheckItem({
35268                 id: "col-"+cm.getColumnId(i),
35269                 text: cm.getColumnHeader(i),
35270                 checked: !cm.isHidden(i),
35271                 hideOnClick:false
35272             }));
35273         }
35274     },
35275
35276     handleHdCtx : function(g, index, e){
35277         e.stopEvent();
35278         var hd = this.getHeaderCell(index);
35279         this.hdCtxIndex = index;
35280         var ms = this.hmenu.items, cm = this.cm;
35281         ms.get("asc").setDisabled(!cm.isSortable(index));
35282         ms.get("desc").setDisabled(!cm.isSortable(index));
35283         if(this.grid.enableColLock !== false){
35284             ms.get("lock").setDisabled(cm.isLocked(index));
35285             ms.get("unlock").setDisabled(!cm.isLocked(index));
35286         }
35287         this.hmenu.show(hd, "tl-bl");
35288     },
35289
35290     handleHdOver : function(e){
35291         var hd = this.findHeaderCell(e.getTarget());
35292         if(hd && !this.headersDisabled){
35293             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35294                this.fly(hd).addClass("x-grid-hd-over");
35295             }
35296         }
35297     },
35298
35299     handleHdOut : function(e){
35300         var hd = this.findHeaderCell(e.getTarget());
35301         if(hd){
35302             this.fly(hd).removeClass("x-grid-hd-over");
35303         }
35304     },
35305
35306     handleSplitDblClick : function(e, t){
35307         var i = this.getCellIndex(t);
35308         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35309             this.autoSizeColumn(i, true);
35310             this.layout();
35311         }
35312     },
35313
35314     render : function(){
35315
35316         var cm = this.cm;
35317         var colCount = cm.getColumnCount();
35318
35319         if(this.grid.monitorWindowResize === true){
35320             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35321         }
35322         var header = this.renderHeaders();
35323         var body = this.templates.body.apply({rows:""});
35324         var html = this.templates.master.apply({
35325             lockedBody: body,
35326             body: body,
35327             lockedHeader: header[0],
35328             header: header[1]
35329         });
35330
35331         //this.updateColumns();
35332
35333         this.grid.getGridEl().dom.innerHTML = html;
35334
35335         this.initElements();
35336         
35337         // a kludge to fix the random scolling effect in webkit
35338         this.el.on("scroll", function() {
35339             this.el.dom.scrollTop=0; // hopefully not recursive..
35340         },this);
35341
35342         this.scroller.on("scroll", this.handleScroll, this);
35343         this.lockedBody.on("mousewheel", this.handleWheel, this);
35344         this.mainBody.on("mousewheel", this.handleWheel, this);
35345
35346         this.mainHd.on("mouseover", this.handleHdOver, this);
35347         this.mainHd.on("mouseout", this.handleHdOut, this);
35348         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35349                 {delegate: "."+this.splitClass});
35350
35351         this.lockedHd.on("mouseover", this.handleHdOver, this);
35352         this.lockedHd.on("mouseout", this.handleHdOut, this);
35353         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35354                 {delegate: "."+this.splitClass});
35355
35356         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35357             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35358         }
35359
35360         this.updateSplitters();
35361
35362         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35363             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35364             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35365         }
35366
35367         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35368             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35369             this.hmenu.add(
35370                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35371                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35372             );
35373             if(this.grid.enableColLock !== false){
35374                 this.hmenu.add('-',
35375                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35376                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35377                 );
35378             }
35379             if(this.grid.enableColumnHide !== false){
35380
35381                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35382                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35383                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35384
35385                 this.hmenu.add('-',
35386                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35387                 );
35388             }
35389             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35390
35391             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35392         }
35393
35394         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35395             this.dd = new Roo.grid.GridDragZone(this.grid, {
35396                 ddGroup : this.grid.ddGroup || 'GridDD'
35397             });
35398         }
35399
35400         /*
35401         for(var i = 0; i < colCount; i++){
35402             if(cm.isHidden(i)){
35403                 this.hideColumn(i);
35404             }
35405             if(cm.config[i].align){
35406                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35407                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35408             }
35409         }*/
35410         
35411         this.updateHeaderSortState();
35412
35413         this.beforeInitialResize();
35414         this.layout(true);
35415
35416         // two part rendering gives faster view to the user
35417         this.renderPhase2.defer(1, this);
35418     },
35419
35420     renderPhase2 : function(){
35421         // render the rows now
35422         this.refresh();
35423         if(this.grid.autoSizeColumns){
35424             this.autoSizeColumns();
35425         }
35426     },
35427
35428     beforeInitialResize : function(){
35429
35430     },
35431
35432     onColumnSplitterMoved : function(i, w){
35433         this.userResized = true;
35434         var cm = this.grid.colModel;
35435         cm.setColumnWidth(i, w, true);
35436         var cid = cm.getColumnId(i);
35437         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35438         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35439         this.updateSplitters();
35440         this.layout();
35441         this.grid.fireEvent("columnresize", i, w);
35442     },
35443
35444     syncRowHeights : function(startIndex, endIndex){
35445         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35446             startIndex = startIndex || 0;
35447             var mrows = this.getBodyTable().rows;
35448             var lrows = this.getLockedTable().rows;
35449             var len = mrows.length-1;
35450             endIndex = Math.min(endIndex || len, len);
35451             for(var i = startIndex; i <= endIndex; i++){
35452                 var m = mrows[i], l = lrows[i];
35453                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35454                 m.style.height = l.style.height = h + "px";
35455             }
35456         }
35457     },
35458
35459     layout : function(initialRender, is2ndPass){
35460         var g = this.grid;
35461         var auto = g.autoHeight;
35462         var scrollOffset = 16;
35463         var c = g.getGridEl(), cm = this.cm,
35464                 expandCol = g.autoExpandColumn,
35465                 gv = this;
35466         //c.beginMeasure();
35467
35468         if(!c.dom.offsetWidth){ // display:none?
35469             if(initialRender){
35470                 this.lockedWrap.show();
35471                 this.mainWrap.show();
35472             }
35473             return;
35474         }
35475
35476         var hasLock = this.cm.isLocked(0);
35477
35478         var tbh = this.headerPanel.getHeight();
35479         var bbh = this.footerPanel.getHeight();
35480
35481         if(auto){
35482             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35483             var newHeight = ch + c.getBorderWidth("tb");
35484             if(g.maxHeight){
35485                 newHeight = Math.min(g.maxHeight, newHeight);
35486             }
35487             c.setHeight(newHeight);
35488         }
35489
35490         if(g.autoWidth){
35491             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35492         }
35493
35494         var s = this.scroller;
35495
35496         var csize = c.getSize(true);
35497
35498         this.el.setSize(csize.width, csize.height);
35499
35500         this.headerPanel.setWidth(csize.width);
35501         this.footerPanel.setWidth(csize.width);
35502
35503         var hdHeight = this.mainHd.getHeight();
35504         var vw = csize.width;
35505         var vh = csize.height - (tbh + bbh);
35506
35507         s.setSize(vw, vh);
35508
35509         var bt = this.getBodyTable();
35510         var ltWidth = hasLock ?
35511                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35512
35513         var scrollHeight = bt.offsetHeight;
35514         var scrollWidth = ltWidth + bt.offsetWidth;
35515         var vscroll = false, hscroll = false;
35516
35517         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35518
35519         var lw = this.lockedWrap, mw = this.mainWrap;
35520         var lb = this.lockedBody, mb = this.mainBody;
35521
35522         setTimeout(function(){
35523             var t = s.dom.offsetTop;
35524             var w = s.dom.clientWidth,
35525                 h = s.dom.clientHeight;
35526
35527             lw.setTop(t);
35528             lw.setSize(ltWidth, h);
35529
35530             mw.setLeftTop(ltWidth, t);
35531             mw.setSize(w-ltWidth, h);
35532
35533             lb.setHeight(h-hdHeight);
35534             mb.setHeight(h-hdHeight);
35535
35536             if(is2ndPass !== true && !gv.userResized && expandCol){
35537                 // high speed resize without full column calculation
35538                 
35539                 var ci = cm.getIndexById(expandCol);
35540                 if (ci < 0) {
35541                     ci = cm.findColumnIndex(expandCol);
35542                 }
35543                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35544                 var expandId = cm.getColumnId(ci);
35545                 var  tw = cm.getTotalWidth(false);
35546                 var currentWidth = cm.getColumnWidth(ci);
35547                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35548                 if(currentWidth != cw){
35549                     cm.setColumnWidth(ci, cw, true);
35550                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35551                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35552                     gv.updateSplitters();
35553                     gv.layout(false, true);
35554                 }
35555             }
35556
35557             if(initialRender){
35558                 lw.show();
35559                 mw.show();
35560             }
35561             //c.endMeasure();
35562         }, 10);
35563     },
35564
35565     onWindowResize : function(){
35566         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35567             return;
35568         }
35569         this.layout();
35570     },
35571
35572     appendFooter : function(parentEl){
35573         return null;
35574     },
35575
35576     sortAscText : "Sort Ascending",
35577     sortDescText : "Sort Descending",
35578     lockText : "Lock Column",
35579     unlockText : "Unlock Column",
35580     columnsText : "Columns"
35581 });
35582
35583
35584 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35585     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35586     this.proxy.el.addClass('x-grid3-col-dd');
35587 };
35588
35589 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35590     handleMouseDown : function(e){
35591
35592     },
35593
35594     callHandleMouseDown : function(e){
35595         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35596     }
35597 });
35598 /*
35599  * Based on:
35600  * Ext JS Library 1.1.1
35601  * Copyright(c) 2006-2007, Ext JS, LLC.
35602  *
35603  * Originally Released Under LGPL - original licence link has changed is not relivant.
35604  *
35605  * Fork - LGPL
35606  * <script type="text/javascript">
35607  */
35608  
35609 // private
35610 // This is a support class used internally by the Grid components
35611 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35612     this.grid = grid;
35613     this.view = grid.getView();
35614     this.proxy = this.view.resizeProxy;
35615     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35616         "gridSplitters" + this.grid.getGridEl().id, {
35617         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35618     });
35619     this.setHandleElId(Roo.id(hd));
35620     this.setOuterHandleElId(Roo.id(hd2));
35621     this.scroll = false;
35622 };
35623 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35624     fly: Roo.Element.fly,
35625
35626     b4StartDrag : function(x, y){
35627         this.view.headersDisabled = true;
35628         this.proxy.setHeight(this.view.mainWrap.getHeight());
35629         var w = this.cm.getColumnWidth(this.cellIndex);
35630         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35631         this.resetConstraints();
35632         this.setXConstraint(minw, 1000);
35633         this.setYConstraint(0, 0);
35634         this.minX = x - minw;
35635         this.maxX = x + 1000;
35636         this.startPos = x;
35637         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35638     },
35639
35640
35641     handleMouseDown : function(e){
35642         ev = Roo.EventObject.setEvent(e);
35643         var t = this.fly(ev.getTarget());
35644         if(t.hasClass("x-grid-split")){
35645             this.cellIndex = this.view.getCellIndex(t.dom);
35646             this.split = t.dom;
35647             this.cm = this.grid.colModel;
35648             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35649                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35650             }
35651         }
35652     },
35653
35654     endDrag : function(e){
35655         this.view.headersDisabled = false;
35656         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35657         var diff = endX - this.startPos;
35658         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35659     },
35660
35661     autoOffset : function(){
35662         this.setDelta(0,0);
35663     }
35664 });/*
35665  * Based on:
35666  * Ext JS Library 1.1.1
35667  * Copyright(c) 2006-2007, Ext JS, LLC.
35668  *
35669  * Originally Released Under LGPL - original licence link has changed is not relivant.
35670  *
35671  * Fork - LGPL
35672  * <script type="text/javascript">
35673  */
35674  
35675 // private
35676 // This is a support class used internally by the Grid components
35677 Roo.grid.GridDragZone = function(grid, config){
35678     this.view = grid.getView();
35679     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35680     if(this.view.lockedBody){
35681         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35682         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35683     }
35684     this.scroll = false;
35685     this.grid = grid;
35686     this.ddel = document.createElement('div');
35687     this.ddel.className = 'x-grid-dd-wrap';
35688 };
35689
35690 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35691     ddGroup : "GridDD",
35692
35693     getDragData : function(e){
35694         var t = Roo.lib.Event.getTarget(e);
35695         var rowIndex = this.view.findRowIndex(t);
35696         if(rowIndex !== false){
35697             var sm = this.grid.selModel;
35698             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35699               //  sm.mouseDown(e, t);
35700             //}
35701             if (e.hasModifier()){
35702                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35703             }
35704             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
35705         }
35706         return false;
35707     },
35708
35709     onInitDrag : function(e){
35710         var data = this.dragData;
35711         this.ddel.innerHTML = this.grid.getDragDropText();
35712         this.proxy.update(this.ddel);
35713         // fire start drag?
35714     },
35715
35716     afterRepair : function(){
35717         this.dragging = false;
35718     },
35719
35720     getRepairXY : function(e, data){
35721         return false;
35722     },
35723
35724     onEndDrag : function(data, e){
35725         // fire end drag?
35726     },
35727
35728     onValidDrop : function(dd, e, id){
35729         // fire drag drop?
35730         this.hideProxy();
35731     },
35732
35733     beforeInvalidDrop : function(e, id){
35734
35735     }
35736 });/*
35737  * Based on:
35738  * Ext JS Library 1.1.1
35739  * Copyright(c) 2006-2007, Ext JS, LLC.
35740  *
35741  * Originally Released Under LGPL - original licence link has changed is not relivant.
35742  *
35743  * Fork - LGPL
35744  * <script type="text/javascript">
35745  */
35746  
35747
35748 /**
35749  * @class Roo.grid.ColumnModel
35750  * @extends Roo.util.Observable
35751  * This is the default implementation of a ColumnModel used by the Grid. It defines
35752  * the columns in the grid.
35753  * <br>Usage:<br>
35754  <pre><code>
35755  var colModel = new Roo.grid.ColumnModel([
35756         {header: "Ticker", width: 60, sortable: true, locked: true},
35757         {header: "Company Name", width: 150, sortable: true},
35758         {header: "Market Cap.", width: 100, sortable: true},
35759         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35760         {header: "Employees", width: 100, sortable: true, resizable: false}
35761  ]);
35762  </code></pre>
35763  * <p>
35764  
35765  * The config options listed for this class are options which may appear in each
35766  * individual column definition.
35767  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35768  * @constructor
35769  * @param {Object} config An Array of column config objects. See this class's
35770  * config objects for details.
35771 */
35772 Roo.grid.ColumnModel = function(config){
35773         /**
35774      * The config passed into the constructor
35775      */
35776     this.config = config;
35777     this.lookup = {};
35778
35779     // if no id, create one
35780     // if the column does not have a dataIndex mapping,
35781     // map it to the order it is in the config
35782     for(var i = 0, len = config.length; i < len; i++){
35783         var c = config[i];
35784         if(typeof c.dataIndex == "undefined"){
35785             c.dataIndex = i;
35786         }
35787         if(typeof c.renderer == "string"){
35788             c.renderer = Roo.util.Format[c.renderer];
35789         }
35790         if(typeof c.id == "undefined"){
35791             c.id = Roo.id();
35792         }
35793         if(c.editor && c.editor.xtype){
35794             c.editor  = Roo.factory(c.editor, Roo.grid);
35795         }
35796         if(c.editor && c.editor.isFormField){
35797             c.editor = new Roo.grid.GridEditor(c.editor);
35798         }
35799         this.lookup[c.id] = c;
35800     }
35801
35802     /**
35803      * The width of columns which have no width specified (defaults to 100)
35804      * @type Number
35805      */
35806     this.defaultWidth = 100;
35807
35808     /**
35809      * Default sortable of columns which have no sortable specified (defaults to false)
35810      * @type Boolean
35811      */
35812     this.defaultSortable = false;
35813
35814     this.addEvents({
35815         /**
35816              * @event widthchange
35817              * Fires when the width of a column changes.
35818              * @param {ColumnModel} this
35819              * @param {Number} columnIndex The column index
35820              * @param {Number} newWidth The new width
35821              */
35822             "widthchange": true,
35823         /**
35824              * @event headerchange
35825              * Fires when the text of a header changes.
35826              * @param {ColumnModel} this
35827              * @param {Number} columnIndex The column index
35828              * @param {Number} newText The new header text
35829              */
35830             "headerchange": true,
35831         /**
35832              * @event hiddenchange
35833              * Fires when a column is hidden or "unhidden".
35834              * @param {ColumnModel} this
35835              * @param {Number} columnIndex The column index
35836              * @param {Boolean} hidden true if hidden, false otherwise
35837              */
35838             "hiddenchange": true,
35839             /**
35840          * @event columnmoved
35841          * Fires when a column is moved.
35842          * @param {ColumnModel} this
35843          * @param {Number} oldIndex
35844          * @param {Number} newIndex
35845          */
35846         "columnmoved" : true,
35847         /**
35848          * @event columlockchange
35849          * Fires when a column's locked state is changed
35850          * @param {ColumnModel} this
35851          * @param {Number} colIndex
35852          * @param {Boolean} locked true if locked
35853          */
35854         "columnlockchange" : true
35855     });
35856     Roo.grid.ColumnModel.superclass.constructor.call(this);
35857 };
35858 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35859     /**
35860      * @cfg {String} header The header text to display in the Grid view.
35861      */
35862     /**
35863      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35864      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35865      * specified, the column's index is used as an index into the Record's data Array.
35866      */
35867     /**
35868      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35869      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35870      */
35871     /**
35872      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35873      * Defaults to the value of the {@link #defaultSortable} property.
35874      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35875      */
35876     /**
35877      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35878      */
35879     /**
35880      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35881      */
35882     /**
35883      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35884      */
35885     /**
35886      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35887      */
35888     /**
35889      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35890      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35891      * default renderer uses the raw data value.
35892      */
35893        /**
35894      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35895      */
35896     /**
35897      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35898      */
35899
35900     /**
35901      * Returns the id of the column at the specified index.
35902      * @param {Number} index The column index
35903      * @return {String} the id
35904      */
35905     getColumnId : function(index){
35906         return this.config[index].id;
35907     },
35908
35909     /**
35910      * Returns the column for a specified id.
35911      * @param {String} id The column id
35912      * @return {Object} the column
35913      */
35914     getColumnById : function(id){
35915         return this.lookup[id];
35916     },
35917
35918     
35919     /**
35920      * Returns the column for a specified dataIndex.
35921      * @param {String} dataIndex The column dataIndex
35922      * @return {Object|Boolean} the column or false if not found
35923      */
35924     getColumnByDataIndex: function(dataIndex){
35925         var index = this.findColumnIndex(dataIndex);
35926         return index > -1 ? this.config[index] : false;
35927     },
35928     
35929     /**
35930      * Returns the index for a specified column id.
35931      * @param {String} id The column id
35932      * @return {Number} the index, or -1 if not found
35933      */
35934     getIndexById : function(id){
35935         for(var i = 0, len = this.config.length; i < len; i++){
35936             if(this.config[i].id == id){
35937                 return i;
35938             }
35939         }
35940         return -1;
35941     },
35942     
35943     /**
35944      * Returns the index for a specified column dataIndex.
35945      * @param {String} dataIndex The column dataIndex
35946      * @return {Number} the index, or -1 if not found
35947      */
35948     
35949     findColumnIndex : function(dataIndex){
35950         for(var i = 0, len = this.config.length; i < len; i++){
35951             if(this.config[i].dataIndex == dataIndex){
35952                 return i;
35953             }
35954         }
35955         return -1;
35956     },
35957     
35958     
35959     moveColumn : function(oldIndex, newIndex){
35960         var c = this.config[oldIndex];
35961         this.config.splice(oldIndex, 1);
35962         this.config.splice(newIndex, 0, c);
35963         this.dataMap = null;
35964         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35965     },
35966
35967     isLocked : function(colIndex){
35968         return this.config[colIndex].locked === true;
35969     },
35970
35971     setLocked : function(colIndex, value, suppressEvent){
35972         if(this.isLocked(colIndex) == value){
35973             return;
35974         }
35975         this.config[colIndex].locked = value;
35976         if(!suppressEvent){
35977             this.fireEvent("columnlockchange", this, colIndex, value);
35978         }
35979     },
35980
35981     getTotalLockedWidth : function(){
35982         var totalWidth = 0;
35983         for(var i = 0; i < this.config.length; i++){
35984             if(this.isLocked(i) && !this.isHidden(i)){
35985                 this.totalWidth += this.getColumnWidth(i);
35986             }
35987         }
35988         return totalWidth;
35989     },
35990
35991     getLockedCount : function(){
35992         for(var i = 0, len = this.config.length; i < len; i++){
35993             if(!this.isLocked(i)){
35994                 return i;
35995             }
35996         }
35997     },
35998
35999     /**
36000      * Returns the number of columns.
36001      * @return {Number}
36002      */
36003     getColumnCount : function(visibleOnly){
36004         if(visibleOnly === true){
36005             var c = 0;
36006             for(var i = 0, len = this.config.length; i < len; i++){
36007                 if(!this.isHidden(i)){
36008                     c++;
36009                 }
36010             }
36011             return c;
36012         }
36013         return this.config.length;
36014     },
36015
36016     /**
36017      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36018      * @param {Function} fn
36019      * @param {Object} scope (optional)
36020      * @return {Array} result
36021      */
36022     getColumnsBy : function(fn, scope){
36023         var r = [];
36024         for(var i = 0, len = this.config.length; i < len; i++){
36025             var c = this.config[i];
36026             if(fn.call(scope||this, c, i) === true){
36027                 r[r.length] = c;
36028             }
36029         }
36030         return r;
36031     },
36032
36033     /**
36034      * Returns true if the specified column is sortable.
36035      * @param {Number} col The column index
36036      * @return {Boolean}
36037      */
36038     isSortable : function(col){
36039         if(typeof this.config[col].sortable == "undefined"){
36040             return this.defaultSortable;
36041         }
36042         return this.config[col].sortable;
36043     },
36044
36045     /**
36046      * Returns the rendering (formatting) function defined for the column.
36047      * @param {Number} col The column index.
36048      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36049      */
36050     getRenderer : function(col){
36051         if(!this.config[col].renderer){
36052             return Roo.grid.ColumnModel.defaultRenderer;
36053         }
36054         return this.config[col].renderer;
36055     },
36056
36057     /**
36058      * Sets the rendering (formatting) function for a column.
36059      * @param {Number} col The column index
36060      * @param {Function} fn The function to use to process the cell's raw data
36061      * to return HTML markup for the grid view. The render function is called with
36062      * the following parameters:<ul>
36063      * <li>Data value.</li>
36064      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36065      * <li>css A CSS style string to apply to the table cell.</li>
36066      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36067      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36068      * <li>Row index</li>
36069      * <li>Column index</li>
36070      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36071      */
36072     setRenderer : function(col, fn){
36073         this.config[col].renderer = fn;
36074     },
36075
36076     /**
36077      * Returns the width for the specified column.
36078      * @param {Number} col The column index
36079      * @return {Number}
36080      */
36081     getColumnWidth : function(col){
36082         return this.config[col].width * 1 || this.defaultWidth;
36083     },
36084
36085     /**
36086      * Sets the width for a column.
36087      * @param {Number} col The column index
36088      * @param {Number} width The new width
36089      */
36090     setColumnWidth : function(col, width, suppressEvent){
36091         this.config[col].width = width;
36092         this.totalWidth = null;
36093         if(!suppressEvent){
36094              this.fireEvent("widthchange", this, col, width);
36095         }
36096     },
36097
36098     /**
36099      * Returns the total width of all columns.
36100      * @param {Boolean} includeHidden True to include hidden column widths
36101      * @return {Number}
36102      */
36103     getTotalWidth : function(includeHidden){
36104         if(!this.totalWidth){
36105             this.totalWidth = 0;
36106             for(var i = 0, len = this.config.length; i < len; i++){
36107                 if(includeHidden || !this.isHidden(i)){
36108                     this.totalWidth += this.getColumnWidth(i);
36109                 }
36110             }
36111         }
36112         return this.totalWidth;
36113     },
36114
36115     /**
36116      * Returns the header for the specified column.
36117      * @param {Number} col The column index
36118      * @return {String}
36119      */
36120     getColumnHeader : function(col){
36121         return this.config[col].header;
36122     },
36123
36124     /**
36125      * Sets the header for a column.
36126      * @param {Number} col The column index
36127      * @param {String} header The new header
36128      */
36129     setColumnHeader : function(col, header){
36130         this.config[col].header = header;
36131         this.fireEvent("headerchange", this, col, header);
36132     },
36133
36134     /**
36135      * Returns the tooltip for the specified column.
36136      * @param {Number} col The column index
36137      * @return {String}
36138      */
36139     getColumnTooltip : function(col){
36140             return this.config[col].tooltip;
36141     },
36142     /**
36143      * Sets the tooltip for a column.
36144      * @param {Number} col The column index
36145      * @param {String} tooltip The new tooltip
36146      */
36147     setColumnTooltip : function(col, tooltip){
36148             this.config[col].tooltip = tooltip;
36149     },
36150
36151     /**
36152      * Returns the dataIndex for the specified column.
36153      * @param {Number} col The column index
36154      * @return {Number}
36155      */
36156     getDataIndex : function(col){
36157         return this.config[col].dataIndex;
36158     },
36159
36160     /**
36161      * Sets the dataIndex for a column.
36162      * @param {Number} col The column index
36163      * @param {Number} dataIndex The new dataIndex
36164      */
36165     setDataIndex : function(col, dataIndex){
36166         this.config[col].dataIndex = dataIndex;
36167     },
36168
36169     
36170     
36171     /**
36172      * Returns true if the cell is editable.
36173      * @param {Number} colIndex The column index
36174      * @param {Number} rowIndex The row index
36175      * @return {Boolean}
36176      */
36177     isCellEditable : function(colIndex, rowIndex){
36178         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36179     },
36180
36181     /**
36182      * Returns the editor defined for the cell/column.
36183      * return false or null to disable editing.
36184      * @param {Number} colIndex The column index
36185      * @param {Number} rowIndex The row index
36186      * @return {Object}
36187      */
36188     getCellEditor : function(colIndex, rowIndex){
36189         return this.config[colIndex].editor;
36190     },
36191
36192     /**
36193      * Sets if a column is editable.
36194      * @param {Number} col The column index
36195      * @param {Boolean} editable True if the column is editable
36196      */
36197     setEditable : function(col, editable){
36198         this.config[col].editable = editable;
36199     },
36200
36201
36202     /**
36203      * Returns true if the column is hidden.
36204      * @param {Number} colIndex The column index
36205      * @return {Boolean}
36206      */
36207     isHidden : function(colIndex){
36208         return this.config[colIndex].hidden;
36209     },
36210
36211
36212     /**
36213      * Returns true if the column width cannot be changed
36214      */
36215     isFixed : function(colIndex){
36216         return this.config[colIndex].fixed;
36217     },
36218
36219     /**
36220      * Returns true if the column can be resized
36221      * @return {Boolean}
36222      */
36223     isResizable : function(colIndex){
36224         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36225     },
36226     /**
36227      * Sets if a column is hidden.
36228      * @param {Number} colIndex The column index
36229      * @param {Boolean} hidden True if the column is hidden
36230      */
36231     setHidden : function(colIndex, hidden){
36232         this.config[colIndex].hidden = hidden;
36233         this.totalWidth = null;
36234         this.fireEvent("hiddenchange", this, colIndex, hidden);
36235     },
36236
36237     /**
36238      * Sets the editor for a column.
36239      * @param {Number} col The column index
36240      * @param {Object} editor The editor object
36241      */
36242     setEditor : function(col, editor){
36243         this.config[col].editor = editor;
36244     }
36245 });
36246
36247 Roo.grid.ColumnModel.defaultRenderer = function(value){
36248         if(typeof value == "string" && value.length < 1){
36249             return "&#160;";
36250         }
36251         return value;
36252 };
36253
36254 // Alias for backwards compatibility
36255 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36256 /*
36257  * Based on:
36258  * Ext JS Library 1.1.1
36259  * Copyright(c) 2006-2007, Ext JS, LLC.
36260  *
36261  * Originally Released Under LGPL - original licence link has changed is not relivant.
36262  *
36263  * Fork - LGPL
36264  * <script type="text/javascript">
36265  */
36266
36267 /**
36268  * @class Roo.grid.AbstractSelectionModel
36269  * @extends Roo.util.Observable
36270  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36271  * implemented by descendant classes.  This class should not be directly instantiated.
36272  * @constructor
36273  */
36274 Roo.grid.AbstractSelectionModel = function(){
36275     this.locked = false;
36276     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36277 };
36278
36279 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36280     /** @ignore Called by the grid automatically. Do not call directly. */
36281     init : function(grid){
36282         this.grid = grid;
36283         this.initEvents();
36284     },
36285
36286     /**
36287      * Locks the selections.
36288      */
36289     lock : function(){
36290         this.locked = true;
36291     },
36292
36293     /**
36294      * Unlocks the selections.
36295      */
36296     unlock : function(){
36297         this.locked = false;
36298     },
36299
36300     /**
36301      * Returns true if the selections are locked.
36302      * @return {Boolean}
36303      */
36304     isLocked : function(){
36305         return this.locked;
36306     }
36307 });/*
36308  * Based on:
36309  * Ext JS Library 1.1.1
36310  * Copyright(c) 2006-2007, Ext JS, LLC.
36311  *
36312  * Originally Released Under LGPL - original licence link has changed is not relivant.
36313  *
36314  * Fork - LGPL
36315  * <script type="text/javascript">
36316  */
36317 /**
36318  * @extends Roo.grid.AbstractSelectionModel
36319  * @class Roo.grid.RowSelectionModel
36320  * The default SelectionModel used by {@link Roo.grid.Grid}.
36321  * It supports multiple selections and keyboard selection/navigation. 
36322  * @constructor
36323  * @param {Object} config
36324  */
36325 Roo.grid.RowSelectionModel = function(config){
36326     Roo.apply(this, config);
36327     this.selections = new Roo.util.MixedCollection(false, function(o){
36328         return o.id;
36329     });
36330
36331     this.last = false;
36332     this.lastActive = false;
36333
36334     this.addEvents({
36335         /**
36336              * @event selectionchange
36337              * Fires when the selection changes
36338              * @param {SelectionModel} this
36339              */
36340             "selectionchange" : true,
36341         /**
36342              * @event afterselectionchange
36343              * Fires after the selection changes (eg. by key press or clicking)
36344              * @param {SelectionModel} this
36345              */
36346             "afterselectionchange" : true,
36347         /**
36348              * @event beforerowselect
36349              * Fires when a row is selected being selected, return false to cancel.
36350              * @param {SelectionModel} this
36351              * @param {Number} rowIndex The selected index
36352              * @param {Boolean} keepExisting False if other selections will be cleared
36353              */
36354             "beforerowselect" : true,
36355         /**
36356              * @event rowselect
36357              * Fires when a row is selected.
36358              * @param {SelectionModel} this
36359              * @param {Number} rowIndex The selected index
36360              * @param {Roo.data.Record} r The record
36361              */
36362             "rowselect" : true,
36363         /**
36364              * @event rowdeselect
36365              * Fires when a row is deselected.
36366              * @param {SelectionModel} this
36367              * @param {Number} rowIndex The selected index
36368              */
36369         "rowdeselect" : true
36370     });
36371     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36372     this.locked = false;
36373 };
36374
36375 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36376     /**
36377      * @cfg {Boolean} singleSelect
36378      * True to allow selection of only one row at a time (defaults to false)
36379      */
36380     singleSelect : false,
36381
36382     // private
36383     initEvents : function(){
36384
36385         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36386             this.grid.on("mousedown", this.handleMouseDown, this);
36387         }else{ // allow click to work like normal
36388             this.grid.on("rowclick", this.handleDragableRowClick, this);
36389         }
36390
36391         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36392             "up" : function(e){
36393                 if(!e.shiftKey){
36394                     this.selectPrevious(e.shiftKey);
36395                 }else if(this.last !== false && this.lastActive !== false){
36396                     var last = this.last;
36397                     this.selectRange(this.last,  this.lastActive-1);
36398                     this.grid.getView().focusRow(this.lastActive);
36399                     if(last !== false){
36400                         this.last = last;
36401                     }
36402                 }else{
36403                     this.selectFirstRow();
36404                 }
36405                 this.fireEvent("afterselectionchange", this);
36406             },
36407             "down" : function(e){
36408                 if(!e.shiftKey){
36409                     this.selectNext(e.shiftKey);
36410                 }else if(this.last !== false && this.lastActive !== false){
36411                     var last = this.last;
36412                     this.selectRange(this.last,  this.lastActive+1);
36413                     this.grid.getView().focusRow(this.lastActive);
36414                     if(last !== false){
36415                         this.last = last;
36416                     }
36417                 }else{
36418                     this.selectFirstRow();
36419                 }
36420                 this.fireEvent("afterselectionchange", this);
36421             },
36422             scope: this
36423         });
36424
36425         var view = this.grid.view;
36426         view.on("refresh", this.onRefresh, this);
36427         view.on("rowupdated", this.onRowUpdated, this);
36428         view.on("rowremoved", this.onRemove, this);
36429     },
36430
36431     // private
36432     onRefresh : function(){
36433         var ds = this.grid.dataSource, i, v = this.grid.view;
36434         var s = this.selections;
36435         s.each(function(r){
36436             if((i = ds.indexOfId(r.id)) != -1){
36437                 v.onRowSelect(i);
36438             }else{
36439                 s.remove(r);
36440             }
36441         });
36442     },
36443
36444     // private
36445     onRemove : function(v, index, r){
36446         this.selections.remove(r);
36447     },
36448
36449     // private
36450     onRowUpdated : function(v, index, r){
36451         if(this.isSelected(r)){
36452             v.onRowSelect(index);
36453         }
36454     },
36455
36456     /**
36457      * Select records.
36458      * @param {Array} records The records to select
36459      * @param {Boolean} keepExisting (optional) True to keep existing selections
36460      */
36461     selectRecords : function(records, keepExisting){
36462         if(!keepExisting){
36463             this.clearSelections();
36464         }
36465         var ds = this.grid.dataSource;
36466         for(var i = 0, len = records.length; i < len; i++){
36467             this.selectRow(ds.indexOf(records[i]), true);
36468         }
36469     },
36470
36471     /**
36472      * Gets the number of selected rows.
36473      * @return {Number}
36474      */
36475     getCount : function(){
36476         return this.selections.length;
36477     },
36478
36479     /**
36480      * Selects the first row in the grid.
36481      */
36482     selectFirstRow : function(){
36483         this.selectRow(0);
36484     },
36485
36486     /**
36487      * Select the last row.
36488      * @param {Boolean} keepExisting (optional) True to keep existing selections
36489      */
36490     selectLastRow : function(keepExisting){
36491         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36492     },
36493
36494     /**
36495      * Selects the row immediately following the last selected row.
36496      * @param {Boolean} keepExisting (optional) True to keep existing selections
36497      */
36498     selectNext : function(keepExisting){
36499         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36500             this.selectRow(this.last+1, keepExisting);
36501             this.grid.getView().focusRow(this.last);
36502         }
36503     },
36504
36505     /**
36506      * Selects the row that precedes the last selected row.
36507      * @param {Boolean} keepExisting (optional) True to keep existing selections
36508      */
36509     selectPrevious : function(keepExisting){
36510         if(this.last){
36511             this.selectRow(this.last-1, keepExisting);
36512             this.grid.getView().focusRow(this.last);
36513         }
36514     },
36515
36516     /**
36517      * Returns the selected records
36518      * @return {Array} Array of selected records
36519      */
36520     getSelections : function(){
36521         return [].concat(this.selections.items);
36522     },
36523
36524     /**
36525      * Returns the first selected record.
36526      * @return {Record}
36527      */
36528     getSelected : function(){
36529         return this.selections.itemAt(0);
36530     },
36531
36532
36533     /**
36534      * Clears all selections.
36535      */
36536     clearSelections : function(fast){
36537         if(this.locked) return;
36538         if(fast !== true){
36539             var ds = this.grid.dataSource;
36540             var s = this.selections;
36541             s.each(function(r){
36542                 this.deselectRow(ds.indexOfId(r.id));
36543             }, this);
36544             s.clear();
36545         }else{
36546             this.selections.clear();
36547         }
36548         this.last = false;
36549     },
36550
36551
36552     /**
36553      * Selects all rows.
36554      */
36555     selectAll : function(){
36556         if(this.locked) return;
36557         this.selections.clear();
36558         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36559             this.selectRow(i, true);
36560         }
36561     },
36562
36563     /**
36564      * Returns True if there is a selection.
36565      * @return {Boolean}
36566      */
36567     hasSelection : function(){
36568         return this.selections.length > 0;
36569     },
36570
36571     /**
36572      * Returns True if the specified row is selected.
36573      * @param {Number/Record} record The record or index of the record to check
36574      * @return {Boolean}
36575      */
36576     isSelected : function(index){
36577         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36578         return (r && this.selections.key(r.id) ? true : false);
36579     },
36580
36581     /**
36582      * Returns True if the specified record id is selected.
36583      * @param {String} id The id of record to check
36584      * @return {Boolean}
36585      */
36586     isIdSelected : function(id){
36587         return (this.selections.key(id) ? true : false);
36588     },
36589
36590     // private
36591     handleMouseDown : function(e, t){
36592         var view = this.grid.getView(), rowIndex;
36593         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36594             return;
36595         };
36596         if(e.shiftKey && this.last !== false){
36597             var last = this.last;
36598             this.selectRange(last, rowIndex, e.ctrlKey);
36599             this.last = last; // reset the last
36600             view.focusRow(rowIndex);
36601         }else{
36602             var isSelected = this.isSelected(rowIndex);
36603             if(e.button !== 0 && isSelected){
36604                 view.focusRow(rowIndex);
36605             }else if(e.ctrlKey && isSelected){
36606                 this.deselectRow(rowIndex);
36607             }else if(!isSelected){
36608                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36609                 view.focusRow(rowIndex);
36610             }
36611         }
36612         this.fireEvent("afterselectionchange", this);
36613     },
36614     // private
36615     handleDragableRowClick :  function(grid, rowIndex, e) 
36616     {
36617         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36618             this.selectRow(rowIndex, false);
36619             grid.view.focusRow(rowIndex);
36620              this.fireEvent("afterselectionchange", this);
36621         }
36622     },
36623     
36624     /**
36625      * Selects multiple rows.
36626      * @param {Array} rows Array of the indexes of the row to select
36627      * @param {Boolean} keepExisting (optional) True to keep existing selections
36628      */
36629     selectRows : function(rows, keepExisting){
36630         if(!keepExisting){
36631             this.clearSelections();
36632         }
36633         for(var i = 0, len = rows.length; i < len; i++){
36634             this.selectRow(rows[i], true);
36635         }
36636     },
36637
36638     /**
36639      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36640      * @param {Number} startRow The index of the first row in the range
36641      * @param {Number} endRow The index of the last row in the range
36642      * @param {Boolean} keepExisting (optional) True to retain existing selections
36643      */
36644     selectRange : function(startRow, endRow, keepExisting){
36645         if(this.locked) return;
36646         if(!keepExisting){
36647             this.clearSelections();
36648         }
36649         if(startRow <= endRow){
36650             for(var i = startRow; i <= endRow; i++){
36651                 this.selectRow(i, true);
36652             }
36653         }else{
36654             for(var i = startRow; i >= endRow; i--){
36655                 this.selectRow(i, true);
36656             }
36657         }
36658     },
36659
36660     /**
36661      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36662      * @param {Number} startRow The index of the first row in the range
36663      * @param {Number} endRow The index of the last row in the range
36664      */
36665     deselectRange : function(startRow, endRow, preventViewNotify){
36666         if(this.locked) return;
36667         for(var i = startRow; i <= endRow; i++){
36668             this.deselectRow(i, preventViewNotify);
36669         }
36670     },
36671
36672     /**
36673      * Selects a row.
36674      * @param {Number} row The index of the row to select
36675      * @param {Boolean} keepExisting (optional) True to keep existing selections
36676      */
36677     selectRow : function(index, keepExisting, preventViewNotify){
36678         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
36679         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36680             if(!keepExisting || this.singleSelect){
36681                 this.clearSelections();
36682             }
36683             var r = this.grid.dataSource.getAt(index);
36684             this.selections.add(r);
36685             this.last = this.lastActive = index;
36686             if(!preventViewNotify){
36687                 this.grid.getView().onRowSelect(index);
36688             }
36689             this.fireEvent("rowselect", this, index, r);
36690             this.fireEvent("selectionchange", this);
36691         }
36692     },
36693
36694     /**
36695      * Deselects a row.
36696      * @param {Number} row The index of the row to deselect
36697      */
36698     deselectRow : function(index, preventViewNotify){
36699         if(this.locked) return;
36700         if(this.last == index){
36701             this.last = false;
36702         }
36703         if(this.lastActive == index){
36704             this.lastActive = false;
36705         }
36706         var r = this.grid.dataSource.getAt(index);
36707         this.selections.remove(r);
36708         if(!preventViewNotify){
36709             this.grid.getView().onRowDeselect(index);
36710         }
36711         this.fireEvent("rowdeselect", this, index);
36712         this.fireEvent("selectionchange", this);
36713     },
36714
36715     // private
36716     restoreLast : function(){
36717         if(this._last){
36718             this.last = this._last;
36719         }
36720     },
36721
36722     // private
36723     acceptsNav : function(row, col, cm){
36724         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36725     },
36726
36727     // private
36728     onEditorKey : function(field, e){
36729         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36730         if(k == e.TAB){
36731             e.stopEvent();
36732             ed.completeEdit();
36733             if(e.shiftKey){
36734                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36735             }else{
36736                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36737             }
36738         }else if(k == e.ENTER && !e.ctrlKey){
36739             e.stopEvent();
36740             ed.completeEdit();
36741             if(e.shiftKey){
36742                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36743             }else{
36744                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36745             }
36746         }else if(k == e.ESC){
36747             ed.cancelEdit();
36748         }
36749         if(newCell){
36750             g.startEditing(newCell[0], newCell[1]);
36751         }
36752     }
36753 });/*
36754  * Based on:
36755  * Ext JS Library 1.1.1
36756  * Copyright(c) 2006-2007, Ext JS, LLC.
36757  *
36758  * Originally Released Under LGPL - original licence link has changed is not relivant.
36759  *
36760  * Fork - LGPL
36761  * <script type="text/javascript">
36762  */
36763 /**
36764  * @class Roo.grid.CellSelectionModel
36765  * @extends Roo.grid.AbstractSelectionModel
36766  * This class provides the basic implementation for cell selection in a grid.
36767  * @constructor
36768  * @param {Object} config The object containing the configuration of this model.
36769  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36770  */
36771 Roo.grid.CellSelectionModel = function(config){
36772     Roo.apply(this, config);
36773
36774     this.selection = null;
36775
36776     this.addEvents({
36777         /**
36778              * @event beforerowselect
36779              * Fires before a cell is selected.
36780              * @param {SelectionModel} this
36781              * @param {Number} rowIndex The selected row index
36782              * @param {Number} colIndex The selected cell index
36783              */
36784             "beforecellselect" : true,
36785         /**
36786              * @event cellselect
36787              * Fires when a cell is selected.
36788              * @param {SelectionModel} this
36789              * @param {Number} rowIndex The selected row index
36790              * @param {Number} colIndex The selected cell index
36791              */
36792             "cellselect" : true,
36793         /**
36794              * @event selectionchange
36795              * Fires when the active selection changes.
36796              * @param {SelectionModel} this
36797              * @param {Object} selection null for no selection or an object (o) with two properties
36798                 <ul>
36799                 <li>o.record: the record object for the row the selection is in</li>
36800                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36801                 </ul>
36802              */
36803             "selectionchange" : true,
36804         /**
36805              * @event tabend
36806              * Fires when the tab (or enter) was pressed on the last editable cell
36807              * You can use this to trigger add new row.
36808              * @param {SelectionModel} this
36809              */
36810             "tabend" : true
36811     });
36812     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36813 };
36814
36815 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36816     
36817     enter_is_tab: false,
36818
36819     /** @ignore */
36820     initEvents : function(){
36821         this.grid.on("mousedown", this.handleMouseDown, this);
36822         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36823         var view = this.grid.view;
36824         view.on("refresh", this.onViewChange, this);
36825         view.on("rowupdated", this.onRowUpdated, this);
36826         view.on("beforerowremoved", this.clearSelections, this);
36827         view.on("beforerowsinserted", this.clearSelections, this);
36828         if(this.grid.isEditor){
36829             this.grid.on("beforeedit", this.beforeEdit,  this);
36830         }
36831     },
36832
36833         //private
36834     beforeEdit : function(e){
36835         this.select(e.row, e.column, false, true, e.record);
36836     },
36837
36838         //private
36839     onRowUpdated : function(v, index, r){
36840         if(this.selection && this.selection.record == r){
36841             v.onCellSelect(index, this.selection.cell[1]);
36842         }
36843     },
36844
36845         //private
36846     onViewChange : function(){
36847         this.clearSelections(true);
36848     },
36849
36850         /**
36851          * Returns the currently selected cell,.
36852          * @return {Array} The selected cell (row, column) or null if none selected.
36853          */
36854     getSelectedCell : function(){
36855         return this.selection ? this.selection.cell : null;
36856     },
36857
36858     /**
36859      * Clears all selections.
36860      * @param {Boolean} true to prevent the gridview from being notified about the change.
36861      */
36862     clearSelections : function(preventNotify){
36863         var s = this.selection;
36864         if(s){
36865             if(preventNotify !== true){
36866                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36867             }
36868             this.selection = null;
36869             this.fireEvent("selectionchange", this, null);
36870         }
36871     },
36872
36873     /**
36874      * Returns true if there is a selection.
36875      * @return {Boolean}
36876      */
36877     hasSelection : function(){
36878         return this.selection ? true : false;
36879     },
36880
36881     /** @ignore */
36882     handleMouseDown : function(e, t){
36883         var v = this.grid.getView();
36884         if(this.isLocked()){
36885             return;
36886         };
36887         var row = v.findRowIndex(t);
36888         var cell = v.findCellIndex(t);
36889         if(row !== false && cell !== false){
36890             this.select(row, cell);
36891         }
36892     },
36893
36894     /**
36895      * Selects a cell.
36896      * @param {Number} rowIndex
36897      * @param {Number} collIndex
36898      */
36899     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36900         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36901             this.clearSelections();
36902             r = r || this.grid.dataSource.getAt(rowIndex);
36903             this.selection = {
36904                 record : r,
36905                 cell : [rowIndex, colIndex]
36906             };
36907             if(!preventViewNotify){
36908                 var v = this.grid.getView();
36909                 v.onCellSelect(rowIndex, colIndex);
36910                 if(preventFocus !== true){
36911                     v.focusCell(rowIndex, colIndex);
36912                 }
36913             }
36914             this.fireEvent("cellselect", this, rowIndex, colIndex);
36915             this.fireEvent("selectionchange", this, this.selection);
36916         }
36917     },
36918
36919         //private
36920     isSelectable : function(rowIndex, colIndex, cm){
36921         return !cm.isHidden(colIndex);
36922     },
36923
36924     /** @ignore */
36925     handleKeyDown : function(e){
36926         //Roo.log('Cell Sel Model handleKeyDown');
36927         if(!e.isNavKeyPress()){
36928             return;
36929         }
36930         var g = this.grid, s = this.selection;
36931         if(!s){
36932             e.stopEvent();
36933             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36934             if(cell){
36935                 this.select(cell[0], cell[1]);
36936             }
36937             return;
36938         }
36939         var sm = this;
36940         var walk = function(row, col, step){
36941             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36942         };
36943         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36944         var newCell;
36945
36946       
36947
36948         switch(k){
36949             case e.TAB:
36950                 // handled by onEditorKey
36951                 if (g.isEditor && g.editing) {
36952                     return;
36953                 }
36954                 if(e.shiftKey) {
36955                     newCell = walk(r, c-1, -1);
36956                 } else {
36957                     newCell = walk(r, c+1, 1);
36958                 }
36959                 break;
36960             
36961             case e.DOWN:
36962                newCell = walk(r+1, c, 1);
36963                 break;
36964             
36965             case e.UP:
36966                 newCell = walk(r-1, c, -1);
36967                 break;
36968             
36969             case e.RIGHT:
36970                 newCell = walk(r, c+1, 1);
36971                 break;
36972             
36973             case e.LEFT:
36974                 newCell = walk(r, c-1, -1);
36975                 break;
36976             
36977             case e.ENTER:
36978                 
36979                 if(g.isEditor && !g.editing){
36980                    g.startEditing(r, c);
36981                    e.stopEvent();
36982                    return;
36983                 }
36984                 
36985                 
36986              break;
36987         };
36988         if(newCell){
36989             this.select(newCell[0], newCell[1]);
36990             e.stopEvent();
36991             
36992         }
36993     },
36994
36995     acceptsNav : function(row, col, cm){
36996         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36997     },
36998     /**
36999      * Selects a cell.
37000      * @param {Number} field (not used) - as it's normally used as a listener
37001      * @param {Number} e - event - fake it by using
37002      *
37003      * var e = Roo.EventObjectImpl.prototype;
37004      * e.keyCode = e.TAB
37005      *
37006      * 
37007      */
37008     onEditorKey : function(field, e){
37009         
37010         var k = e.getKey(),
37011             newCell,
37012             g = this.grid,
37013             ed = g.activeEditor,
37014             forward = false;
37015         ///Roo.log('onEditorKey' + k);
37016         
37017         
37018         if (this.enter_is_tab && k == e.ENTER) {
37019             k = e.TAB;
37020         }
37021         
37022         if(k == e.TAB){
37023             if(e.shiftKey){
37024                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37025             }else{
37026                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37027                 forward = true;
37028             }
37029             
37030             e.stopEvent();
37031             
37032         }else if(k == e.ENTER &&  !e.ctrlKey){
37033             ed.completeEdit();
37034             e.stopEvent();
37035             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37036         }else if(k == e.ESC){
37037             ed.cancelEdit();
37038         }
37039         
37040         
37041         if(newCell){
37042             //Roo.log('next cell after edit');
37043             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37044         } else if (forward) {
37045             // tabbed past last
37046             this.fireEvent.defer(100, this, ['tabend',this]);
37047         }
37048     }
37049 });/*
37050  * Based on:
37051  * Ext JS Library 1.1.1
37052  * Copyright(c) 2006-2007, Ext JS, LLC.
37053  *
37054  * Originally Released Under LGPL - original licence link has changed is not relivant.
37055  *
37056  * Fork - LGPL
37057  * <script type="text/javascript">
37058  */
37059  
37060 /**
37061  * @class Roo.grid.EditorGrid
37062  * @extends Roo.grid.Grid
37063  * Class for creating and editable grid.
37064  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37065  * The container MUST have some type of size defined for the grid to fill. The container will be 
37066  * automatically set to position relative if it isn't already.
37067  * @param {Object} dataSource The data model to bind to
37068  * @param {Object} colModel The column model with info about this grid's columns
37069  */
37070 Roo.grid.EditorGrid = function(container, config){
37071     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37072     this.getGridEl().addClass("xedit-grid");
37073
37074     if(!this.selModel){
37075         this.selModel = new Roo.grid.CellSelectionModel();
37076     }
37077
37078     this.activeEditor = null;
37079
37080         this.addEvents({
37081             /**
37082              * @event beforeedit
37083              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37084              * <ul style="padding:5px;padding-left:16px;">
37085              * <li>grid - This grid</li>
37086              * <li>record - The record being edited</li>
37087              * <li>field - The field name being edited</li>
37088              * <li>value - The value for the field being edited.</li>
37089              * <li>row - The grid row index</li>
37090              * <li>column - The grid column index</li>
37091              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37092              * </ul>
37093              * @param {Object} e An edit event (see above for description)
37094              */
37095             "beforeedit" : true,
37096             /**
37097              * @event afteredit
37098              * Fires after a cell is edited. <br />
37099              * <ul style="padding:5px;padding-left:16px;">
37100              * <li>grid - This grid</li>
37101              * <li>record - The record being edited</li>
37102              * <li>field - The field name being edited</li>
37103              * <li>value - The value being set</li>
37104              * <li>originalValue - The original value for the field, before the edit.</li>
37105              * <li>row - The grid row index</li>
37106              * <li>column - The grid column index</li>
37107              * </ul>
37108              * @param {Object} e An edit event (see above for description)
37109              */
37110             "afteredit" : true,
37111             /**
37112              * @event validateedit
37113              * Fires after a cell is edited, but before the value is set in the record. 
37114          * You can use this to modify the value being set in the field, Return false
37115              * to cancel the change. The edit event object has the following properties <br />
37116              * <ul style="padding:5px;padding-left:16px;">
37117          * <li>editor - This editor</li>
37118              * <li>grid - This grid</li>
37119              * <li>record - The record being edited</li>
37120              * <li>field - The field name being edited</li>
37121              * <li>value - The value being set</li>
37122              * <li>originalValue - The original value for the field, before the edit.</li>
37123              * <li>row - The grid row index</li>
37124              * <li>column - The grid column index</li>
37125              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37126              * </ul>
37127              * @param {Object} e An edit event (see above for description)
37128              */
37129             "validateedit" : true
37130         });
37131     this.on("bodyscroll", this.stopEditing,  this);
37132     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37133 };
37134
37135 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37136     /**
37137      * @cfg {Number} clicksToEdit
37138      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37139      */
37140     clicksToEdit: 2,
37141
37142     // private
37143     isEditor : true,
37144     // private
37145     trackMouseOver: false, // causes very odd FF errors
37146
37147     onCellDblClick : function(g, row, col){
37148         this.startEditing(row, col);
37149     },
37150
37151     onEditComplete : function(ed, value, startValue){
37152         this.editing = false;
37153         this.activeEditor = null;
37154         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37155         var r = ed.record;
37156         var field = this.colModel.getDataIndex(ed.col);
37157         var e = {
37158             grid: this,
37159             record: r,
37160             field: field,
37161             originalValue: startValue,
37162             value: value,
37163             row: ed.row,
37164             column: ed.col,
37165             cancel:false,
37166             editor: ed
37167         };
37168         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37169         cell.show();
37170           
37171         if(String(value) !== String(startValue)){
37172             
37173             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37174                 r.set(field, e.value);
37175                 // if we are dealing with a combo box..
37176                 // then we also set the 'name' colum to be the displayField
37177                 if (ed.field.displayField && ed.field.name) {
37178                     r.set(ed.field.name, ed.field.el.dom.value);
37179                 }
37180                 
37181                 delete e.cancel; //?? why!!!
37182                 this.fireEvent("afteredit", e);
37183             }
37184         } else {
37185             this.fireEvent("afteredit", e); // always fire it!
37186         }
37187         this.view.focusCell(ed.row, ed.col);
37188     },
37189
37190     /**
37191      * Starts editing the specified for the specified row/column
37192      * @param {Number} rowIndex
37193      * @param {Number} colIndex
37194      */
37195     startEditing : function(row, col){
37196         this.stopEditing();
37197         if(this.colModel.isCellEditable(col, row)){
37198             this.view.ensureVisible(row, col, true);
37199           
37200             var r = this.dataSource.getAt(row);
37201             var field = this.colModel.getDataIndex(col);
37202             var cell = Roo.get(this.view.getCell(row,col));
37203             var e = {
37204                 grid: this,
37205                 record: r,
37206                 field: field,
37207                 value: r.data[field],
37208                 row: row,
37209                 column: col,
37210                 cancel:false 
37211             };
37212             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37213                 this.editing = true;
37214                 var ed = this.colModel.getCellEditor(col, row);
37215                 
37216                 if (!ed) {
37217                     return;
37218                 }
37219                 if(!ed.rendered){
37220                     ed.render(ed.parentEl || document.body);
37221                 }
37222                 ed.field.reset();
37223                
37224                 cell.hide();
37225                 
37226                 (function(){ // complex but required for focus issues in safari, ie and opera
37227                     ed.row = row;
37228                     ed.col = col;
37229                     ed.record = r;
37230                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37231                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37232                     this.activeEditor = ed;
37233                     var v = r.data[field];
37234                     ed.startEdit(this.view.getCell(row, col), v);
37235                     // combo's with 'displayField and name set
37236                     if (ed.field.displayField && ed.field.name) {
37237                         ed.field.el.dom.value = r.data[ed.field.name];
37238                     }
37239                     
37240                     
37241                 }).defer(50, this);
37242             }
37243         }
37244     },
37245         
37246     /**
37247      * Stops any active editing
37248      */
37249     stopEditing : function(){
37250         if(this.activeEditor){
37251             this.activeEditor.completeEdit();
37252         }
37253         this.activeEditor = null;
37254     }
37255 });/*
37256  * Based on:
37257  * Ext JS Library 1.1.1
37258  * Copyright(c) 2006-2007, Ext JS, LLC.
37259  *
37260  * Originally Released Under LGPL - original licence link has changed is not relivant.
37261  *
37262  * Fork - LGPL
37263  * <script type="text/javascript">
37264  */
37265
37266 // private - not really -- you end up using it !
37267 // This is a support class used internally by the Grid components
37268
37269 /**
37270  * @class Roo.grid.GridEditor
37271  * @extends Roo.Editor
37272  * Class for creating and editable grid elements.
37273  * @param {Object} config any settings (must include field)
37274  */
37275 Roo.grid.GridEditor = function(field, config){
37276     if (!config && field.field) {
37277         config = field;
37278         field = Roo.factory(config.field, Roo.form);
37279     }
37280     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37281     field.monitorTab = false;
37282 };
37283
37284 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37285     
37286     /**
37287      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37288      */
37289     
37290     alignment: "tl-tl",
37291     autoSize: "width",
37292     hideEl : false,
37293     cls: "x-small-editor x-grid-editor",
37294     shim:false,
37295     shadow:"frame"
37296 });/*
37297  * Based on:
37298  * Ext JS Library 1.1.1
37299  * Copyright(c) 2006-2007, Ext JS, LLC.
37300  *
37301  * Originally Released Under LGPL - original licence link has changed is not relivant.
37302  *
37303  * Fork - LGPL
37304  * <script type="text/javascript">
37305  */
37306   
37307
37308   
37309 Roo.grid.PropertyRecord = Roo.data.Record.create([
37310     {name:'name',type:'string'},  'value'
37311 ]);
37312
37313
37314 Roo.grid.PropertyStore = function(grid, source){
37315     this.grid = grid;
37316     this.store = new Roo.data.Store({
37317         recordType : Roo.grid.PropertyRecord
37318     });
37319     this.store.on('update', this.onUpdate,  this);
37320     if(source){
37321         this.setSource(source);
37322     }
37323     Roo.grid.PropertyStore.superclass.constructor.call(this);
37324 };
37325
37326
37327
37328 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37329     setSource : function(o){
37330         this.source = o;
37331         this.store.removeAll();
37332         var data = [];
37333         for(var k in o){
37334             if(this.isEditableValue(o[k])){
37335                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37336             }
37337         }
37338         this.store.loadRecords({records: data}, {}, true);
37339     },
37340
37341     onUpdate : function(ds, record, type){
37342         if(type == Roo.data.Record.EDIT){
37343             var v = record.data['value'];
37344             var oldValue = record.modified['value'];
37345             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37346                 this.source[record.id] = v;
37347                 record.commit();
37348                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37349             }else{
37350                 record.reject();
37351             }
37352         }
37353     },
37354
37355     getProperty : function(row){
37356        return this.store.getAt(row);
37357     },
37358
37359     isEditableValue: function(val){
37360         if(val && val instanceof Date){
37361             return true;
37362         }else if(typeof val == 'object' || typeof val == 'function'){
37363             return false;
37364         }
37365         return true;
37366     },
37367
37368     setValue : function(prop, value){
37369         this.source[prop] = value;
37370         this.store.getById(prop).set('value', value);
37371     },
37372
37373     getSource : function(){
37374         return this.source;
37375     }
37376 });
37377
37378 Roo.grid.PropertyColumnModel = function(grid, store){
37379     this.grid = grid;
37380     var g = Roo.grid;
37381     g.PropertyColumnModel.superclass.constructor.call(this, [
37382         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37383         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37384     ]);
37385     this.store = store;
37386     this.bselect = Roo.DomHelper.append(document.body, {
37387         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37388             {tag: 'option', value: 'true', html: 'true'},
37389             {tag: 'option', value: 'false', html: 'false'}
37390         ]
37391     });
37392     Roo.id(this.bselect);
37393     var f = Roo.form;
37394     this.editors = {
37395         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37396         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37397         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37398         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37399         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37400     };
37401     this.renderCellDelegate = this.renderCell.createDelegate(this);
37402     this.renderPropDelegate = this.renderProp.createDelegate(this);
37403 };
37404
37405 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37406     
37407     
37408     nameText : 'Name',
37409     valueText : 'Value',
37410     
37411     dateFormat : 'm/j/Y',
37412     
37413     
37414     renderDate : function(dateVal){
37415         return dateVal.dateFormat(this.dateFormat);
37416     },
37417
37418     renderBool : function(bVal){
37419         return bVal ? 'true' : 'false';
37420     },
37421
37422     isCellEditable : function(colIndex, rowIndex){
37423         return colIndex == 1;
37424     },
37425
37426     getRenderer : function(col){
37427         return col == 1 ?
37428             this.renderCellDelegate : this.renderPropDelegate;
37429     },
37430
37431     renderProp : function(v){
37432         return this.getPropertyName(v);
37433     },
37434
37435     renderCell : function(val){
37436         var rv = val;
37437         if(val instanceof Date){
37438             rv = this.renderDate(val);
37439         }else if(typeof val == 'boolean'){
37440             rv = this.renderBool(val);
37441         }
37442         return Roo.util.Format.htmlEncode(rv);
37443     },
37444
37445     getPropertyName : function(name){
37446         var pn = this.grid.propertyNames;
37447         return pn && pn[name] ? pn[name] : name;
37448     },
37449
37450     getCellEditor : function(colIndex, rowIndex){
37451         var p = this.store.getProperty(rowIndex);
37452         var n = p.data['name'], val = p.data['value'];
37453         
37454         if(typeof(this.grid.customEditors[n]) == 'string'){
37455             return this.editors[this.grid.customEditors[n]];
37456         }
37457         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37458             return this.grid.customEditors[n];
37459         }
37460         if(val instanceof Date){
37461             return this.editors['date'];
37462         }else if(typeof val == 'number'){
37463             return this.editors['number'];
37464         }else if(typeof val == 'boolean'){
37465             return this.editors['boolean'];
37466         }else{
37467             return this.editors['string'];
37468         }
37469     }
37470 });
37471
37472 /**
37473  * @class Roo.grid.PropertyGrid
37474  * @extends Roo.grid.EditorGrid
37475  * This class represents the  interface of a component based property grid control.
37476  * <br><br>Usage:<pre><code>
37477  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37478       
37479  });
37480  // set any options
37481  grid.render();
37482  * </code></pre>
37483   
37484  * @constructor
37485  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37486  * The container MUST have some type of size defined for the grid to fill. The container will be
37487  * automatically set to position relative if it isn't already.
37488  * @param {Object} config A config object that sets properties on this grid.
37489  */
37490 Roo.grid.PropertyGrid = function(container, config){
37491     config = config || {};
37492     var store = new Roo.grid.PropertyStore(this);
37493     this.store = store;
37494     var cm = new Roo.grid.PropertyColumnModel(this, store);
37495     store.store.sort('name', 'ASC');
37496     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37497         ds: store.store,
37498         cm: cm,
37499         enableColLock:false,
37500         enableColumnMove:false,
37501         stripeRows:false,
37502         trackMouseOver: false,
37503         clicksToEdit:1
37504     }, config));
37505     this.getGridEl().addClass('x-props-grid');
37506     this.lastEditRow = null;
37507     this.on('columnresize', this.onColumnResize, this);
37508     this.addEvents({
37509          /**
37510              * @event beforepropertychange
37511              * Fires before a property changes (return false to stop?)
37512              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37513              * @param {String} id Record Id
37514              * @param {String} newval New Value
37515          * @param {String} oldval Old Value
37516              */
37517         "beforepropertychange": true,
37518         /**
37519              * @event propertychange
37520              * Fires after a property changes
37521              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37522              * @param {String} id Record Id
37523              * @param {String} newval New Value
37524          * @param {String} oldval Old Value
37525              */
37526         "propertychange": true
37527     });
37528     this.customEditors = this.customEditors || {};
37529 };
37530 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37531     
37532      /**
37533      * @cfg {Object} customEditors map of colnames=> custom editors.
37534      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37535      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37536      * false disables editing of the field.
37537          */
37538     
37539       /**
37540      * @cfg {Object} propertyNames map of property Names to their displayed value
37541          */
37542     
37543     render : function(){
37544         Roo.grid.PropertyGrid.superclass.render.call(this);
37545         this.autoSize.defer(100, this);
37546     },
37547
37548     autoSize : function(){
37549         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37550         if(this.view){
37551             this.view.fitColumns();
37552         }
37553     },
37554
37555     onColumnResize : function(){
37556         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37557         this.autoSize();
37558     },
37559     /**
37560      * Sets the data for the Grid
37561      * accepts a Key => Value object of all the elements avaiable.
37562      * @param {Object} data  to appear in grid.
37563      */
37564     setSource : function(source){
37565         this.store.setSource(source);
37566         //this.autoSize();
37567     },
37568     /**
37569      * Gets all the data from the grid.
37570      * @return {Object} data  data stored in grid
37571      */
37572     getSource : function(){
37573         return this.store.getSource();
37574     }
37575 });/*
37576  * Based on:
37577  * Ext JS Library 1.1.1
37578  * Copyright(c) 2006-2007, Ext JS, LLC.
37579  *
37580  * Originally Released Under LGPL - original licence link has changed is not relivant.
37581  *
37582  * Fork - LGPL
37583  * <script type="text/javascript">
37584  */
37585  
37586 /**
37587  * @class Roo.LoadMask
37588  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37589  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37590  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37591  * element's UpdateManager load indicator and will be destroyed after the initial load.
37592  * @constructor
37593  * Create a new LoadMask
37594  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37595  * @param {Object} config The config object
37596  */
37597 Roo.LoadMask = function(el, config){
37598     this.el = Roo.get(el);
37599     Roo.apply(this, config);
37600     if(this.store){
37601         this.store.on('beforeload', this.onBeforeLoad, this);
37602         this.store.on('load', this.onLoad, this);
37603         this.store.on('loadexception', this.onLoadException, this);
37604         this.removeMask = false;
37605     }else{
37606         var um = this.el.getUpdateManager();
37607         um.showLoadIndicator = false; // disable the default indicator
37608         um.on('beforeupdate', this.onBeforeLoad, this);
37609         um.on('update', this.onLoad, this);
37610         um.on('failure', this.onLoad, this);
37611         this.removeMask = true;
37612     }
37613 };
37614
37615 Roo.LoadMask.prototype = {
37616     /**
37617      * @cfg {Boolean} removeMask
37618      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37619      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37620      */
37621     /**
37622      * @cfg {String} msg
37623      * The text to display in a centered loading message box (defaults to 'Loading...')
37624      */
37625     msg : 'Loading...',
37626     /**
37627      * @cfg {String} msgCls
37628      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37629      */
37630     msgCls : 'x-mask-loading',
37631
37632     /**
37633      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37634      * @type Boolean
37635      */
37636     disabled: false,
37637
37638     /**
37639      * Disables the mask to prevent it from being displayed
37640      */
37641     disable : function(){
37642        this.disabled = true;
37643     },
37644
37645     /**
37646      * Enables the mask so that it can be displayed
37647      */
37648     enable : function(){
37649         this.disabled = false;
37650     },
37651     
37652     onLoadException : function()
37653     {
37654         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37655             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37656         }
37657         this.el.unmask(this.removeMask);
37658     },
37659     // private
37660     onLoad : function()
37661     {
37662         this.el.unmask(this.removeMask);
37663     },
37664
37665     // private
37666     onBeforeLoad : function(){
37667         if(!this.disabled){
37668             this.el.mask(this.msg, this.msgCls);
37669         }
37670     },
37671
37672     // private
37673     destroy : function(){
37674         if(this.store){
37675             this.store.un('beforeload', this.onBeforeLoad, this);
37676             this.store.un('load', this.onLoad, this);
37677             this.store.un('loadexception', this.onLoadException, this);
37678         }else{
37679             var um = this.el.getUpdateManager();
37680             um.un('beforeupdate', this.onBeforeLoad, this);
37681             um.un('update', this.onLoad, this);
37682             um.un('failure', this.onLoad, this);
37683         }
37684     }
37685 };/*
37686  * Based on:
37687  * Ext JS Library 1.1.1
37688  * Copyright(c) 2006-2007, Ext JS, LLC.
37689  *
37690  * Originally Released Under LGPL - original licence link has changed is not relivant.
37691  *
37692  * Fork - LGPL
37693  * <script type="text/javascript">
37694  */
37695 Roo.XTemplate = function(){
37696     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37697     var s = this.html;
37698
37699     s = ['<tpl>', s, '</tpl>'].join('');
37700
37701     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
37702
37703     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
37704     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
37705     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
37706     var m, id = 0;
37707     var tpls = [];
37708
37709     while(m = s.match(re)){
37710        var m2 = m[0].match(nameRe);
37711        var m3 = m[0].match(ifRe);
37712        var m4 = m[0].match(execRe);
37713        var exp = null, fn = null, exec = null;
37714        var name = m2 && m2[1] ? m2[1] : '';
37715        if(m3){
37716            exp = m3 && m3[1] ? m3[1] : null;
37717            if(exp){
37718                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
37719            }
37720        }
37721        if(m4){
37722            exp = m4 && m4[1] ? m4[1] : null;
37723            if(exp){
37724                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
37725            }
37726        }
37727        if(name){
37728            switch(name){
37729                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
37730                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
37731                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
37732            }
37733        }
37734        tpls.push({
37735             id: id,
37736             target: name,
37737             exec: exec,
37738             test: fn,
37739             body: m[1]||''
37740         });
37741        s = s.replace(m[0], '{xtpl'+ id + '}');
37742        ++id;
37743     }
37744     for(var i = tpls.length-1; i >= 0; --i){
37745         this.compileTpl(tpls[i]);
37746     }
37747     this.master = tpls[tpls.length-1];
37748     this.tpls = tpls;
37749 };
37750 Roo.extend(Roo.XTemplate, Roo.Template, {
37751
37752     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37753
37754     applySubTemplate : function(id, values, parent){
37755         var t = this.tpls[id];
37756         if(t.test && !t.test.call(this, values, parent)){
37757             return '';
37758         }
37759         if(t.exec && t.exec.call(this, values, parent)){
37760             return '';
37761         }
37762         var vs = t.target ? t.target.call(this, values, parent) : values;
37763         parent = t.target ? values : parent;
37764         if(t.target && vs instanceof Array){
37765             var buf = [];
37766             for(var i = 0, len = vs.length; i < len; i++){
37767                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
37768             }
37769             return buf.join('');
37770         }
37771         return t.compiled.call(this, vs, parent);
37772     },
37773
37774     compileTpl : function(tpl){
37775         var fm = Roo.util.Format;
37776         var useF = this.disableFormats !== true;
37777         var sep = Roo.isGecko ? "+" : ",";
37778         var fn = function(m, name, format, args){
37779             if(name.substr(0, 4) == 'xtpl'){
37780                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
37781             }
37782             var v;
37783             if(name.indexOf('.') != -1){
37784                 v = name;
37785             }else{
37786                 v = "values['" + name + "']";
37787             }
37788             if(format && useF){
37789                 args = args ? ',' + args : "";
37790                 if(format.substr(0, 5) != "this."){
37791                     format = "fm." + format + '(';
37792                 }else{
37793                     format = 'this.call("'+ format.substr(5) + '", ';
37794                     args = ", values";
37795                 }
37796             }else{
37797                 args= ''; format = "("+v+" === undefined ? '' : ";
37798             }
37799             return "'"+ sep + format + v + args + ")"+sep+"'";
37800         };
37801         var body;
37802         // branched to use + in gecko and [].join() in others
37803         if(Roo.isGecko){
37804             body = "tpl.compiled = function(values, parent){ return '" +
37805                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
37806                     "';};";
37807         }else{
37808             body = ["tpl.compiled = function(values, parent){ return ['"];
37809             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
37810             body.push("'].join('');};");
37811             body = body.join('');
37812         }
37813         /** eval:var:zzzzzzz */
37814         eval(body);
37815         return this;
37816     },
37817
37818     applyTemplate : function(values){
37819         return this.master.compiled.call(this, values, {});
37820         var s = this.subs;
37821     },
37822
37823     apply : function(){
37824         return this.applyTemplate.apply(this, arguments);
37825     },
37826
37827     compile : function(){return this;}
37828 });
37829
37830 Roo.XTemplate.from = function(el){
37831     el = Roo.getDom(el);
37832     return new Roo.XTemplate(el.value || el.innerHTML);
37833 };/*
37834  * Original code for Roojs - LGPL
37835  * <script type="text/javascript">
37836  */
37837  
37838 /**
37839  * @class Roo.XComponent
37840  * A delayed Element creator...
37841  * Or a way to group chunks of interface together.
37842  * 
37843  * Mypart.xyx = new Roo.XComponent({
37844
37845     parent : 'Mypart.xyz', // empty == document.element.!!
37846     order : '001',
37847     name : 'xxxx'
37848     region : 'xxxx'
37849     disabled : function() {} 
37850      
37851     tree : function() { // return an tree of xtype declared components
37852         var MODULE = this;
37853         return 
37854         {
37855             xtype : 'NestedLayoutPanel',
37856             // technicall
37857         }
37858      ]
37859  *})
37860  *
37861  *
37862  * It can be used to build a big heiracy, with parent etc.
37863  * or you can just use this to render a single compoent to a dom element
37864  * MYPART.render(Roo.Element | String(id) | dom_element )
37865  * 
37866  * @extends Roo.util.Observable
37867  * @constructor
37868  * @param cfg {Object} configuration of component
37869  * 
37870  */
37871 Roo.XComponent = function(cfg) {
37872     Roo.apply(this, cfg);
37873     this.addEvents({ 
37874         /**
37875              * @event built
37876              * Fires when this the componnt is built
37877              * @param {Roo.XComponent} c the component
37878              */
37879         'built' : true,
37880         /**
37881              * @event buildcomplete
37882              * Fires on the top level element when all elements have been built
37883              * @param {Roo.XComponent} c the top level component.
37884          */
37885         'buildcomplete' : true
37886         
37887     });
37888     this.region = this.region || 'center'; // default..
37889     Roo.XComponent.register(this);
37890     this.modules = false;
37891     this.el = false; // where the layout goes..
37892     
37893     
37894 }
37895 Roo.extend(Roo.XComponent, Roo.util.Observable, {
37896     /**
37897      * @property el
37898      * The created element (with Roo.factory())
37899      * @type {Roo.Layout}
37900      */
37901     el  : false,
37902     
37903     /**
37904      * @property el
37905      * for BC  - use el in new code
37906      * @type {Roo.Layout}
37907      */
37908     panel : false,
37909     
37910     /**
37911      * @property layout
37912      * for BC  - use el in new code
37913      * @type {Roo.Layout}
37914      */
37915     layout : false,
37916     
37917      /**
37918      * @cfg {Function|boolean} disabled
37919      * If this module is disabled by some rule, return true from the funtion
37920      */
37921     disabled : false,
37922     
37923     /**
37924      * @cfg {String} parent 
37925      * Name of parent element which it get xtype added to..
37926      */
37927     parent: false,
37928     
37929     /**
37930      * @cfg {String} order
37931      * Used to set the order in which elements are created (usefull for multiple tabs)
37932      */
37933     
37934     order : false,
37935     /**
37936      * @cfg {String} name
37937      * String to display while loading.
37938      */
37939     name : false,
37940     /**
37941      * @cfg {String} region
37942      * Region to render component to (defaults to center)
37943      */
37944     region : 'center',
37945     
37946     /**
37947      * @cfg {Array} items
37948      * A single item array - the first element is the root of the tree..
37949      * It's done this way to stay compatible with the Xtype system...
37950      */
37951     items : false,
37952     
37953     
37954      /**
37955      * render
37956      * render element to dom or tree
37957      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
37958      */
37959     
37960     render : function(el)
37961     {
37962         
37963         el = el || false;
37964         var hp = this.parent ? 1 : 0;
37965         
37966         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
37967             // if parent is a '#.....' string, then let's use that..
37968             var ename = this.parent.substr(1)
37969             this.parent = false;
37970             el = Roo.get(ename);
37971             if (!el) {
37972                 Roo.log("Warning - element can not be found :#" + ename );
37973                 return;
37974             }
37975         }
37976         
37977         
37978         if (!this.parent) {
37979             
37980             el = el ? Roo.get(el) : false;
37981             
37982             // it's a top level one..
37983             this.parent =  {
37984                 el : new Roo.BorderLayout(el || document.body, {
37985                 
37986                      center: {
37987                          titlebar: false,
37988                          autoScroll:false,
37989                          closeOnTab: true,
37990                          tabPosition: 'top',
37991                           //resizeTabs: true,
37992                          alwaysShowTabs: el && hp? false :  true,
37993                          hideTabs: el || !hp ? true :  false,
37994                          minTabWidth: 140
37995                      }
37996                  })
37997             }
37998         }
37999         
38000         
38001             
38002         var tree = this.tree();
38003         tree.region = tree.region || this.region;
38004         this.el = this.parent.el.addxtype(tree);
38005         this.fireEvent('built', this);
38006         
38007         this.panel = this.el;
38008         this.layout = this.panel.layout;    
38009          
38010     }
38011     
38012 });
38013
38014 Roo.apply(Roo.XComponent, {
38015     
38016     /**
38017      * @property  buildCompleted
38018      * True when the builder has completed building the interface.
38019      * @type Boolean
38020      */
38021     buildCompleted : false,
38022      
38023     /**
38024      * @property  topModule
38025      * the upper most module - uses document.element as it's constructor.
38026      * @type Object
38027      */
38028      
38029     topModule  : false,
38030       
38031     /**
38032      * @property  modules
38033      * array of modules to be created by registration system.
38034      * @type {Array} of Roo.XComponent
38035      */
38036     
38037     modules : [],
38038     /**
38039      * @property  elmodules
38040      * array of modules to be created by which use #ID 
38041      * @type {Array} of Roo.XComponent
38042      */
38043      
38044     elmodules : [],
38045
38046     
38047     /**
38048      * Register components to be built later.
38049      *
38050      * This solves the following issues
38051      * - Building is not done on page load, but after an authentication process has occured.
38052      * - Interface elements are registered on page load
38053      * - Parent Interface elements may not be loaded before child, so this handles that..
38054      * 
38055      *
38056      * example:
38057      * 
38058      * MyApp.register({
38059           order : '000001',
38060           module : 'Pman.Tab.projectMgr',
38061           region : 'center',
38062           parent : 'Pman.layout',
38063           disabled : false,  // or use a function..
38064         })
38065      
38066      * * @param {Object} details about module
38067      */
38068     register : function(obj) {
38069         this.modules.push(obj);
38070          
38071     },
38072     /**
38073      * convert a string to an object..
38074      * eg. 'AAA.BBB' -> finds AAA.BBB
38075
38076      */
38077     
38078     toObject : function(str)
38079     {
38080         if (!str || typeof(str) == 'object') {
38081             return str;
38082         }
38083         if (str.substring(0,1) == '#') {
38084             return str;
38085         }
38086
38087         var ar = str.split('.');
38088         var rt, o;
38089         rt = ar.shift();
38090             /** eval:var:o */
38091         try {
38092             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38093         } catch (e) {
38094             throw "Module not found : " + str;
38095         }
38096         
38097         if (o === false) {
38098             throw "Module not found : " + str;
38099         }
38100         Roo.each(ar, function(e) {
38101             if (typeof(o[e]) == 'undefined') {
38102                 throw "Module not found : " + str;
38103             }
38104             o = o[e];
38105         });
38106         
38107         return o;
38108         
38109     },
38110     
38111     
38112     /**
38113      * move modules into their correct place in the tree..
38114      * 
38115      */
38116     preBuild : function ()
38117     {
38118         var _t = this;
38119         Roo.each(this.modules , function (obj)
38120         {
38121             var opar = obj.parent;
38122             try { 
38123                 obj.parent = this.toObject(opar);
38124             } catch(e) {
38125                 Roo.log(e.toString());
38126                 return;
38127             }
38128             
38129             if (!obj.parent) {
38130                 this.topModule = obj;
38131                 return;
38132             }
38133             if (typeof(obj.parent) == 'string') {
38134                 this.elmodules.push(obj);
38135                 return;
38136             }
38137             if (obj.parent.constructor != Roo.XComponent) {
38138                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
38139             }
38140             if (!obj.parent.modules) {
38141                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38142                     function(o) { return o.order + '' }
38143                 );
38144             }
38145             
38146             obj.parent.modules.add(obj);
38147         }, this);
38148     },
38149     
38150      /**
38151      * make a list of modules to build.
38152      * @return {Array} list of modules. 
38153      */ 
38154     
38155     buildOrder : function()
38156     {
38157         var _this = this;
38158         var cmp = function(a,b) {   
38159             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38160         };
38161         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38162             throw "No top level modules to build";
38163         }
38164         
38165         // make a flat list in order of modules to build.
38166         var mods = this.topModule ? [ this.topModule ] : [];
38167         Roo.each(this.elmodules,function(e) { mods.push(e) });
38168
38169         
38170         // add modules to their parents..
38171         var addMod = function(m) {
38172            // Roo.debug && Roo.log(m.modKey);
38173             
38174             mods.push(m);
38175             if (m.modules) {
38176                 m.modules.keySort('ASC',  cmp );
38177                 m.modules.each(addMod);
38178             }
38179             // not sure if this is used any more..
38180             if (m.finalize) {
38181                 m.finalize.name = m.name + " (clean up) ";
38182                 mods.push(m.finalize);
38183             }
38184             
38185         }
38186         if (this.topModule) { 
38187             this.topModule.modules.keySort('ASC',  cmp );
38188             this.topModule.modules.each(addMod);
38189         }
38190         return mods;
38191     },
38192     
38193      /**
38194      * Build the registered modules.
38195      * @param {Object} parent element.
38196      * @param {Function} optional method to call after module has been added.
38197      * 
38198      */ 
38199    
38200     build : function() 
38201     {
38202         
38203         this.preBuild();
38204         var mods = this.buildOrder();
38205       
38206         //this.allmods = mods;
38207         //Roo.debug && Roo.log(mods);
38208         //return;
38209         if (!mods.length) { // should not happen
38210             throw "NO modules!!!";
38211         }
38212         
38213         
38214         
38215         // flash it up as modal - so we store the mask!?
38216         Roo.MessageBox.show({ title: 'loading' });
38217         Roo.MessageBox.show({
38218            title: "Please wait...",
38219            msg: "Building Interface...",
38220            width:450,
38221            progress:true,
38222            closable:false,
38223            modal: false
38224           
38225         });
38226         var total = mods.length;
38227         
38228         var _this = this;
38229         var progressRun = function() {
38230             if (!mods.length) {
38231                 Roo.debug && Roo.log('hide?');
38232                 Roo.MessageBox.hide();
38233                 if (_this.topModule) { 
38234                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
38235                 }
38236                 // THE END...
38237                 return false;   
38238             }
38239             
38240             var m = mods.shift();
38241             
38242             
38243             Roo.debug && Roo.log(m);
38244             // not sure if this is supported any more.. - modules that are are just function
38245             if (typeof(m) == 'function') { 
38246                 m.call(this);
38247                 return progressRun.defer(10, _this);
38248             } 
38249             
38250             
38251             
38252             Roo.MessageBox.updateProgress(
38253                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
38254                     " of " + total + 
38255                     (m.name ? (' - ' + m.name) : '')
38256                     );
38257             
38258          
38259             // is the module disabled?
38260             var disabled = (typeof(m.disabled) == 'function') ?
38261                 m.disabled.call(m.module.disabled) : m.disabled;    
38262             
38263             
38264             if (disabled) {
38265                 return progressRun(); // we do not update the display!
38266             }
38267             
38268             // now build 
38269             
38270             m.render();
38271             // it's 10 on top level, and 1 on others??? why...
38272             return progressRun.defer(10, _this);
38273              
38274         }
38275         progressRun.defer(1, _this);
38276      
38277         
38278         
38279     }
38280     
38281      
38282    
38283     
38284     
38285 });
38286  //<script type="text/javascript">
38287
38288
38289 /**
38290  * @class Roo.Login
38291  * @extends Roo.LayoutDialog
38292  * A generic Login Dialog..... - only one needed in theory!?!?
38293  *
38294  * Fires XComponent builder on success...
38295  * 
38296  * Sends 
38297  *    username,password, lang = for login actions.
38298  *    check = 1 for periodic checking that sesion is valid.
38299  *    passwordRequest = email request password
38300  *    logout = 1 = to logout
38301  * 
38302  * Affects: (this id="????" elements)
38303  *   loading  (removed) (used to indicate application is loading)
38304  *   loading-mask (hides) (used to hide application when it's building loading)
38305  *   
38306  * 
38307  * Usage: 
38308  *    
38309  * 
38310  * Myapp.login = Roo.Login({
38311      url: xxxx,
38312    
38313      realm : 'Myapp', 
38314      
38315      
38316      method : 'POST',
38317      
38318      
38319      * 
38320  })
38321  * 
38322  * 
38323  * 
38324  **/
38325  
38326 Roo.Login = function(cfg)
38327 {
38328     this.addEvents({
38329         'refreshed' : true
38330     });
38331     
38332     Roo.apply(this,cfg);
38333     
38334     Roo.onReady(function() {
38335         this.onLoad();
38336     }, this);
38337     // call parent..
38338     
38339    
38340     Roo.Login.superclass.constructor.call(this, this);
38341     //this.addxtype(this.items[0]);
38342     
38343     
38344 }
38345
38346
38347 Roo.extend(Roo.Login, Roo.LayoutDialog, {
38348     
38349     /**
38350      * @cfg {String} method
38351      * Method used to query for login details.
38352      */
38353     
38354     method : 'POST',
38355     /**
38356      * @cfg {String} url
38357      * URL to query login data. - eg. baseURL + '/Login.php'
38358      */
38359     url : '',
38360     
38361     /**
38362      * @property user
38363      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
38364      * @type {Object} 
38365      */
38366     user : false,
38367     /**
38368      * @property checkFails
38369      * Number of times we have attempted to get authentication check, and failed.
38370      * @type {Number} 
38371      */
38372     checkFails : 0,
38373       /**
38374      * @property intervalID
38375      * The window interval that does the constant login checking.
38376      * @type {Number} 
38377      */
38378     intervalID : 0,
38379     
38380     
38381     onLoad : function() // called on page load...
38382     {
38383         // load 
38384          
38385         if (Roo.get('loading')) { // clear any loading indicator..
38386             Roo.get('loading').remove();
38387         }
38388         
38389         //this.switchLang('en'); // set the language to english..
38390        
38391         this.check({
38392             success:  function(response, opts)  {  // check successfull...
38393             
38394                 var res = this.processResponse(response);
38395                 this.checkFails =0;
38396                 if (!res.success) { // error!
38397                     this.checkFails = 5;
38398                     //console.log('call failure');
38399                     return this.failure(response,opts);
38400                 }
38401                 
38402                 if (!res.data.id) { // id=0 == login failure.
38403                     return this.show();
38404                 }
38405                 
38406                               
38407                         //console.log(success);
38408                 this.fillAuth(res.data);   
38409                 this.checkFails =0;
38410                 Roo.XComponent.build();
38411             },
38412             failure : this.show
38413         });
38414         
38415     }, 
38416     
38417     
38418     check: function(cfg) // called every so often to refresh cookie etc..
38419     {
38420         if (cfg.again) { // could be undefined..
38421             this.checkFails++;
38422         } else {
38423             this.checkFails = 0;
38424         }
38425         var _this = this;
38426         if (this.sending) {
38427             if ( this.checkFails > 4) {
38428                 Roo.MessageBox.alert("Error",  
38429                     "Error getting authentication status. - try reloading, or wait a while", function() {
38430                         _this.sending = false;
38431                     }); 
38432                 return;
38433             }
38434             cfg.again = true;
38435             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
38436             return;
38437         }
38438         this.sending = true;
38439         
38440         Roo.Ajax.request({  
38441             url: this.url,
38442             params: {
38443                 getAuthUser: true
38444             },  
38445             method: this.method,
38446             success:  cfg.success || this.success,
38447             failure : cfg.failure || this.failure,
38448             scope : this,
38449             callCfg : cfg
38450               
38451         });  
38452     }, 
38453     
38454     
38455     logout: function()
38456     {
38457         window.onbeforeunload = function() { }; // false does not work for IE..
38458         this.user = false;
38459         var _this = this;
38460         
38461         Roo.Ajax.request({  
38462             url: this.url,
38463             params: {
38464                 logout: 1
38465             },  
38466             method: 'GET',
38467             failure : function() {
38468                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
38469                     document.location = document.location.toString() + '?ts=' + Math.random();
38470                 });
38471                 
38472             },
38473             success : function() {
38474                 _this.user = false;
38475                 this.checkFails =0;
38476                 // fixme..
38477                 document.location = document.location.toString() + '?ts=' + Math.random();
38478             }
38479               
38480               
38481         }); 
38482     },
38483     
38484     processResponse : function (response)
38485     {
38486         var res = '';
38487         try {
38488             res = Roo.decode(response.responseText);
38489             // oops...
38490             if (typeof(res) != 'object') {
38491                 res = { success : false, errorMsg : res, errors : true };
38492             }
38493             if (typeof(res.success) == 'undefined') {
38494                 res.success = false;
38495             }
38496             
38497         } catch(e) {
38498             res = { success : false,  errorMsg : response.responseText, errors : true };
38499         }
38500         return res;
38501     },
38502     
38503     success : function(response, opts)  // check successfull...
38504     {  
38505         this.sending = false;
38506         var res = this.processResponse(response);
38507         if (!res.success) {
38508             return this.failure(response, opts);
38509         }
38510         if (!res.data || !res.data.id) {
38511             return this.failure(response,opts);
38512         }
38513         //console.log(res);
38514         this.fillAuth(res.data);
38515         
38516         this.checkFails =0;
38517         
38518     },
38519     
38520     
38521     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
38522     {
38523         this.authUser = -1;
38524         this.sending = false;
38525         var res = this.processResponse(response);
38526         //console.log(res);
38527         if ( this.checkFails > 2) {
38528         
38529             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
38530                 "Error getting authentication status. - try reloading"); 
38531             return;
38532         }
38533         opts.callCfg.again = true;
38534         this.check.defer(1000, this, [ opts.callCfg ]);
38535         return;  
38536     },
38537     
38538     
38539     
38540     fillAuth: function(au) {
38541         this.startAuthCheck();
38542         this.authUserId = au.id;
38543         this.authUser = au;
38544         this.lastChecked = new Date();
38545         this.fireEvent('refreshed', au);
38546         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
38547         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
38548         au.lang = au.lang || 'en';
38549         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
38550         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
38551         this.switchLang(au.lang );
38552         
38553      
38554         // open system... - -on setyp..
38555         if (this.authUserId  < 0) {
38556             Roo.MessageBox.alert("Warning", 
38557                 "This is an open system - please set up a admin user with a password.");  
38558         }
38559          
38560         //Pman.onload(); // which should do nothing if it's a re-auth result...
38561         
38562              
38563     },
38564     
38565     startAuthCheck : function() // starter for timeout checking..
38566     {
38567         if (this.intervalID) { // timer already in place...
38568             return false;
38569         }
38570         var _this = this;
38571         this.intervalID =  window.setInterval(function() {
38572               _this.check(false);
38573             }, 120000); // every 120 secs = 2mins..
38574         
38575         
38576     },
38577          
38578     
38579     switchLang : function (lang) 
38580     {
38581         _T = typeof(_T) == 'undefined' ? false : _T;
38582           if (!_T || !lang.length) {
38583             return;
38584         }
38585         
38586         if (!_T && lang != 'en') {
38587             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38588             return;
38589         }
38590         
38591         if (typeof(_T.en) == 'undefined') {
38592             _T.en = {};
38593             Roo.apply(_T.en, _T);
38594         }
38595         
38596         if (typeof(_T[lang]) == 'undefined') {
38597             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38598             return;
38599         }
38600         
38601         
38602         Roo.apply(_T, _T[lang]);
38603         // just need to set the text values for everything...
38604         var _this = this;
38605         /* this will not work ...
38606         if (this.form) { 
38607             
38608                
38609             function formLabel(name, val) {
38610                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
38611             }
38612             
38613             formLabel('password', "Password"+':');
38614             formLabel('username', "Email Address"+':');
38615             formLabel('lang', "Language"+':');
38616             this.dialog.setTitle("Login");
38617             this.dialog.buttons[0].setText("Forgot Password");
38618             this.dialog.buttons[1].setText("Login");
38619         }
38620         */
38621         
38622         
38623     },
38624     
38625     
38626     title: "Login",
38627     modal: true,
38628     width:  350,
38629     //height: 230,
38630     height: 180,
38631     shadow: true,
38632     minWidth:200,
38633     minHeight:180,
38634     //proxyDrag: true,
38635     closable: false,
38636     draggable: false,
38637     collapsible: false,
38638     resizable: false,
38639     center: {  // needed??
38640         autoScroll:false,
38641         titlebar: false,
38642        // tabPosition: 'top',
38643         hideTabs: true,
38644         closeOnTab: true,
38645         alwaysShowTabs: false
38646     } ,
38647     listeners : {
38648         
38649         show  : function(dlg)
38650         {
38651             //console.log(this);
38652             this.form = this.layout.getRegion('center').activePanel.form;
38653             this.form.dialog = dlg;
38654             this.buttons[0].form = this.form;
38655             this.buttons[0].dialog = dlg;
38656             this.buttons[1].form = this.form;
38657             this.buttons[1].dialog = dlg;
38658            
38659            //this.resizeToLogo.defer(1000,this);
38660             // this is all related to resizing for logos..
38661             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
38662            //// if (!sz) {
38663              //   this.resizeToLogo.defer(1000,this);
38664              //   return;
38665            // }
38666             //var w = Ext.lib.Dom.getViewWidth() - 100;
38667             //var h = Ext.lib.Dom.getViewHeight() - 100;
38668             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
38669             //this.center();
38670             if (this.disabled) {
38671                 this.hide();
38672                 return;
38673             }
38674             
38675             if (this.user.id < 0) { // used for inital setup situations.
38676                 return;
38677             }
38678             
38679             if (this.intervalID) {
38680                 // remove the timer
38681                 window.clearInterval(this.intervalID);
38682                 this.intervalID = false;
38683             }
38684             
38685             
38686             if (Roo.get('loading')) {
38687                 Roo.get('loading').remove();
38688             }
38689             if (Roo.get('loading-mask')) {
38690                 Roo.get('loading-mask').hide();
38691             }
38692             
38693             //incomming._node = tnode;
38694             this.form.reset();
38695             //this.dialog.modal = !modal;
38696             //this.dialog.show();
38697             this.el.unmask(); 
38698             
38699             
38700             this.form.setValues({
38701                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
38702                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
38703             });
38704             
38705             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
38706             if (this.form.findField('username').getValue().length > 0 ){
38707                 this.form.findField('password').focus();
38708             } else {
38709                this.form.findField('username').focus();
38710             }
38711     
38712         }
38713     },
38714     items : [
38715          {
38716        
38717             xtype : 'ContentPanel',
38718             xns : Roo,
38719             region: 'center',
38720             fitToFrame : true,
38721             
38722             items : [
38723     
38724                 {
38725                
38726                     xtype : 'Form',
38727                     xns : Roo.form,
38728                     labelWidth: 100,
38729                     style : 'margin: 10px;',
38730                     
38731                     listeners : {
38732                         actionfailed : function(f, act) {
38733                             // form can return { errors: .... }
38734                                 
38735                             //act.result.errors // invalid form element list...
38736                             //act.result.errorMsg// invalid form element list...
38737                             
38738                             this.dialog.el.unmask();
38739                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
38740                                         "Login failed - communication error - try again.");
38741                                       
38742                         },
38743                         actioncomplete: function(re, act) {
38744                              
38745                             Roo.state.Manager.set(
38746                                 this.dialog.realm + '.username',  
38747                                     this.findField('username').getValue()
38748                             );
38749                             Roo.state.Manager.set(
38750                                 this.dialog.realm + '.lang',  
38751                                 this.findField('lang').getValue() 
38752                             );
38753                             
38754                             this.dialog.fillAuth(act.result.data);
38755                               
38756                             this.dialog.hide();
38757                             
38758                             if (Roo.get('loading-mask')) {
38759                                 Roo.get('loading-mask').show();
38760                             }
38761                             Roo.XComponent.build();
38762                             
38763                              
38764                             
38765                         }
38766                     },
38767                     items : [
38768                         {
38769                             xtype : 'TextField',
38770                             xns : Roo.form,
38771                             fieldLabel: "Email Address",
38772                             name: 'username',
38773                             width:200,
38774                             autoCreate : {tag: "input", type: "text", size: "20"}
38775                         },
38776                         {
38777                             xtype : 'TextField',
38778                             xns : Roo.form,
38779                             fieldLabel: "Password",
38780                             inputType: 'password',
38781                             name: 'password',
38782                             width:200,
38783                             autoCreate : {tag: "input", type: "text", size: "20"},
38784                             listeners : {
38785                                 specialkey : function(e,ev) {
38786                                     if (ev.keyCode == 13) {
38787                                         this.form.dialog.el.mask("Logging in");
38788                                         this.form.doAction('submit', {
38789                                             url: this.form.dialog.url,
38790                                             method: this.form.dialog.method
38791                                         });
38792                                     }
38793                                 }
38794                             }  
38795                         },
38796                         {
38797                             xtype : 'ComboBox',
38798                             xns : Roo.form,
38799                             fieldLabel: "Language",
38800                             name : 'langdisp',
38801                             store: {
38802                                 xtype : 'SimpleStore',
38803                                 fields: ['lang', 'ldisp'],
38804                                 data : [
38805                                     [ 'en', 'English' ],
38806                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
38807                                     [ 'zh_CN', '\u7C21\u4E2D' ]
38808                                 ]
38809                             },
38810                             
38811                             valueField : 'lang',
38812                             hiddenName:  'lang',
38813                             width: 200,
38814                             displayField:'ldisp',
38815                             typeAhead: false,
38816                             editable: false,
38817                             mode: 'local',
38818                             triggerAction: 'all',
38819                             emptyText:'Select a Language...',
38820                             selectOnFocus:true,
38821                             listeners : {
38822                                 select :  function(cb, rec, ix) {
38823                                     this.form.switchLang(rec.data.lang);
38824                                 }
38825                             }
38826                         
38827                         }
38828                     ]
38829                 }
38830                   
38831                 
38832             ]
38833         }
38834     ],
38835     buttons : [
38836         {
38837             xtype : 'Button',
38838             xns : 'Roo',
38839             text : "Forgot Password",
38840             listeners : {
38841                 click : function() {
38842                     //console.log(this);
38843                     var n = this.form.findField('username').getValue();
38844                     if (!n.length) {
38845                         Roo.MessageBox.alert("Error", "Fill in your email address");
38846                         return;
38847                     }
38848                     Roo.Ajax.request({
38849                         url: this.dialog.url,
38850                         params: {
38851                             passwordRequest: n
38852                         },
38853                         method: this.dialog.method,
38854                         success:  function(response, opts)  {  // check successfull...
38855                         
38856                             var res = this.dialog.processResponse(response);
38857                             if (!res.success) { // error!
38858                                Roo.MessageBox.alert("Error" ,
38859                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
38860                                return;
38861                             }
38862                             Roo.MessageBox.alert("Notice" ,
38863                                 "Please check you email for the Password Reset message");
38864                         },
38865                         failure : function() {
38866                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
38867                         }
38868                         
38869                     });
38870                 }
38871             }
38872         },
38873         {
38874             xtype : 'Button',
38875             xns : 'Roo',
38876             text : "Login",
38877             listeners : {
38878                 
38879                 click : function () {
38880                         
38881                     this.dialog.el.mask("Logging in");
38882                     this.form.doAction('submit', {
38883                             url: this.dialog.url,
38884                             method: this.dialog.method
38885                     });
38886                 }
38887             }
38888         }
38889     ]
38890   
38891   
38892 })
38893  
38894
38895
38896