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){
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             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5064             return;
5065         }
5066         var r = o.records, t = o.totalRecords || r.length;
5067         if(!options || options.add !== true){
5068             if(this.pruneModifiedRecords){
5069                 this.modified = [];
5070             }
5071             for(var i = 0, len = r.length; i < len; i++){
5072                 r[i].join(this);
5073             }
5074             if(this.snapshot){
5075                 this.data = this.snapshot;
5076                 delete this.snapshot;
5077             }
5078             this.data.clear();
5079             this.data.addAll(r);
5080             this.totalLength = t;
5081             this.applySort();
5082             this.fireEvent("datachanged", this);
5083         }else{
5084             this.totalLength = Math.max(t, this.data.length+r.length);
5085             this.add(r);
5086         }
5087         this.fireEvent("load", this, r, options);
5088         if(options.callback){
5089             options.callback.call(options.scope || this, r, options, true);
5090         }
5091     },
5092
5093     /**
5094      * Loads data from a passed data block. A Reader which understands the format of the data
5095      * must have been configured in the constructor.
5096      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5097      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5098      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5099      */
5100     loadData : function(o, append){
5101         var r = this.reader.readRecords(o);
5102         this.loadRecords(r, {add: append}, true);
5103     },
5104
5105     /**
5106      * Gets the number of cached records.
5107      * <p>
5108      * <em>If using paging, this may not be the total size of the dataset. If the data object
5109      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5110      * the data set size</em>
5111      */
5112     getCount : function(){
5113         return this.data.length || 0;
5114     },
5115
5116     /**
5117      * Gets the total number of records in the dataset as returned by the server.
5118      * <p>
5119      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5120      * the dataset size</em>
5121      */
5122     getTotalCount : function(){
5123         return this.totalLength || 0;
5124     },
5125
5126     /**
5127      * Returns the sort state of the Store as an object with two properties:
5128      * <pre><code>
5129  field {String} The name of the field by which the Records are sorted
5130  direction {String} The sort order, "ASC" or "DESC"
5131      * </code></pre>
5132      */
5133     getSortState : function(){
5134         return this.sortInfo;
5135     },
5136
5137     // private
5138     applySort : function(){
5139         if(this.sortInfo && !this.remoteSort){
5140             var s = this.sortInfo, f = s.field;
5141             var st = this.fields.get(f).sortType;
5142             var fn = function(r1, r2){
5143                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5144                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5145             };
5146             this.data.sort(s.direction, fn);
5147             if(this.snapshot && this.snapshot != this.data){
5148                 this.snapshot.sort(s.direction, fn);
5149             }
5150         }
5151     },
5152
5153     /**
5154      * Sets the default sort column and order to be used by the next load operation.
5155      * @param {String} fieldName The name of the field to sort by.
5156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5157      */
5158     setDefaultSort : function(field, dir){
5159         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5160     },
5161
5162     /**
5163      * Sort the Records.
5164      * If remote sorting is used, the sort is performed on the server, and the cache is
5165      * reloaded. If local sorting is used, the cache is sorted internally.
5166      * @param {String} fieldName The name of the field to sort by.
5167      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5168      */
5169     sort : function(fieldName, dir){
5170         var f = this.fields.get(fieldName);
5171         if(!dir){
5172             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5173             
5174             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5175                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5176             }else{
5177                 dir = f.sortDir;
5178             }
5179         }
5180         this.sortToggle[f.name] = dir;
5181         this.sortInfo = {field: f.name, direction: dir};
5182         if(!this.remoteSort){
5183             this.applySort();
5184             this.fireEvent("datachanged", this);
5185         }else{
5186             this.load(this.lastOptions);
5187         }
5188     },
5189
5190     /**
5191      * Calls the specified function for each of the Records in the cache.
5192      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5193      * Returning <em>false</em> aborts and exits the iteration.
5194      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5195      */
5196     each : function(fn, scope){
5197         this.data.each(fn, scope);
5198     },
5199
5200     /**
5201      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5202      * (e.g., during paging).
5203      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5204      */
5205     getModifiedRecords : function(){
5206         return this.modified;
5207     },
5208
5209     // private
5210     createFilterFn : function(property, value, anyMatch){
5211         if(!value.exec){ // not a regex
5212             value = String(value);
5213             if(value.length == 0){
5214                 return false;
5215             }
5216             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5217         }
5218         return function(r){
5219             return value.test(r.data[property]);
5220         };
5221     },
5222
5223     /**
5224      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5225      * @param {String} property A field on your records
5226      * @param {Number} start The record index to start at (defaults to 0)
5227      * @param {Number} end The last record index to include (defaults to length - 1)
5228      * @return {Number} The sum
5229      */
5230     sum : function(property, start, end){
5231         var rs = this.data.items, v = 0;
5232         start = start || 0;
5233         end = (end || end === 0) ? end : rs.length-1;
5234
5235         for(var i = start; i <= end; i++){
5236             v += (rs[i].data[property] || 0);
5237         }
5238         return v;
5239     },
5240
5241     /**
5242      * Filter the records by a specified property.
5243      * @param {String} field A field on your records
5244      * @param {String/RegExp} value Either a string that the field
5245      * should start with or a RegExp to test against the field
5246      * @param {Boolean} anyMatch True to match any part not just the beginning
5247      */
5248     filter : function(property, value, anyMatch){
5249         var fn = this.createFilterFn(property, value, anyMatch);
5250         return fn ? this.filterBy(fn) : this.clearFilter();
5251     },
5252
5253     /**
5254      * Filter by a function. The specified function will be called with each
5255      * record in this data source. If the function returns true the record is included,
5256      * otherwise it is filtered.
5257      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5258      * @param {Object} scope (optional) The scope of the function (defaults to this)
5259      */
5260     filterBy : function(fn, scope){
5261         this.snapshot = this.snapshot || this.data;
5262         this.data = this.queryBy(fn, scope||this);
5263         this.fireEvent("datachanged", this);
5264     },
5265
5266     /**
5267      * Query the records by a specified property.
5268      * @param {String} field A field on your records
5269      * @param {String/RegExp} value Either a string that the field
5270      * should start with or a RegExp to test against the field
5271      * @param {Boolean} anyMatch True to match any part not just the beginning
5272      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      */
5274     query : function(property, value, anyMatch){
5275         var fn = this.createFilterFn(property, value, anyMatch);
5276         return fn ? this.queryBy(fn) : this.data.clone();
5277     },
5278
5279     /**
5280      * Query by a function. The specified function will be called with each
5281      * record in this data source. If the function returns true the record is included
5282      * in the results.
5283      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5284      * @param {Object} scope (optional) The scope of the function (defaults to this)
5285       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5286      **/
5287     queryBy : function(fn, scope){
5288         var data = this.snapshot || this.data;
5289         return data.filterBy(fn, scope||this);
5290     },
5291
5292     /**
5293      * Collects unique values for a particular dataIndex from this store.
5294      * @param {String} dataIndex The property to collect
5295      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5296      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5297      * @return {Array} An array of the unique values
5298      **/
5299     collect : function(dataIndex, allowNull, bypassFilter){
5300         var d = (bypassFilter === true && this.snapshot) ?
5301                 this.snapshot.items : this.data.items;
5302         var v, sv, r = [], l = {};
5303         for(var i = 0, len = d.length; i < len; i++){
5304             v = d[i].data[dataIndex];
5305             sv = String(v);
5306             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5307                 l[sv] = true;
5308                 r[r.length] = v;
5309             }
5310         }
5311         return r;
5312     },
5313
5314     /**
5315      * Revert to a view of the Record cache with no filtering applied.
5316      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5317      */
5318     clearFilter : function(suppressEvent){
5319         if(this.snapshot && this.snapshot != this.data){
5320             this.data = this.snapshot;
5321             delete this.snapshot;
5322             if(suppressEvent !== true){
5323                 this.fireEvent("datachanged", this);
5324             }
5325         }
5326     },
5327
5328     // private
5329     afterEdit : function(record){
5330         if(this.modified.indexOf(record) == -1){
5331             this.modified.push(record);
5332         }
5333         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5334     },
5335
5336     // private
5337     afterReject : function(record){
5338         this.modified.remove(record);
5339         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5340     },
5341
5342     // private
5343     afterCommit : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5346     },
5347
5348     /**
5349      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5350      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5351      */
5352     commitChanges : function(){
5353         var m = this.modified.slice(0);
5354         this.modified = [];
5355         for(var i = 0, len = m.length; i < len; i++){
5356             m[i].commit();
5357         }
5358     },
5359
5360     /**
5361      * Cancel outstanding changes on all changed records.
5362      */
5363     rejectChanges : function(){
5364         var m = this.modified.slice(0);
5365         this.modified = [];
5366         for(var i = 0, len = m.length; i < len; i++){
5367             m[i].reject();
5368         }
5369     },
5370
5371     onMetaChange : function(meta, rtype, o){
5372         this.recordType = rtype;
5373         this.fields = rtype.prototype.fields;
5374         delete this.snapshot;
5375         this.sortInfo = meta.sortInfo || this.sortInfo;
5376         this.modified = [];
5377         this.fireEvent('metachange', this, this.reader.meta);
5378     }
5379 });/*
5380  * Based on:
5381  * Ext JS Library 1.1.1
5382  * Copyright(c) 2006-2007, Ext JS, LLC.
5383  *
5384  * Originally Released Under LGPL - original licence link has changed is not relivant.
5385  *
5386  * Fork - LGPL
5387  * <script type="text/javascript">
5388  */
5389
5390 /**
5391  * @class Roo.data.SimpleStore
5392  * @extends Roo.data.Store
5393  * Small helper class to make creating Stores from Array data easier.
5394  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5395  * @cfg {Array} fields An array of field definition objects, or field name strings.
5396  * @cfg {Array} data The multi-dimensional array of data
5397  * @constructor
5398  * @param {Object} config
5399  */
5400 Roo.data.SimpleStore = function(config){
5401     Roo.data.SimpleStore.superclass.constructor.call(this, {
5402         isLocal : true,
5403         reader: new Roo.data.ArrayReader({
5404                 id: config.id
5405             },
5406             Roo.data.Record.create(config.fields)
5407         ),
5408         proxy : new Roo.data.MemoryProxy(config.data)
5409     });
5410     this.load();
5411 };
5412 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5413  * Based on:
5414  * Ext JS Library 1.1.1
5415  * Copyright(c) 2006-2007, Ext JS, LLC.
5416  *
5417  * Originally Released Under LGPL - original licence link has changed is not relivant.
5418  *
5419  * Fork - LGPL
5420  * <script type="text/javascript">
5421  */
5422
5423 /**
5424 /**
5425  * @extends Roo.data.Store
5426  * @class Roo.data.JsonStore
5427  * Small helper class to make creating Stores for JSON data easier. <br/>
5428 <pre><code>
5429 var store = new Roo.data.JsonStore({
5430     url: 'get-images.php',
5431     root: 'images',
5432     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5433 });
5434 </code></pre>
5435  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5436  * JsonReader and HttpProxy (unless inline data is provided).</b>
5437  * @cfg {Array} fields An array of field definition objects, or field name strings.
5438  * @constructor
5439  * @param {Object} config
5440  */
5441 Roo.data.JsonStore = function(c){
5442     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5443         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5444         reader: new Roo.data.JsonReader(c, c.fields)
5445     }));
5446 };
5447 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5448  * Based on:
5449  * Ext JS Library 1.1.1
5450  * Copyright(c) 2006-2007, Ext JS, LLC.
5451  *
5452  * Originally Released Under LGPL - original licence link has changed is not relivant.
5453  *
5454  * Fork - LGPL
5455  * <script type="text/javascript">
5456  */
5457
5458  
5459 Roo.data.Field = function(config){
5460     if(typeof config == "string"){
5461         config = {name: config};
5462     }
5463     Roo.apply(this, config);
5464     
5465     if(!this.type){
5466         this.type = "auto";
5467     }
5468     
5469     var st = Roo.data.SortTypes;
5470     // named sortTypes are supported, here we look them up
5471     if(typeof this.sortType == "string"){
5472         this.sortType = st[this.sortType];
5473     }
5474     
5475     // set default sortType for strings and dates
5476     if(!this.sortType){
5477         switch(this.type){
5478             case "string":
5479                 this.sortType = st.asUCString;
5480                 break;
5481             case "date":
5482                 this.sortType = st.asDate;
5483                 break;
5484             default:
5485                 this.sortType = st.none;
5486         }
5487     }
5488
5489     // define once
5490     var stripRe = /[\$,%]/g;
5491
5492     // prebuilt conversion function for this field, instead of
5493     // switching every time we're reading a value
5494     if(!this.convert){
5495         var cv, dateFormat = this.dateFormat;
5496         switch(this.type){
5497             case "":
5498             case "auto":
5499             case undefined:
5500                 cv = function(v){ return v; };
5501                 break;
5502             case "string":
5503                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5504                 break;
5505             case "int":
5506                 cv = function(v){
5507                     return v !== undefined && v !== null && v !== '' ?
5508                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5509                     };
5510                 break;
5511             case "float":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5515                     };
5516                 break;
5517             case "bool":
5518             case "boolean":
5519                 cv = function(v){ return v === true || v === "true" || v == 1; };
5520                 break;
5521             case "date":
5522                 cv = function(v){
5523                     if(!v){
5524                         return '';
5525                     }
5526                     if(v instanceof Date){
5527                         return v;
5528                     }
5529                     if(dateFormat){
5530                         if(dateFormat == "timestamp"){
5531                             return new Date(v*1000);
5532                         }
5533                         return Date.parseDate(v, dateFormat);
5534                     }
5535                     var parsed = Date.parse(v);
5536                     return parsed ? new Date(parsed) : null;
5537                 };
5538              break;
5539             
5540         }
5541         this.convert = cv;
5542     }
5543 };
5544
5545 Roo.data.Field.prototype = {
5546     dateFormat: null,
5547     defaultValue: "",
5548     mapping: null,
5549     sortType : null,
5550     sortDir : "ASC"
5551 };/*
5552  * Based on:
5553  * Ext JS Library 1.1.1
5554  * Copyright(c) 2006-2007, Ext JS, LLC.
5555  *
5556  * Originally Released Under LGPL - original licence link has changed is not relivant.
5557  *
5558  * Fork - LGPL
5559  * <script type="text/javascript">
5560  */
5561  
5562 // Base class for reading structured data from a data source.  This class is intended to be
5563 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5564
5565 /**
5566  * @class Roo.data.DataReader
5567  * Base class for reading structured data from a data source.  This class is intended to be
5568  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5569  */
5570
5571 Roo.data.DataReader = function(meta, recordType){
5572     
5573     this.meta = meta;
5574     
5575     this.recordType = recordType instanceof Array ? 
5576         Roo.data.Record.create(recordType) : recordType;
5577 };
5578
5579 Roo.data.DataReader.prototype = {
5580      /**
5581      * Create an empty record
5582      * @param {Object} data (optional) - overlay some values
5583      * @return {Roo.data.Record} record created.
5584      */
5585     newRow :  function(d) {
5586         var da =  {};
5587         this.recordType.prototype.fields.each(function(c) {
5588             switch( c.type) {
5589                 case 'int' : da[c.name] = 0; break;
5590                 case 'date' : da[c.name] = new Date(); break;
5591                 case 'float' : da[c.name] = 0.0; break;
5592                 case 'boolean' : da[c.name] = false; break;
5593                 default : da[c.name] = ""; break;
5594             }
5595             
5596         });
5597         return new this.recordType(Roo.apply(da, d));
5598     }
5599     
5600 };/*
5601  * Based on:
5602  * Ext JS Library 1.1.1
5603  * Copyright(c) 2006-2007, Ext JS, LLC.
5604  *
5605  * Originally Released Under LGPL - original licence link has changed is not relivant.
5606  *
5607  * Fork - LGPL
5608  * <script type="text/javascript">
5609  */
5610
5611 /**
5612  * @class Roo.data.DataProxy
5613  * @extends Roo.data.Observable
5614  * This class is an abstract base class for implementations which provide retrieval of
5615  * unformatted data objects.<br>
5616  * <p>
5617  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5618  * (of the appropriate type which knows how to parse the data object) to provide a block of
5619  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5620  * <p>
5621  * Custom implementations must implement the load method as described in
5622  * {@link Roo.data.HttpProxy#load}.
5623  */
5624 Roo.data.DataProxy = function(){
5625     this.addEvents({
5626         /**
5627          * @event beforeload
5628          * Fires before a network request is made to retrieve a data object.
5629          * @param {Object} This DataProxy object.
5630          * @param {Object} params The params parameter to the load function.
5631          */
5632         beforeload : true,
5633         /**
5634          * @event load
5635          * Fires before the load method's callback is called.
5636          * @param {Object} This DataProxy object.
5637          * @param {Object} o The data object.
5638          * @param {Object} arg The callback argument object passed to the load function.
5639          */
5640         load : true,
5641         /**
5642          * @event loadexception
5643          * Fires if an Exception occurs during data retrieval.
5644          * @param {Object} This DataProxy object.
5645          * @param {Object} o The data object.
5646          * @param {Object} arg The callback argument object passed to the load function.
5647          * @param {Object} e The Exception.
5648          */
5649         loadexception : true
5650     });
5651     Roo.data.DataProxy.superclass.constructor.call(this);
5652 };
5653
5654 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5655
5656     /**
5657      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5658      */
5659 /*
5660  * Based on:
5661  * Ext JS Library 1.1.1
5662  * Copyright(c) 2006-2007, Ext JS, LLC.
5663  *
5664  * Originally Released Under LGPL - original licence link has changed is not relivant.
5665  *
5666  * Fork - LGPL
5667  * <script type="text/javascript">
5668  */
5669 /**
5670  * @class Roo.data.MemoryProxy
5671  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5672  * to the Reader when its load method is called.
5673  * @constructor
5674  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5675  */
5676 Roo.data.MemoryProxy = function(data){
5677     if (data.data) {
5678         data = data.data;
5679     }
5680     Roo.data.MemoryProxy.superclass.constructor.call(this);
5681     this.data = data;
5682 };
5683
5684 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5685     /**
5686      * Load data from the requested source (in this case an in-memory
5687      * data object passed to the constructor), read the data object into
5688      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5689      * process that block using the passed callback.
5690      * @param {Object} params This parameter is not used by the MemoryProxy class.
5691      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5692      * object into a block of Roo.data.Records.
5693      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5694      * The function must be passed <ul>
5695      * <li>The Record block object</li>
5696      * <li>The "arg" argument from the load function</li>
5697      * <li>A boolean success indicator</li>
5698      * </ul>
5699      * @param {Object} scope The scope in which to call the callback
5700      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5701      */
5702     load : function(params, reader, callback, scope, arg){
5703         params = params || {};
5704         var result;
5705         try {
5706             result = reader.readRecords(this.data);
5707         }catch(e){
5708             this.fireEvent("loadexception", this, arg, null, e);
5709             callback.call(scope, null, arg, false);
5710             return;
5711         }
5712         callback.call(scope, result, arg, true);
5713     },
5714     
5715     // private
5716     update : function(params, records){
5717         
5718     }
5719 });/*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.HttpProxy
5731  * @extends Roo.data.DataProxy
5732  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5733  * configured to reference a certain URL.<br><br>
5734  * <p>
5735  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5736  * from which the running page was served.<br><br>
5737  * <p>
5738  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5739  * <p>
5740  * Be aware that to enable the browser to parse an XML document, the server must set
5741  * the Content-Type header in the HTTP response to "text/xml".
5742  * @constructor
5743  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5744  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5745  * will be used to make the request.
5746  */
5747 Roo.data.HttpProxy = function(conn){
5748     Roo.data.HttpProxy.superclass.constructor.call(this);
5749     // is conn a conn config or a real conn?
5750     this.conn = conn;
5751     this.useAjax = !conn || !conn.events;
5752   
5753 };
5754
5755 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5756     // thse are take from connection...
5757     
5758     /**
5759      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5760      */
5761     /**
5762      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5763      * extra parameters to each request made by this object. (defaults to undefined)
5764      */
5765     /**
5766      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5767      *  to each request made by this object. (defaults to undefined)
5768      */
5769     /**
5770      * @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)
5771      */
5772     /**
5773      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5774      */
5775      /**
5776      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5777      * @type Boolean
5778      */
5779   
5780
5781     /**
5782      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5783      * @type Boolean
5784      */
5785     /**
5786      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5787      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5788      * a finer-grained basis than the DataProxy events.
5789      */
5790     getConnection : function(){
5791         return this.useAjax ? Roo.Ajax : this.conn;
5792     },
5793
5794     /**
5795      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5796      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5797      * process that block using the passed callback.
5798      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5799      * for the request to the remote server.
5800      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5801      * object into a block of Roo.data.Records.
5802      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5803      * The function must be passed <ul>
5804      * <li>The Record block object</li>
5805      * <li>The "arg" argument from the load function</li>
5806      * <li>A boolean success indicator</li>
5807      * </ul>
5808      * @param {Object} scope The scope in which to call the callback
5809      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5810      */
5811     load : function(params, reader, callback, scope, arg){
5812         if(this.fireEvent("beforeload", this, params) !== false){
5813             var  o = {
5814                 params : params || {},
5815                 request: {
5816                     callback : callback,
5817                     scope : scope,
5818                     arg : arg
5819                 },
5820                 reader: reader,
5821                 callback : this.loadResponse,
5822                 scope: this
5823             };
5824             if(this.useAjax){
5825                 Roo.applyIf(o, this.conn);
5826                 if(this.activeRequest){
5827                     Roo.Ajax.abort(this.activeRequest);
5828                 }
5829                 this.activeRequest = Roo.Ajax.request(o);
5830             }else{
5831                 this.conn.request(o);
5832             }
5833         }else{
5834             callback.call(scope||this, null, arg, false);
5835         }
5836     },
5837
5838     // private
5839     loadResponse : function(o, success, response){
5840         delete this.activeRequest;
5841         if(!success){
5842             this.fireEvent("loadexception", this, o, response);
5843             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5844             return;
5845         }
5846         var result;
5847         try {
5848             result = o.reader.read(response);
5849         }catch(e){
5850             this.fireEvent("loadexception", this, o, response, e);
5851             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5852             return;
5853         }
5854         
5855         this.fireEvent("load", this, o, o.request.arg);
5856         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5857     },
5858
5859     // private
5860     update : function(dataSet){
5861
5862     },
5863
5864     // private
5865     updateResponse : function(dataSet){
5866
5867     }
5868 });/*
5869  * Based on:
5870  * Ext JS Library 1.1.1
5871  * Copyright(c) 2006-2007, Ext JS, LLC.
5872  *
5873  * Originally Released Under LGPL - original licence link has changed is not relivant.
5874  *
5875  * Fork - LGPL
5876  * <script type="text/javascript">
5877  */
5878
5879 /**
5880  * @class Roo.data.ScriptTagProxy
5881  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5882  * other than the originating domain of the running page.<br><br>
5883  * <p>
5884  * <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
5885  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5886  * <p>
5887  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5888  * source code that is used as the source inside a &lt;script> tag.<br><br>
5889  * <p>
5890  * In order for the browser to process the returned data, the server must wrap the data object
5891  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5892  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5893  * depending on whether the callback name was passed:
5894  * <p>
5895  * <pre><code>
5896 boolean scriptTag = false;
5897 String cb = request.getParameter("callback");
5898 if (cb != null) {
5899     scriptTag = true;
5900     response.setContentType("text/javascript");
5901 } else {
5902     response.setContentType("application/x-json");
5903 }
5904 Writer out = response.getWriter();
5905 if (scriptTag) {
5906     out.write(cb + "(");
5907 }
5908 out.print(dataBlock.toJsonString());
5909 if (scriptTag) {
5910     out.write(");");
5911 }
5912 </pre></code>
5913  *
5914  * @constructor
5915  * @param {Object} config A configuration object.
5916  */
5917 Roo.data.ScriptTagProxy = function(config){
5918     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5919     Roo.apply(this, config);
5920     this.head = document.getElementsByTagName("head")[0];
5921 };
5922
5923 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5924
5925 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5926     /**
5927      * @cfg {String} url The URL from which to request the data object.
5928      */
5929     /**
5930      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5931      */
5932     timeout : 30000,
5933     /**
5934      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5935      * the server the name of the callback function set up by the load call to process the returned data object.
5936      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5937      * javascript output which calls this named function passing the data object as its only parameter.
5938      */
5939     callbackParam : "callback",
5940     /**
5941      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5942      * name to the request.
5943      */
5944     nocache : true,
5945
5946     /**
5947      * Load data from the configured URL, read the data object into
5948      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5949      * process that block using the passed callback.
5950      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5951      * for the request to the remote server.
5952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5953      * object into a block of Roo.data.Records.
5954      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5955      * The function must be passed <ul>
5956      * <li>The Record block object</li>
5957      * <li>The "arg" argument from the load function</li>
5958      * <li>A boolean success indicator</li>
5959      * </ul>
5960      * @param {Object} scope The scope in which to call the callback
5961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5962      */
5963     load : function(params, reader, callback, scope, arg){
5964         if(this.fireEvent("beforeload", this, params) !== false){
5965
5966             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5967
5968             var url = this.url;
5969             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5970             if(this.nocache){
5971                 url += "&_dc=" + (new Date().getTime());
5972             }
5973             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5974             var trans = {
5975                 id : transId,
5976                 cb : "stcCallback"+transId,
5977                 scriptId : "stcScript"+transId,
5978                 params : params,
5979                 arg : arg,
5980                 url : url,
5981                 callback : callback,
5982                 scope : scope,
5983                 reader : reader
5984             };
5985             var conn = this;
5986
5987             window[trans.cb] = function(o){
5988                 conn.handleResponse(o, trans);
5989             };
5990
5991             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5992
5993             if(this.autoAbort !== false){
5994                 this.abort();
5995             }
5996
5997             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5998
5999             var script = document.createElement("script");
6000             script.setAttribute("src", url);
6001             script.setAttribute("type", "text/javascript");
6002             script.setAttribute("id", trans.scriptId);
6003             this.head.appendChild(script);
6004
6005             this.trans = trans;
6006         }else{
6007             callback.call(scope||this, null, arg, false);
6008         }
6009     },
6010
6011     // private
6012     isLoading : function(){
6013         return this.trans ? true : false;
6014     },
6015
6016     /**
6017      * Abort the current server request.
6018      */
6019     abort : function(){
6020         if(this.isLoading()){
6021             this.destroyTrans(this.trans);
6022         }
6023     },
6024
6025     // private
6026     destroyTrans : function(trans, isLoaded){
6027         this.head.removeChild(document.getElementById(trans.scriptId));
6028         clearTimeout(trans.timeoutId);
6029         if(isLoaded){
6030             window[trans.cb] = undefined;
6031             try{
6032                 delete window[trans.cb];
6033             }catch(e){}
6034         }else{
6035             // if hasn't been loaded, wait for load to remove it to prevent script error
6036             window[trans.cb] = function(){
6037                 window[trans.cb] = undefined;
6038                 try{
6039                     delete window[trans.cb];
6040                 }catch(e){}
6041             };
6042         }
6043     },
6044
6045     // private
6046     handleResponse : function(o, trans){
6047         this.trans = false;
6048         this.destroyTrans(trans, true);
6049         var result;
6050         try {
6051             result = trans.reader.readRecords(o);
6052         }catch(e){
6053             this.fireEvent("loadexception", this, o, trans.arg, e);
6054             trans.callback.call(trans.scope||window, null, trans.arg, false);
6055             return;
6056         }
6057         this.fireEvent("load", this, o, trans.arg);
6058         trans.callback.call(trans.scope||window, result, trans.arg, true);
6059     },
6060
6061     // private
6062     handleFailure : function(trans){
6063         this.trans = false;
6064         this.destroyTrans(trans, false);
6065         this.fireEvent("loadexception", this, null, trans.arg);
6066         trans.callback.call(trans.scope||window, null, trans.arg, false);
6067     }
6068 });/*
6069  * Based on:
6070  * Ext JS Library 1.1.1
6071  * Copyright(c) 2006-2007, Ext JS, LLC.
6072  *
6073  * Originally Released Under LGPL - original licence link has changed is not relivant.
6074  *
6075  * Fork - LGPL
6076  * <script type="text/javascript">
6077  */
6078
6079 /**
6080  * @class Roo.data.JsonReader
6081  * @extends Roo.data.DataReader
6082  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6083  * based on mappings in a provided Roo.data.Record constructor.
6084  * 
6085  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6086  * in the reply previously. 
6087  * 
6088  * <p>
6089  * Example code:
6090  * <pre><code>
6091 var RecordDef = Roo.data.Record.create([
6092     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6093     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6094 ]);
6095 var myReader = new Roo.data.JsonReader({
6096     totalProperty: "results",    // The property which contains the total dataset size (optional)
6097     root: "rows",                // The property which contains an Array of row objects
6098     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6099 }, RecordDef);
6100 </code></pre>
6101  * <p>
6102  * This would consume a JSON file like this:
6103  * <pre><code>
6104 { 'results': 2, 'rows': [
6105     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6106     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6107 }
6108 </code></pre>
6109  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6110  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6111  * paged from the remote server.
6112  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6113  * @cfg {String} root name of the property which contains the Array of row objects.
6114  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6115  * @constructor
6116  * Create a new JsonReader
6117  * @param {Object} meta Metadata configuration options
6118  * @param {Object} recordType Either an Array of field definition objects,
6119  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6120  */
6121 Roo.data.JsonReader = function(meta, recordType){
6122     
6123     meta = meta || {};
6124     // set some defaults:
6125     Roo.applyIf(meta, {
6126         totalProperty: 'total',
6127         successProperty : 'success',
6128         root : 'data',
6129         id : 'id'
6130     });
6131     
6132     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6133 };
6134 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6135     
6136     /**
6137      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6138      * Used by Store query builder to append _requestMeta to params.
6139      * 
6140      */
6141     metaFromRemote : false,
6142     /**
6143      * This method is only used by a DataProxy which has retrieved data from a remote server.
6144      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6145      * @return {Object} data A data block which is used by an Roo.data.Store object as
6146      * a cache of Roo.data.Records.
6147      */
6148     read : function(response){
6149         var json = response.responseText;
6150        
6151         var o = /* eval:var:o */ eval("("+json+")");
6152         if(!o) {
6153             throw {message: "JsonReader.read: Json object not found"};
6154         }
6155         
6156         if(o.metaData){
6157             
6158             delete this.ef;
6159             this.metaFromRemote = true;
6160             this.meta = o.metaData;
6161             this.recordType = Roo.data.Record.create(o.metaData.fields);
6162             this.onMetaChange(this.meta, this.recordType, o);
6163         }
6164         return this.readRecords(o);
6165     },
6166
6167     // private function a store will implement
6168     onMetaChange : function(meta, recordType, o){
6169
6170     },
6171
6172     /**
6173          * @ignore
6174          */
6175     simpleAccess: function(obj, subsc) {
6176         return obj[subsc];
6177     },
6178
6179         /**
6180          * @ignore
6181          */
6182     getJsonAccessor: function(){
6183         var re = /[\[\.]/;
6184         return function(expr) {
6185             try {
6186                 return(re.test(expr))
6187                     ? new Function("obj", "return obj." + expr)
6188                     : function(obj){
6189                         return obj[expr];
6190                     };
6191             } catch(e){}
6192             return Roo.emptyFn;
6193         };
6194     }(),
6195
6196     /**
6197      * Create a data block containing Roo.data.Records from an XML document.
6198      * @param {Object} o An object which contains an Array of row objects in the property specified
6199      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6200      * which contains the total size of the dataset.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     readRecords : function(o){
6205         /**
6206          * After any data loads, the raw JSON data is available for further custom processing.
6207          * @type Object
6208          */
6209         this.jsonData = o;
6210         var s = this.meta, Record = this.recordType,
6211             f = Record.prototype.fields, fi = f.items, fl = f.length;
6212
6213 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6214         if (!this.ef) {
6215             if(s.totalProperty) {
6216                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6217                 }
6218                 if(s.successProperty) {
6219                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6220                 }
6221                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6222                 if (s.id) {
6223                         var g = this.getJsonAccessor(s.id);
6224                         this.getId = function(rec) {
6225                                 var r = g(rec);
6226                                 return (r === undefined || r === "") ? null : r;
6227                         };
6228                 } else {
6229                         this.getId = function(){return null;};
6230                 }
6231             this.ef = [];
6232             for(var jj = 0; jj < fl; jj++){
6233                 f = fi[jj];
6234                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6235                 this.ef[jj] = this.getJsonAccessor(map);
6236             }
6237         }
6238
6239         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6240         if(s.totalProperty){
6241             var vt = parseInt(this.getTotal(o), 10);
6242             if(!isNaN(vt)){
6243                 totalRecords = vt;
6244             }
6245         }
6246         if(s.successProperty){
6247             var vs = this.getSuccess(o);
6248             if(vs === false || vs === 'false'){
6249                 success = false;
6250             }
6251         }
6252         var records = [];
6253             for(var i = 0; i < c; i++){
6254                     var n = root[i];
6255                 var values = {};
6256                 var id = this.getId(n);
6257                 for(var j = 0; j < fl; j++){
6258                     f = fi[j];
6259                 var v = this.ef[j](n);
6260                 if (!f.convert) {
6261                     Roo.log('missing convert for ' + f.name);
6262                     Roo.log(f);
6263                     continue;
6264                 }
6265                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6266                 }
6267                 var record = new Record(values, id);
6268                 record.json = n;
6269                 records[i] = record;
6270             }
6271             return {
6272                 success : success,
6273                 records : records,
6274                 totalRecords : totalRecords
6275             };
6276     }
6277 });/*
6278  * Based on:
6279  * Ext JS Library 1.1.1
6280  * Copyright(c) 2006-2007, Ext JS, LLC.
6281  *
6282  * Originally Released Under LGPL - original licence link has changed is not relivant.
6283  *
6284  * Fork - LGPL
6285  * <script type="text/javascript">
6286  */
6287
6288 /**
6289  * @class Roo.data.XmlReader
6290  * @extends Roo.data.DataReader
6291  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6292  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6293  * <p>
6294  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6295  * header in the HTTP response must be set to "text/xml".</em>
6296  * <p>
6297  * Example code:
6298  * <pre><code>
6299 var RecordDef = Roo.data.Record.create([
6300    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6301    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6302 ]);
6303 var myReader = new Roo.data.XmlReader({
6304    totalRecords: "results", // The element which contains the total dataset size (optional)
6305    record: "row",           // The repeated element which contains row information
6306    id: "id"                 // The element within the row that provides an ID for the record (optional)
6307 }, RecordDef);
6308 </code></pre>
6309  * <p>
6310  * This would consume an XML file like this:
6311  * <pre><code>
6312 &lt;?xml?>
6313 &lt;dataset>
6314  &lt;results>2&lt;/results>
6315  &lt;row>
6316    &lt;id>1&lt;/id>
6317    &lt;name>Bill&lt;/name>
6318    &lt;occupation>Gardener&lt;/occupation>
6319  &lt;/row>
6320  &lt;row>
6321    &lt;id>2&lt;/id>
6322    &lt;name>Ben&lt;/name>
6323    &lt;occupation>Horticulturalist&lt;/occupation>
6324  &lt;/row>
6325 &lt;/dataset>
6326 </code></pre>
6327  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6328  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6329  * paged from the remote server.
6330  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6331  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6332  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6333  * a record identifier value.
6334  * @constructor
6335  * Create a new XmlReader
6336  * @param {Object} meta Metadata configuration options
6337  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6338  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6339  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6340  */
6341 Roo.data.XmlReader = function(meta, recordType){
6342     meta = meta || {};
6343     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6344 };
6345 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6346     /**
6347      * This method is only used by a DataProxy which has retrieved data from a remote server.
6348          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6349          * to contain a method called 'responseXML' that returns an XML document object.
6350      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6351      * a cache of Roo.data.Records.
6352      */
6353     read : function(response){
6354         var doc = response.responseXML;
6355         if(!doc) {
6356             throw {message: "XmlReader.read: XML Document not available"};
6357         }
6358         return this.readRecords(doc);
6359     },
6360
6361     /**
6362      * Create a data block containing Roo.data.Records from an XML document.
6363          * @param {Object} doc A parsed XML document.
6364      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6365      * a cache of Roo.data.Records.
6366      */
6367     readRecords : function(doc){
6368         /**
6369          * After any data loads/reads, the raw XML Document is available for further custom processing.
6370          * @type XMLDocument
6371          */
6372         this.xmlData = doc;
6373         var root = doc.documentElement || doc;
6374         var q = Roo.DomQuery;
6375         var recordType = this.recordType, fields = recordType.prototype.fields;
6376         var sid = this.meta.id;
6377         var totalRecords = 0, success = true;
6378         if(this.meta.totalRecords){
6379             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6380         }
6381         
6382         if(this.meta.success){
6383             var sv = q.selectValue(this.meta.success, root, true);
6384             success = sv !== false && sv !== 'false';
6385         }
6386         var records = [];
6387         var ns = q.select(this.meta.record, root);
6388         for(var i = 0, len = ns.length; i < len; i++) {
6389                 var n = ns[i];
6390                 var values = {};
6391                 var id = sid ? q.selectValue(sid, n) : undefined;
6392                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6393                     var f = fields.items[j];
6394                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6395                     v = f.convert(v);
6396                     values[f.name] = v;
6397                 }
6398                 var record = new recordType(values, id);
6399                 record.node = n;
6400                 records[records.length] = record;
6401             }
6402
6403             return {
6404                 success : success,
6405                 records : records,
6406                 totalRecords : totalRecords || records.length
6407             };
6408     }
6409 });/*
6410  * Based on:
6411  * Ext JS Library 1.1.1
6412  * Copyright(c) 2006-2007, Ext JS, LLC.
6413  *
6414  * Originally Released Under LGPL - original licence link has changed is not relivant.
6415  *
6416  * Fork - LGPL
6417  * <script type="text/javascript">
6418  */
6419
6420 /**
6421  * @class Roo.data.ArrayReader
6422  * @extends Roo.data.DataReader
6423  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6424  * Each element of that Array represents a row of data fields. The
6425  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6426  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6427  * <p>
6428  * Example code:.
6429  * <pre><code>
6430 var RecordDef = Roo.data.Record.create([
6431     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6432     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6433 ]);
6434 var myReader = new Roo.data.ArrayReader({
6435     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6436 }, RecordDef);
6437 </code></pre>
6438  * <p>
6439  * This would consume an Array like this:
6440  * <pre><code>
6441 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6442   </code></pre>
6443  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6444  * @constructor
6445  * Create a new JsonReader
6446  * @param {Object} meta Metadata configuration options.
6447  * @param {Object} recordType Either an Array of field definition objects
6448  * as specified to {@link Roo.data.Record#create},
6449  * or an {@link Roo.data.Record} object
6450  * created using {@link Roo.data.Record#create}.
6451  */
6452 Roo.data.ArrayReader = function(meta, recordType){
6453     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6454 };
6455
6456 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6457     /**
6458      * Create a data block containing Roo.data.Records from an XML document.
6459      * @param {Object} o An Array of row objects which represents the dataset.
6460      * @return {Object} data A data block which is used by an Roo.data.Store object as
6461      * a cache of Roo.data.Records.
6462      */
6463     readRecords : function(o){
6464         var sid = this.meta ? this.meta.id : null;
6465         var recordType = this.recordType, fields = recordType.prototype.fields;
6466         var records = [];
6467         var root = o;
6468             for(var i = 0; i < root.length; i++){
6469                     var n = root[i];
6470                 var values = {};
6471                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6472                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6473                 var f = fields.items[j];
6474                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6475                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6476                 v = f.convert(v);
6477                 values[f.name] = v;
6478             }
6479                 var record = new recordType(values, id);
6480                 record.json = n;
6481                 records[records.length] = record;
6482             }
6483             return {
6484                 records : records,
6485                 totalRecords : records.length
6486             };
6487     }
6488 });/*
6489  * Based on:
6490  * Ext JS Library 1.1.1
6491  * Copyright(c) 2006-2007, Ext JS, LLC.
6492  *
6493  * Originally Released Under LGPL - original licence link has changed is not relivant.
6494  *
6495  * Fork - LGPL
6496  * <script type="text/javascript">
6497  */
6498
6499
6500 /**
6501  * @class Roo.data.Tree
6502  * @extends Roo.util.Observable
6503  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6504  * in the tree have most standard DOM functionality.
6505  * @constructor
6506  * @param {Node} root (optional) The root node
6507  */
6508 Roo.data.Tree = function(root){
6509    this.nodeHash = {};
6510    /**
6511     * The root node for this tree
6512     * @type Node
6513     */
6514    this.root = null;
6515    if(root){
6516        this.setRootNode(root);
6517    }
6518    this.addEvents({
6519        /**
6520         * @event append
6521         * Fires when a new child node is appended to a node in this tree.
6522         * @param {Tree} tree The owner tree
6523         * @param {Node} parent The parent node
6524         * @param {Node} node The newly appended node
6525         * @param {Number} index The index of the newly appended node
6526         */
6527        "append" : true,
6528        /**
6529         * @event remove
6530         * Fires when a child node is removed from a node in this tree.
6531         * @param {Tree} tree The owner tree
6532         * @param {Node} parent The parent node
6533         * @param {Node} node The child node removed
6534         */
6535        "remove" : true,
6536        /**
6537         * @event move
6538         * Fires when a node is moved to a new location in the tree
6539         * @param {Tree} tree The owner tree
6540         * @param {Node} node The node moved
6541         * @param {Node} oldParent The old parent of this node
6542         * @param {Node} newParent The new parent of this node
6543         * @param {Number} index The index it was moved to
6544         */
6545        "move" : true,
6546        /**
6547         * @event insert
6548         * Fires when a new child node is inserted in a node in this tree.
6549         * @param {Tree} tree The owner tree
6550         * @param {Node} parent The parent node
6551         * @param {Node} node The child node inserted
6552         * @param {Node} refNode The child node the node was inserted before
6553         */
6554        "insert" : true,
6555        /**
6556         * @event beforeappend
6557         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6558         * @param {Tree} tree The owner tree
6559         * @param {Node} parent The parent node
6560         * @param {Node} node The child node to be appended
6561         */
6562        "beforeappend" : true,
6563        /**
6564         * @event beforeremove
6565         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6566         * @param {Tree} tree The owner tree
6567         * @param {Node} parent The parent node
6568         * @param {Node} node The child node to be removed
6569         */
6570        "beforeremove" : true,
6571        /**
6572         * @event beforemove
6573         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6574         * @param {Tree} tree The owner tree
6575         * @param {Node} node The node being moved
6576         * @param {Node} oldParent The parent of the node
6577         * @param {Node} newParent The new parent the node is moving to
6578         * @param {Number} index The index it is being moved to
6579         */
6580        "beforemove" : true,
6581        /**
6582         * @event beforeinsert
6583         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be inserted
6587         * @param {Node} refNode The child node the node is being inserted before
6588         */
6589        "beforeinsert" : true
6590    });
6591
6592     Roo.data.Tree.superclass.constructor.call(this);
6593 };
6594
6595 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6596     pathSeparator: "/",
6597
6598     proxyNodeEvent : function(){
6599         return this.fireEvent.apply(this, arguments);
6600     },
6601
6602     /**
6603      * Returns the root node for this tree.
6604      * @return {Node}
6605      */
6606     getRootNode : function(){
6607         return this.root;
6608     },
6609
6610     /**
6611      * Sets the root node for this tree.
6612      * @param {Node} node
6613      * @return {Node}
6614      */
6615     setRootNode : function(node){
6616         this.root = node;
6617         node.ownerTree = this;
6618         node.isRoot = true;
6619         this.registerNode(node);
6620         return node;
6621     },
6622
6623     /**
6624      * Gets a node in this tree by its id.
6625      * @param {String} id
6626      * @return {Node}
6627      */
6628     getNodeById : function(id){
6629         return this.nodeHash[id];
6630     },
6631
6632     registerNode : function(node){
6633         this.nodeHash[node.id] = node;
6634     },
6635
6636     unregisterNode : function(node){
6637         delete this.nodeHash[node.id];
6638     },
6639
6640     toString : function(){
6641         return "[Tree"+(this.id?" "+this.id:"")+"]";
6642     }
6643 });
6644
6645 /**
6646  * @class Roo.data.Node
6647  * @extends Roo.util.Observable
6648  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6649  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6650  * @constructor
6651  * @param {Object} attributes The attributes/config for the node
6652  */
6653 Roo.data.Node = function(attributes){
6654     /**
6655      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6656      * @type {Object}
6657      */
6658     this.attributes = attributes || {};
6659     this.leaf = this.attributes.leaf;
6660     /**
6661      * The node id. @type String
6662      */
6663     this.id = this.attributes.id;
6664     if(!this.id){
6665         this.id = Roo.id(null, "ynode-");
6666         this.attributes.id = this.id;
6667     }
6668     /**
6669      * All child nodes of this node. @type Array
6670      */
6671     this.childNodes = [];
6672     if(!this.childNodes.indexOf){ // indexOf is a must
6673         this.childNodes.indexOf = function(o){
6674             for(var i = 0, len = this.length; i < len; i++){
6675                 if(this[i] == o) {
6676                     return i;
6677                 }
6678             }
6679             return -1;
6680         };
6681     }
6682     /**
6683      * The parent node for this node. @type Node
6684      */
6685     this.parentNode = null;
6686     /**
6687      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6688      */
6689     this.firstChild = null;
6690     /**
6691      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6692      */
6693     this.lastChild = null;
6694     /**
6695      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6696      */
6697     this.previousSibling = null;
6698     /**
6699      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6700      */
6701     this.nextSibling = null;
6702
6703     this.addEvents({
6704        /**
6705         * @event append
6706         * Fires when a new child node is appended
6707         * @param {Tree} tree The owner tree
6708         * @param {Node} this This node
6709         * @param {Node} node The newly appended node
6710         * @param {Number} index The index of the newly appended node
6711         */
6712        "append" : true,
6713        /**
6714         * @event remove
6715         * Fires when a child node is removed
6716         * @param {Tree} tree The owner tree
6717         * @param {Node} this This node
6718         * @param {Node} node The removed node
6719         */
6720        "remove" : true,
6721        /**
6722         * @event move
6723         * Fires when this node is moved to a new location in the tree
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} oldParent The old parent of this node
6727         * @param {Node} newParent The new parent of this node
6728         * @param {Number} index The index it was moved to
6729         */
6730        "move" : true,
6731        /**
6732         * @event insert
6733         * Fires when a new child node is inserted.
6734         * @param {Tree} tree The owner tree
6735         * @param {Node} this This node
6736         * @param {Node} node The child node inserted
6737         * @param {Node} refNode The child node the node was inserted before
6738         */
6739        "insert" : true,
6740        /**
6741         * @event beforeappend
6742         * Fires before a new child is appended, return false to cancel the append.
6743         * @param {Tree} tree The owner tree
6744         * @param {Node} this This node
6745         * @param {Node} node The child node to be appended
6746         */
6747        "beforeappend" : true,
6748        /**
6749         * @event beforeremove
6750         * Fires before a child is removed, return false to cancel the remove.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be removed
6754         */
6755        "beforeremove" : true,
6756        /**
6757         * @event beforemove
6758         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} oldParent The parent of this node
6762         * @param {Node} newParent The new parent this node is moving to
6763         * @param {Number} index The index it is being moved to
6764         */
6765        "beforemove" : true,
6766        /**
6767         * @event beforeinsert
6768         * Fires before a new child is inserted, return false to cancel the insert.
6769         * @param {Tree} tree The owner tree
6770         * @param {Node} this This node
6771         * @param {Node} node The child node to be inserted
6772         * @param {Node} refNode The child node the node is being inserted before
6773         */
6774        "beforeinsert" : true
6775    });
6776     this.listeners = this.attributes.listeners;
6777     Roo.data.Node.superclass.constructor.call(this);
6778 };
6779
6780 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6781     fireEvent : function(evtName){
6782         // first do standard event for this node
6783         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6784             return false;
6785         }
6786         // then bubble it up to the tree if the event wasn't cancelled
6787         var ot = this.getOwnerTree();
6788         if(ot){
6789             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6790                 return false;
6791             }
6792         }
6793         return true;
6794     },
6795
6796     /**
6797      * Returns true if this node is a leaf
6798      * @return {Boolean}
6799      */
6800     isLeaf : function(){
6801         return this.leaf === true;
6802     },
6803
6804     // private
6805     setFirstChild : function(node){
6806         this.firstChild = node;
6807     },
6808
6809     //private
6810     setLastChild : function(node){
6811         this.lastChild = node;
6812     },
6813
6814
6815     /**
6816      * Returns true if this node is the last child of its parent
6817      * @return {Boolean}
6818      */
6819     isLast : function(){
6820        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6821     },
6822
6823     /**
6824      * Returns true if this node is the first child of its parent
6825      * @return {Boolean}
6826      */
6827     isFirst : function(){
6828        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6829     },
6830
6831     hasChildNodes : function(){
6832         return !this.isLeaf() && this.childNodes.length > 0;
6833     },
6834
6835     /**
6836      * Insert node(s) as the last child node of this node.
6837      * @param {Node/Array} node The node or Array of nodes to append
6838      * @return {Node} The appended node if single append, or null if an array was passed
6839      */
6840     appendChild : function(node){
6841         var multi = false;
6842         if(node instanceof Array){
6843             multi = node;
6844         }else if(arguments.length > 1){
6845             multi = arguments;
6846         }
6847         // if passed an array or multiple args do them one by one
6848         if(multi){
6849             for(var i = 0, len = multi.length; i < len; i++) {
6850                 this.appendChild(multi[i]);
6851             }
6852         }else{
6853             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6854                 return false;
6855             }
6856             var index = this.childNodes.length;
6857             var oldParent = node.parentNode;
6858             // it's a move, make sure we move it cleanly
6859             if(oldParent){
6860                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6861                     return false;
6862                 }
6863                 oldParent.removeChild(node);
6864             }
6865             index = this.childNodes.length;
6866             if(index == 0){
6867                 this.setFirstChild(node);
6868             }
6869             this.childNodes.push(node);
6870             node.parentNode = this;
6871             var ps = this.childNodes[index-1];
6872             if(ps){
6873                 node.previousSibling = ps;
6874                 ps.nextSibling = node;
6875             }else{
6876                 node.previousSibling = null;
6877             }
6878             node.nextSibling = null;
6879             this.setLastChild(node);
6880             node.setOwnerTree(this.getOwnerTree());
6881             this.fireEvent("append", this.ownerTree, this, node, index);
6882             if(oldParent){
6883                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6884             }
6885             return node;
6886         }
6887     },
6888
6889     /**
6890      * Removes a child node from this node.
6891      * @param {Node} node The node to remove
6892      * @return {Node} The removed node
6893      */
6894     removeChild : function(node){
6895         var index = this.childNodes.indexOf(node);
6896         if(index == -1){
6897             return false;
6898         }
6899         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6900             return false;
6901         }
6902
6903         // remove it from childNodes collection
6904         this.childNodes.splice(index, 1);
6905
6906         // update siblings
6907         if(node.previousSibling){
6908             node.previousSibling.nextSibling = node.nextSibling;
6909         }
6910         if(node.nextSibling){
6911             node.nextSibling.previousSibling = node.previousSibling;
6912         }
6913
6914         // update child refs
6915         if(this.firstChild == node){
6916             this.setFirstChild(node.nextSibling);
6917         }
6918         if(this.lastChild == node){
6919             this.setLastChild(node.previousSibling);
6920         }
6921
6922         node.setOwnerTree(null);
6923         // clear any references from the node
6924         node.parentNode = null;
6925         node.previousSibling = null;
6926         node.nextSibling = null;
6927         this.fireEvent("remove", this.ownerTree, this, node);
6928         return node;
6929     },
6930
6931     /**
6932      * Inserts the first node before the second node in this nodes childNodes collection.
6933      * @param {Node} node The node to insert
6934      * @param {Node} refNode The node to insert before (if null the node is appended)
6935      * @return {Node} The inserted node
6936      */
6937     insertBefore : function(node, refNode){
6938         if(!refNode){ // like standard Dom, refNode can be null for append
6939             return this.appendChild(node);
6940         }
6941         // nothing to do
6942         if(node == refNode){
6943             return false;
6944         }
6945
6946         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6947             return false;
6948         }
6949         var index = this.childNodes.indexOf(refNode);
6950         var oldParent = node.parentNode;
6951         var refIndex = index;
6952
6953         // when moving internally, indexes will change after remove
6954         if(oldParent == this && this.childNodes.indexOf(node) < index){
6955             refIndex--;
6956         }
6957
6958         // it's a move, make sure we move it cleanly
6959         if(oldParent){
6960             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6961                 return false;
6962             }
6963             oldParent.removeChild(node);
6964         }
6965         if(refIndex == 0){
6966             this.setFirstChild(node);
6967         }
6968         this.childNodes.splice(refIndex, 0, node);
6969         node.parentNode = this;
6970         var ps = this.childNodes[refIndex-1];
6971         if(ps){
6972             node.previousSibling = ps;
6973             ps.nextSibling = node;
6974         }else{
6975             node.previousSibling = null;
6976         }
6977         node.nextSibling = refNode;
6978         refNode.previousSibling = node;
6979         node.setOwnerTree(this.getOwnerTree());
6980         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6981         if(oldParent){
6982             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6983         }
6984         return node;
6985     },
6986
6987     /**
6988      * Returns the child node at the specified index.
6989      * @param {Number} index
6990      * @return {Node}
6991      */
6992     item : function(index){
6993         return this.childNodes[index];
6994     },
6995
6996     /**
6997      * Replaces one child node in this node with another.
6998      * @param {Node} newChild The replacement node
6999      * @param {Node} oldChild The node to replace
7000      * @return {Node} The replaced node
7001      */
7002     replaceChild : function(newChild, oldChild){
7003         this.insertBefore(newChild, oldChild);
7004         this.removeChild(oldChild);
7005         return oldChild;
7006     },
7007
7008     /**
7009      * Returns the index of a child node
7010      * @param {Node} node
7011      * @return {Number} The index of the node or -1 if it was not found
7012      */
7013     indexOf : function(child){
7014         return this.childNodes.indexOf(child);
7015     },
7016
7017     /**
7018      * Returns the tree this node is in.
7019      * @return {Tree}
7020      */
7021     getOwnerTree : function(){
7022         // if it doesn't have one, look for one
7023         if(!this.ownerTree){
7024             var p = this;
7025             while(p){
7026                 if(p.ownerTree){
7027                     this.ownerTree = p.ownerTree;
7028                     break;
7029                 }
7030                 p = p.parentNode;
7031             }
7032         }
7033         return this.ownerTree;
7034     },
7035
7036     /**
7037      * Returns depth of this node (the root node has a depth of 0)
7038      * @return {Number}
7039      */
7040     getDepth : function(){
7041         var depth = 0;
7042         var p = this;
7043         while(p.parentNode){
7044             ++depth;
7045             p = p.parentNode;
7046         }
7047         return depth;
7048     },
7049
7050     // private
7051     setOwnerTree : function(tree){
7052         // if it's move, we need to update everyone
7053         if(tree != this.ownerTree){
7054             if(this.ownerTree){
7055                 this.ownerTree.unregisterNode(this);
7056             }
7057             this.ownerTree = tree;
7058             var cs = this.childNodes;
7059             for(var i = 0, len = cs.length; i < len; i++) {
7060                 cs[i].setOwnerTree(tree);
7061             }
7062             if(tree){
7063                 tree.registerNode(this);
7064             }
7065         }
7066     },
7067
7068     /**
7069      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7070      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7071      * @return {String} The path
7072      */
7073     getPath : function(attr){
7074         attr = attr || "id";
7075         var p = this.parentNode;
7076         var b = [this.attributes[attr]];
7077         while(p){
7078             b.unshift(p.attributes[attr]);
7079             p = p.parentNode;
7080         }
7081         var sep = this.getOwnerTree().pathSeparator;
7082         return sep + b.join(sep);
7083     },
7084
7085     /**
7086      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7087      * function call will be the scope provided or the current node. The arguments to the function
7088      * will be the args provided or the current node. If the function returns false at any point,
7089      * the bubble is stopped.
7090      * @param {Function} fn The function to call
7091      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7092      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7093      */
7094     bubble : function(fn, scope, args){
7095         var p = this;
7096         while(p){
7097             if(fn.call(scope || p, args || p) === false){
7098                 break;
7099             }
7100             p = p.parentNode;
7101         }
7102     },
7103
7104     /**
7105      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7106      * function call will be the scope provided or the current node. The arguments to the function
7107      * will be the args provided or the current node. If the function returns false at any point,
7108      * the cascade is stopped on that branch.
7109      * @param {Function} fn The function to call
7110      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7111      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7112      */
7113     cascade : function(fn, scope, args){
7114         if(fn.call(scope || this, args || this) !== false){
7115             var cs = this.childNodes;
7116             for(var i = 0, len = cs.length; i < len; i++) {
7117                 cs[i].cascade(fn, scope, args);
7118             }
7119         }
7120     },
7121
7122     /**
7123      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7124      * function call will be the scope provided or the current node. The arguments to the function
7125      * will be the args provided or the current node. If the function returns false at any point,
7126      * the iteration stops.
7127      * @param {Function} fn The function to call
7128      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7129      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7130      */
7131     eachChild : function(fn, scope, args){
7132         var cs = this.childNodes;
7133         for(var i = 0, len = cs.length; i < len; i++) {
7134                 if(fn.call(scope || this, args || cs[i]) === false){
7135                     break;
7136                 }
7137         }
7138     },
7139
7140     /**
7141      * Finds the first child that has the attribute with the specified value.
7142      * @param {String} attribute The attribute name
7143      * @param {Mixed} value The value to search for
7144      * @return {Node} The found child or null if none was found
7145      */
7146     findChild : function(attribute, value){
7147         var cs = this.childNodes;
7148         for(var i = 0, len = cs.length; i < len; i++) {
7149                 if(cs[i].attributes[attribute] == value){
7150                     return cs[i];
7151                 }
7152         }
7153         return null;
7154     },
7155
7156     /**
7157      * Finds the first child by a custom function. The child matches if the function passed
7158      * returns true.
7159      * @param {Function} fn
7160      * @param {Object} scope (optional)
7161      * @return {Node} The found child or null if none was found
7162      */
7163     findChildBy : function(fn, scope){
7164         var cs = this.childNodes;
7165         for(var i = 0, len = cs.length; i < len; i++) {
7166                 if(fn.call(scope||cs[i], cs[i]) === true){
7167                     return cs[i];
7168                 }
7169         }
7170         return null;
7171     },
7172
7173     /**
7174      * Sorts this nodes children using the supplied sort function
7175      * @param {Function} fn
7176      * @param {Object} scope (optional)
7177      */
7178     sort : function(fn, scope){
7179         var cs = this.childNodes;
7180         var len = cs.length;
7181         if(len > 0){
7182             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7183             cs.sort(sortFn);
7184             for(var i = 0; i < len; i++){
7185                 var n = cs[i];
7186                 n.previousSibling = cs[i-1];
7187                 n.nextSibling = cs[i+1];
7188                 if(i == 0){
7189                     this.setFirstChild(n);
7190                 }
7191                 if(i == len-1){
7192                     this.setLastChild(n);
7193                 }
7194             }
7195         }
7196     },
7197
7198     /**
7199      * Returns true if this node is an ancestor (at any point) of the passed node.
7200      * @param {Node} node
7201      * @return {Boolean}
7202      */
7203     contains : function(node){
7204         return node.isAncestor(this);
7205     },
7206
7207     /**
7208      * Returns true if the passed node is an ancestor (at any point) of this node.
7209      * @param {Node} node
7210      * @return {Boolean}
7211      */
7212     isAncestor : function(node){
7213         var p = this.parentNode;
7214         while(p){
7215             if(p == node){
7216                 return true;
7217             }
7218             p = p.parentNode;
7219         }
7220         return false;
7221     },
7222
7223     toString : function(){
7224         return "[Node"+(this.id?" "+this.id:"")+"]";
7225     }
7226 });/*
7227  * Based on:
7228  * Ext JS Library 1.1.1
7229  * Copyright(c) 2006-2007, Ext JS, LLC.
7230  *
7231  * Originally Released Under LGPL - original licence link has changed is not relivant.
7232  *
7233  * Fork - LGPL
7234  * <script type="text/javascript">
7235  */
7236  
7237
7238 /**
7239  * @class Roo.ComponentMgr
7240  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7241  * @singleton
7242  */
7243 Roo.ComponentMgr = function(){
7244     var all = new Roo.util.MixedCollection();
7245
7246     return {
7247         /**
7248          * Registers a component.
7249          * @param {Roo.Component} c The component
7250          */
7251         register : function(c){
7252             all.add(c);
7253         },
7254
7255         /**
7256          * Unregisters a component.
7257          * @param {Roo.Component} c The component
7258          */
7259         unregister : function(c){
7260             all.remove(c);
7261         },
7262
7263         /**
7264          * Returns a component by id
7265          * @param {String} id The component id
7266          */
7267         get : function(id){
7268             return all.get(id);
7269         },
7270
7271         /**
7272          * Registers a function that will be called when a specified component is added to ComponentMgr
7273          * @param {String} id The component id
7274          * @param {Funtction} fn The callback function
7275          * @param {Object} scope The scope of the callback
7276          */
7277         onAvailable : function(id, fn, scope){
7278             all.on("add", function(index, o){
7279                 if(o.id == id){
7280                     fn.call(scope || o, o);
7281                     all.un("add", fn, scope);
7282                 }
7283             });
7284         }
7285     };
7286 }();/*
7287  * Based on:
7288  * Ext JS Library 1.1.1
7289  * Copyright(c) 2006-2007, Ext JS, LLC.
7290  *
7291  * Originally Released Under LGPL - original licence link has changed is not relivant.
7292  *
7293  * Fork - LGPL
7294  * <script type="text/javascript">
7295  */
7296  
7297 /**
7298  * @class Roo.Component
7299  * @extends Roo.util.Observable
7300  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7301  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7302  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7303  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7304  * All visual components (widgets) that require rendering into a layout should subclass Component.
7305  * @constructor
7306  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7307  * 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
7308  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7309  */
7310 Roo.Component = function(config){
7311     config = config || {};
7312     if(config.tagName || config.dom || typeof config == "string"){ // element object
7313         config = {el: config, id: config.id || config};
7314     }
7315     this.initialConfig = config;
7316
7317     Roo.apply(this, config);
7318     this.addEvents({
7319         /**
7320          * @event disable
7321          * Fires after the component is disabled.
7322              * @param {Roo.Component} this
7323              */
7324         disable : true,
7325         /**
7326          * @event enable
7327          * Fires after the component is enabled.
7328              * @param {Roo.Component} this
7329              */
7330         enable : true,
7331         /**
7332          * @event beforeshow
7333          * Fires before the component is shown.  Return false to stop the show.
7334              * @param {Roo.Component} this
7335              */
7336         beforeshow : true,
7337         /**
7338          * @event show
7339          * Fires after the component is shown.
7340              * @param {Roo.Component} this
7341              */
7342         show : true,
7343         /**
7344          * @event beforehide
7345          * Fires before the component is hidden. Return false to stop the hide.
7346              * @param {Roo.Component} this
7347              */
7348         beforehide : true,
7349         /**
7350          * @event hide
7351          * Fires after the component is hidden.
7352              * @param {Roo.Component} this
7353              */
7354         hide : true,
7355         /**
7356          * @event beforerender
7357          * Fires before the component is rendered. Return false to stop the render.
7358              * @param {Roo.Component} this
7359              */
7360         beforerender : true,
7361         /**
7362          * @event render
7363          * Fires after the component is rendered.
7364              * @param {Roo.Component} this
7365              */
7366         render : true,
7367         /**
7368          * @event beforedestroy
7369          * Fires before the component is destroyed. Return false to stop the destroy.
7370              * @param {Roo.Component} this
7371              */
7372         beforedestroy : true,
7373         /**
7374          * @event destroy
7375          * Fires after the component is destroyed.
7376              * @param {Roo.Component} this
7377              */
7378         destroy : true
7379     });
7380     if(!this.id){
7381         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7382     }
7383     Roo.ComponentMgr.register(this);
7384     Roo.Component.superclass.constructor.call(this);
7385     this.initComponent();
7386     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7387         this.render(this.renderTo);
7388         delete this.renderTo;
7389     }
7390 };
7391
7392 // private
7393 Roo.Component.AUTO_ID = 1000;
7394
7395 Roo.extend(Roo.Component, Roo.util.Observable, {
7396     /**
7397      * @property {Boolean} hidden
7398      * true if this component is hidden. Read-only.
7399      */
7400     hidden : false,
7401     /**
7402      * true if this component is disabled. Read-only.
7403      */
7404     disabled : false,
7405     /**
7406      * true if this component has been rendered. Read-only.
7407      */
7408     rendered : false,
7409     
7410     /** @cfg {String} disableClass
7411      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7412      */
7413     disabledClass : "x-item-disabled",
7414         /** @cfg {Boolean} allowDomMove
7415          * Whether the component can move the Dom node when rendering (defaults to true).
7416          */
7417     allowDomMove : true,
7418     /** @cfg {String} hideMode
7419      * How this component should hidden. Supported values are
7420      * "visibility" (css visibility), "offsets" (negative offset position) and
7421      * "display" (css display) - defaults to "display".
7422      */
7423     hideMode: 'display',
7424
7425     // private
7426     ctype : "Roo.Component",
7427
7428     /** @cfg {String} actionMode 
7429      * which property holds the element that used for  hide() / show() / disable() / enable()
7430      * default is 'el' 
7431      */
7432     actionMode : "el",
7433
7434     // private
7435     getActionEl : function(){
7436         return this[this.actionMode];
7437     },
7438
7439     initComponent : Roo.emptyFn,
7440     /**
7441      * If this is a lazy rendering component, render it to its container element.
7442      * @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.
7443      */
7444     render : function(container, position){
7445         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7446             if(!container && this.el){
7447                 this.el = Roo.get(this.el);
7448                 container = this.el.dom.parentNode;
7449                 this.allowDomMove = false;
7450             }
7451             this.container = Roo.get(container);
7452             this.rendered = true;
7453             if(position !== undefined){
7454                 if(typeof position == 'number'){
7455                     position = this.container.dom.childNodes[position];
7456                 }else{
7457                     position = Roo.getDom(position);
7458                 }
7459             }
7460             this.onRender(this.container, position || null);
7461             if(this.cls){
7462                 this.el.addClass(this.cls);
7463                 delete this.cls;
7464             }
7465             if(this.style){
7466                 this.el.applyStyles(this.style);
7467                 delete this.style;
7468             }
7469             this.fireEvent("render", this);
7470             this.afterRender(this.container);
7471             if(this.hidden){
7472                 this.hide();
7473             }
7474             if(this.disabled){
7475                 this.disable();
7476             }
7477         }
7478         return this;
7479     },
7480
7481     // private
7482     // default function is not really useful
7483     onRender : function(ct, position){
7484         if(this.el){
7485             this.el = Roo.get(this.el);
7486             if(this.allowDomMove !== false){
7487                 ct.dom.insertBefore(this.el.dom, position);
7488             }
7489         }
7490     },
7491
7492     // private
7493     getAutoCreate : function(){
7494         var cfg = typeof this.autoCreate == "object" ?
7495                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7496         if(this.id && !cfg.id){
7497             cfg.id = this.id;
7498         }
7499         return cfg;
7500     },
7501
7502     // private
7503     afterRender : Roo.emptyFn,
7504
7505     /**
7506      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7507      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7508      */
7509     destroy : function(){
7510         if(this.fireEvent("beforedestroy", this) !== false){
7511             this.purgeListeners();
7512             this.beforeDestroy();
7513             if(this.rendered){
7514                 this.el.removeAllListeners();
7515                 this.el.remove();
7516                 if(this.actionMode == "container"){
7517                     this.container.remove();
7518                 }
7519             }
7520             this.onDestroy();
7521             Roo.ComponentMgr.unregister(this);
7522             this.fireEvent("destroy", this);
7523         }
7524     },
7525
7526         // private
7527     beforeDestroy : function(){
7528
7529     },
7530
7531         // private
7532         onDestroy : function(){
7533
7534     },
7535
7536     /**
7537      * Returns the underlying {@link Roo.Element}.
7538      * @return {Roo.Element} The element
7539      */
7540     getEl : function(){
7541         return this.el;
7542     },
7543
7544     /**
7545      * Returns the id of this component.
7546      * @return {String}
7547      */
7548     getId : function(){
7549         return this.id;
7550     },
7551
7552     /**
7553      * Try to focus this component.
7554      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7555      * @return {Roo.Component} this
7556      */
7557     focus : function(selectText){
7558         if(this.rendered){
7559             this.el.focus();
7560             if(selectText === true){
7561                 this.el.dom.select();
7562             }
7563         }
7564         return this;
7565     },
7566
7567     // private
7568     blur : function(){
7569         if(this.rendered){
7570             this.el.blur();
7571         }
7572         return this;
7573     },
7574
7575     /**
7576      * Disable this component.
7577      * @return {Roo.Component} this
7578      */
7579     disable : function(){
7580         if(this.rendered){
7581             this.onDisable();
7582         }
7583         this.disabled = true;
7584         this.fireEvent("disable", this);
7585         return this;
7586     },
7587
7588         // private
7589     onDisable : function(){
7590         this.getActionEl().addClass(this.disabledClass);
7591         this.el.dom.disabled = true;
7592     },
7593
7594     /**
7595      * Enable this component.
7596      * @return {Roo.Component} this
7597      */
7598     enable : function(){
7599         if(this.rendered){
7600             this.onEnable();
7601         }
7602         this.disabled = false;
7603         this.fireEvent("enable", this);
7604         return this;
7605     },
7606
7607         // private
7608     onEnable : function(){
7609         this.getActionEl().removeClass(this.disabledClass);
7610         this.el.dom.disabled = false;
7611     },
7612
7613     /**
7614      * Convenience function for setting disabled/enabled by boolean.
7615      * @param {Boolean} disabled
7616      */
7617     setDisabled : function(disabled){
7618         this[disabled ? "disable" : "enable"]();
7619     },
7620
7621     /**
7622      * Show this component.
7623      * @return {Roo.Component} this
7624      */
7625     show: function(){
7626         if(this.fireEvent("beforeshow", this) !== false){
7627             this.hidden = false;
7628             if(this.rendered){
7629                 this.onShow();
7630             }
7631             this.fireEvent("show", this);
7632         }
7633         return this;
7634     },
7635
7636     // private
7637     onShow : function(){
7638         var ae = this.getActionEl();
7639         if(this.hideMode == 'visibility'){
7640             ae.dom.style.visibility = "visible";
7641         }else if(this.hideMode == 'offsets'){
7642             ae.removeClass('x-hidden');
7643         }else{
7644             ae.dom.style.display = "";
7645         }
7646     },
7647
7648     /**
7649      * Hide this component.
7650      * @return {Roo.Component} this
7651      */
7652     hide: function(){
7653         if(this.fireEvent("beforehide", this) !== false){
7654             this.hidden = true;
7655             if(this.rendered){
7656                 this.onHide();
7657             }
7658             this.fireEvent("hide", this);
7659         }
7660         return this;
7661     },
7662
7663     // private
7664     onHide : function(){
7665         var ae = this.getActionEl();
7666         if(this.hideMode == 'visibility'){
7667             ae.dom.style.visibility = "hidden";
7668         }else if(this.hideMode == 'offsets'){
7669             ae.addClass('x-hidden');
7670         }else{
7671             ae.dom.style.display = "none";
7672         }
7673     },
7674
7675     /**
7676      * Convenience function to hide or show this component by boolean.
7677      * @param {Boolean} visible True to show, false to hide
7678      * @return {Roo.Component} this
7679      */
7680     setVisible: function(visible){
7681         if(visible) {
7682             this.show();
7683         }else{
7684             this.hide();
7685         }
7686         return this;
7687     },
7688
7689     /**
7690      * Returns true if this component is visible.
7691      */
7692     isVisible : function(){
7693         return this.getActionEl().isVisible();
7694     },
7695
7696     cloneConfig : function(overrides){
7697         overrides = overrides || {};
7698         var id = overrides.id || Roo.id();
7699         var cfg = Roo.applyIf(overrides, this.initialConfig);
7700         cfg.id = id; // prevent dup id
7701         return new this.constructor(cfg);
7702     }
7703 });/*
7704  * Based on:
7705  * Ext JS Library 1.1.1
7706  * Copyright(c) 2006-2007, Ext JS, LLC.
7707  *
7708  * Originally Released Under LGPL - original licence link has changed is not relivant.
7709  *
7710  * Fork - LGPL
7711  * <script type="text/javascript">
7712  */
7713  (function(){ 
7714 /**
7715  * @class Roo.Layer
7716  * @extends Roo.Element
7717  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7718  * automatic maintaining of shadow/shim positions.
7719  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7720  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7721  * you can pass a string with a CSS class name. False turns off the shadow.
7722  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7723  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7724  * @cfg {String} cls CSS class to add to the element
7725  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7726  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7727  * @constructor
7728  * @param {Object} config An object with config options.
7729  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7730  */
7731
7732 Roo.Layer = function(config, existingEl){
7733     config = config || {};
7734     var dh = Roo.DomHelper;
7735     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7736     if(existingEl){
7737         this.dom = Roo.getDom(existingEl);
7738     }
7739     if(!this.dom){
7740         var o = config.dh || {tag: "div", cls: "x-layer"};
7741         this.dom = dh.append(pel, o);
7742     }
7743     if(config.cls){
7744         this.addClass(config.cls);
7745     }
7746     this.constrain = config.constrain !== false;
7747     this.visibilityMode = Roo.Element.VISIBILITY;
7748     if(config.id){
7749         this.id = this.dom.id = config.id;
7750     }else{
7751         this.id = Roo.id(this.dom);
7752     }
7753     this.zindex = config.zindex || this.getZIndex();
7754     this.position("absolute", this.zindex);
7755     if(config.shadow){
7756         this.shadowOffset = config.shadowOffset || 4;
7757         this.shadow = new Roo.Shadow({
7758             offset : this.shadowOffset,
7759             mode : config.shadow
7760         });
7761     }else{
7762         this.shadowOffset = 0;
7763     }
7764     this.useShim = config.shim !== false && Roo.useShims;
7765     this.useDisplay = config.useDisplay;
7766     this.hide();
7767 };
7768
7769 var supr = Roo.Element.prototype;
7770
7771 // shims are shared among layer to keep from having 100 iframes
7772 var shims = [];
7773
7774 Roo.extend(Roo.Layer, Roo.Element, {
7775
7776     getZIndex : function(){
7777         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7778     },
7779
7780     getShim : function(){
7781         if(!this.useShim){
7782             return null;
7783         }
7784         if(this.shim){
7785             return this.shim;
7786         }
7787         var shim = shims.shift();
7788         if(!shim){
7789             shim = this.createShim();
7790             shim.enableDisplayMode('block');
7791             shim.dom.style.display = 'none';
7792             shim.dom.style.visibility = 'visible';
7793         }
7794         var pn = this.dom.parentNode;
7795         if(shim.dom.parentNode != pn){
7796             pn.insertBefore(shim.dom, this.dom);
7797         }
7798         shim.setStyle('z-index', this.getZIndex()-2);
7799         this.shim = shim;
7800         return shim;
7801     },
7802
7803     hideShim : function(){
7804         if(this.shim){
7805             this.shim.setDisplayed(false);
7806             shims.push(this.shim);
7807             delete this.shim;
7808         }
7809     },
7810
7811     disableShadow : function(){
7812         if(this.shadow){
7813             this.shadowDisabled = true;
7814             this.shadow.hide();
7815             this.lastShadowOffset = this.shadowOffset;
7816             this.shadowOffset = 0;
7817         }
7818     },
7819
7820     enableShadow : function(show){
7821         if(this.shadow){
7822             this.shadowDisabled = false;
7823             this.shadowOffset = this.lastShadowOffset;
7824             delete this.lastShadowOffset;
7825             if(show){
7826                 this.sync(true);
7827             }
7828         }
7829     },
7830
7831     // private
7832     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7833     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7834     sync : function(doShow){
7835         var sw = this.shadow;
7836         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7837             var sh = this.getShim();
7838
7839             var w = this.getWidth(),
7840                 h = this.getHeight();
7841
7842             var l = this.getLeft(true),
7843                 t = this.getTop(true);
7844
7845             if(sw && !this.shadowDisabled){
7846                 if(doShow && !sw.isVisible()){
7847                     sw.show(this);
7848                 }else{
7849                     sw.realign(l, t, w, h);
7850                 }
7851                 if(sh){
7852                     if(doShow){
7853                        sh.show();
7854                     }
7855                     // fit the shim behind the shadow, so it is shimmed too
7856                     var a = sw.adjusts, s = sh.dom.style;
7857                     s.left = (Math.min(l, l+a.l))+"px";
7858                     s.top = (Math.min(t, t+a.t))+"px";
7859                     s.width = (w+a.w)+"px";
7860                     s.height = (h+a.h)+"px";
7861                 }
7862             }else if(sh){
7863                 if(doShow){
7864                    sh.show();
7865                 }
7866                 sh.setSize(w, h);
7867                 sh.setLeftTop(l, t);
7868             }
7869             
7870         }
7871     },
7872
7873     // private
7874     destroy : function(){
7875         this.hideShim();
7876         if(this.shadow){
7877             this.shadow.hide();
7878         }
7879         this.removeAllListeners();
7880         var pn = this.dom.parentNode;
7881         if(pn){
7882             pn.removeChild(this.dom);
7883         }
7884         Roo.Element.uncache(this.id);
7885     },
7886
7887     remove : function(){
7888         this.destroy();
7889     },
7890
7891     // private
7892     beginUpdate : function(){
7893         this.updating = true;
7894     },
7895
7896     // private
7897     endUpdate : function(){
7898         this.updating = false;
7899         this.sync(true);
7900     },
7901
7902     // private
7903     hideUnders : function(negOffset){
7904         if(this.shadow){
7905             this.shadow.hide();
7906         }
7907         this.hideShim();
7908     },
7909
7910     // private
7911     constrainXY : function(){
7912         if(this.constrain){
7913             var vw = Roo.lib.Dom.getViewWidth(),
7914                 vh = Roo.lib.Dom.getViewHeight();
7915             var s = Roo.get(document).getScroll();
7916
7917             var xy = this.getXY();
7918             var x = xy[0], y = xy[1];   
7919             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7920             // only move it if it needs it
7921             var moved = false;
7922             // first validate right/bottom
7923             if((x + w) > vw+s.left){
7924                 x = vw - w - this.shadowOffset;
7925                 moved = true;
7926             }
7927             if((y + h) > vh+s.top){
7928                 y = vh - h - this.shadowOffset;
7929                 moved = true;
7930             }
7931             // then make sure top/left isn't negative
7932             if(x < s.left){
7933                 x = s.left;
7934                 moved = true;
7935             }
7936             if(y < s.top){
7937                 y = s.top;
7938                 moved = true;
7939             }
7940             if(moved){
7941                 if(this.avoidY){
7942                     var ay = this.avoidY;
7943                     if(y <= ay && (y+h) >= ay){
7944                         y = ay-h-5;   
7945                     }
7946                 }
7947                 xy = [x, y];
7948                 this.storeXY(xy);
7949                 supr.setXY.call(this, xy);
7950                 this.sync();
7951             }
7952         }
7953     },
7954
7955     isVisible : function(){
7956         return this.visible;    
7957     },
7958
7959     // private
7960     showAction : function(){
7961         this.visible = true; // track visibility to prevent getStyle calls
7962         if(this.useDisplay === true){
7963             this.setDisplayed("");
7964         }else if(this.lastXY){
7965             supr.setXY.call(this, this.lastXY);
7966         }else if(this.lastLT){
7967             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7968         }
7969     },
7970
7971     // private
7972     hideAction : function(){
7973         this.visible = false;
7974         if(this.useDisplay === true){
7975             this.setDisplayed(false);
7976         }else{
7977             this.setLeftTop(-10000,-10000);
7978         }
7979     },
7980
7981     // overridden Element method
7982     setVisible : function(v, a, d, c, e){
7983         if(v){
7984             this.showAction();
7985         }
7986         if(a && v){
7987             var cb = function(){
7988                 this.sync(true);
7989                 if(c){
7990                     c();
7991                 }
7992             }.createDelegate(this);
7993             supr.setVisible.call(this, true, true, d, cb, e);
7994         }else{
7995             if(!v){
7996                 this.hideUnders(true);
7997             }
7998             var cb = c;
7999             if(a){
8000                 cb = function(){
8001                     this.hideAction();
8002                     if(c){
8003                         c();
8004                     }
8005                 }.createDelegate(this);
8006             }
8007             supr.setVisible.call(this, v, a, d, cb, e);
8008             if(v){
8009                 this.sync(true);
8010             }else if(!a){
8011                 this.hideAction();
8012             }
8013         }
8014     },
8015
8016     storeXY : function(xy){
8017         delete this.lastLT;
8018         this.lastXY = xy;
8019     },
8020
8021     storeLeftTop : function(left, top){
8022         delete this.lastXY;
8023         this.lastLT = [left, top];
8024     },
8025
8026     // private
8027     beforeFx : function(){
8028         this.beforeAction();
8029         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8030     },
8031
8032     // private
8033     afterFx : function(){
8034         Roo.Layer.superclass.afterFx.apply(this, arguments);
8035         this.sync(this.isVisible());
8036     },
8037
8038     // private
8039     beforeAction : function(){
8040         if(!this.updating && this.shadow){
8041             this.shadow.hide();
8042         }
8043     },
8044
8045     // overridden Element method
8046     setLeft : function(left){
8047         this.storeLeftTop(left, this.getTop(true));
8048         supr.setLeft.apply(this, arguments);
8049         this.sync();
8050     },
8051
8052     setTop : function(top){
8053         this.storeLeftTop(this.getLeft(true), top);
8054         supr.setTop.apply(this, arguments);
8055         this.sync();
8056     },
8057
8058     setLeftTop : function(left, top){
8059         this.storeLeftTop(left, top);
8060         supr.setLeftTop.apply(this, arguments);
8061         this.sync();
8062     },
8063
8064     setXY : function(xy, a, d, c, e){
8065         this.fixDisplay();
8066         this.beforeAction();
8067         this.storeXY(xy);
8068         var cb = this.createCB(c);
8069         supr.setXY.call(this, xy, a, d, cb, e);
8070         if(!a){
8071             cb();
8072         }
8073     },
8074
8075     // private
8076     createCB : function(c){
8077         var el = this;
8078         return function(){
8079             el.constrainXY();
8080             el.sync(true);
8081             if(c){
8082                 c();
8083             }
8084         };
8085     },
8086
8087     // overridden Element method
8088     setX : function(x, a, d, c, e){
8089         this.setXY([x, this.getY()], a, d, c, e);
8090     },
8091
8092     // overridden Element method
8093     setY : function(y, a, d, c, e){
8094         this.setXY([this.getX(), y], a, d, c, e);
8095     },
8096
8097     // overridden Element method
8098     setSize : function(w, h, a, d, c, e){
8099         this.beforeAction();
8100         var cb = this.createCB(c);
8101         supr.setSize.call(this, w, h, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // overridden Element method
8108     setWidth : function(w, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setWidth.call(this, w, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setHeight : function(h, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setHeight.call(this, h, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setBounds : function(x, y, w, h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         if(!a){
8132             this.storeXY([x, y]);
8133             supr.setXY.call(this, [x, y]);
8134             supr.setSize.call(this, w, h, a, d, cb, e);
8135             cb();
8136         }else{
8137             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8138         }
8139         return this;
8140     },
8141     
8142     /**
8143      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8144      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8145      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8146      * @param {Number} zindex The new z-index to set
8147      * @return {this} The Layer
8148      */
8149     setZIndex : function(zindex){
8150         this.zindex = zindex;
8151         this.setStyle("z-index", zindex + 2);
8152         if(this.shadow){
8153             this.shadow.setZIndex(zindex + 1);
8154         }
8155         if(this.shim){
8156             this.shim.setStyle("z-index", zindex);
8157         }
8158     }
8159 });
8160 })();/*
8161  * Based on:
8162  * Ext JS Library 1.1.1
8163  * Copyright(c) 2006-2007, Ext JS, LLC.
8164  *
8165  * Originally Released Under LGPL - original licence link has changed is not relivant.
8166  *
8167  * Fork - LGPL
8168  * <script type="text/javascript">
8169  */
8170
8171
8172 /**
8173  * @class Roo.Shadow
8174  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8175  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8176  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8177  * @constructor
8178  * Create a new Shadow
8179  * @param {Object} config The config object
8180  */
8181 Roo.Shadow = function(config){
8182     Roo.apply(this, config);
8183     if(typeof this.mode != "string"){
8184         this.mode = this.defaultMode;
8185     }
8186     var o = this.offset, a = {h: 0};
8187     var rad = Math.floor(this.offset/2);
8188     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8189         case "drop":
8190             a.w = 0;
8191             a.l = a.t = o;
8192             a.t -= 1;
8193             if(Roo.isIE){
8194                 a.l -= this.offset + rad;
8195                 a.t -= this.offset + rad;
8196                 a.w -= rad;
8197                 a.h -= rad;
8198                 a.t += 1;
8199             }
8200         break;
8201         case "sides":
8202             a.w = (o*2);
8203             a.l = -o;
8204             a.t = o-1;
8205             if(Roo.isIE){
8206                 a.l -= (this.offset - rad);
8207                 a.t -= this.offset + rad;
8208                 a.l += 1;
8209                 a.w -= (this.offset - rad)*2;
8210                 a.w -= rad + 1;
8211                 a.h -= 1;
8212             }
8213         break;
8214         case "frame":
8215             a.w = a.h = (o*2);
8216             a.l = a.t = -o;
8217             a.t += 1;
8218             a.h -= 2;
8219             if(Roo.isIE){
8220                 a.l -= (this.offset - rad);
8221                 a.t -= (this.offset - rad);
8222                 a.l += 1;
8223                 a.w -= (this.offset + rad + 1);
8224                 a.h -= (this.offset + rad);
8225                 a.h += 1;
8226             }
8227         break;
8228     };
8229
8230     this.adjusts = a;
8231 };
8232
8233 Roo.Shadow.prototype = {
8234     /**
8235      * @cfg {String} mode
8236      * The shadow display mode.  Supports the following options:<br />
8237      * sides: Shadow displays on both sides and bottom only<br />
8238      * frame: Shadow displays equally on all four sides<br />
8239      * drop: Traditional bottom-right drop shadow (default)
8240      */
8241     /**
8242      * @cfg {String} offset
8243      * The number of pixels to offset the shadow from the element (defaults to 4)
8244      */
8245     offset: 4,
8246
8247     // private
8248     defaultMode: "drop",
8249
8250     /**
8251      * Displays the shadow under the target element
8252      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8253      */
8254     show : function(target){
8255         target = Roo.get(target);
8256         if(!this.el){
8257             this.el = Roo.Shadow.Pool.pull();
8258             if(this.el.dom.nextSibling != target.dom){
8259                 this.el.insertBefore(target);
8260             }
8261         }
8262         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8263         if(Roo.isIE){
8264             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8265         }
8266         this.realign(
8267             target.getLeft(true),
8268             target.getTop(true),
8269             target.getWidth(),
8270             target.getHeight()
8271         );
8272         this.el.dom.style.display = "block";
8273     },
8274
8275     /**
8276      * Returns true if the shadow is visible, else false
8277      */
8278     isVisible : function(){
8279         return this.el ? true : false;  
8280     },
8281
8282     /**
8283      * Direct alignment when values are already available. Show must be called at least once before
8284      * calling this method to ensure it is initialized.
8285      * @param {Number} left The target element left position
8286      * @param {Number} top The target element top position
8287      * @param {Number} width The target element width
8288      * @param {Number} height The target element height
8289      */
8290     realign : function(l, t, w, h){
8291         if(!this.el){
8292             return;
8293         }
8294         var a = this.adjusts, d = this.el.dom, s = d.style;
8295         var iea = 0;
8296         s.left = (l+a.l)+"px";
8297         s.top = (t+a.t)+"px";
8298         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8299  
8300         if(s.width != sws || s.height != shs){
8301             s.width = sws;
8302             s.height = shs;
8303             if(!Roo.isIE){
8304                 var cn = d.childNodes;
8305                 var sww = Math.max(0, (sw-12))+"px";
8306                 cn[0].childNodes[1].style.width = sww;
8307                 cn[1].childNodes[1].style.width = sww;
8308                 cn[2].childNodes[1].style.width = sww;
8309                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8310             }
8311         }
8312     },
8313
8314     /**
8315      * Hides this shadow
8316      */
8317     hide : function(){
8318         if(this.el){
8319             this.el.dom.style.display = "none";
8320             Roo.Shadow.Pool.push(this.el);
8321             delete this.el;
8322         }
8323     },
8324
8325     /**
8326      * Adjust the z-index of this shadow
8327      * @param {Number} zindex The new z-index
8328      */
8329     setZIndex : function(z){
8330         this.zIndex = z;
8331         if(this.el){
8332             this.el.setStyle("z-index", z);
8333         }
8334     }
8335 };
8336
8337 // Private utility class that manages the internal Shadow cache
8338 Roo.Shadow.Pool = function(){
8339     var p = [];
8340     var markup = Roo.isIE ?
8341                  '<div class="x-ie-shadow"></div>' :
8342                  '<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>';
8343     return {
8344         pull : function(){
8345             var sh = p.shift();
8346             if(!sh){
8347                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8348                 sh.autoBoxAdjust = false;
8349             }
8350             return sh;
8351         },
8352
8353         push : function(sh){
8354             p.push(sh);
8355         }
8356     };
8357 }();/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367
8368 /**
8369  * @class Roo.BoxComponent
8370  * @extends Roo.Component
8371  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8372  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8373  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8374  * layout containers.
8375  * @constructor
8376  * @param {Roo.Element/String/Object} config The configuration options.
8377  */
8378 Roo.BoxComponent = function(config){
8379     Roo.Component.call(this, config);
8380     this.addEvents({
8381         /**
8382          * @event resize
8383          * Fires after the component is resized.
8384              * @param {Roo.Component} this
8385              * @param {Number} adjWidth The box-adjusted width that was set
8386              * @param {Number} adjHeight The box-adjusted height that was set
8387              * @param {Number} rawWidth The width that was originally specified
8388              * @param {Number} rawHeight The height that was originally specified
8389              */
8390         resize : true,
8391         /**
8392          * @event move
8393          * Fires after the component is moved.
8394              * @param {Roo.Component} this
8395              * @param {Number} x The new x position
8396              * @param {Number} y The new y position
8397              */
8398         move : true
8399     });
8400 };
8401
8402 Roo.extend(Roo.BoxComponent, Roo.Component, {
8403     // private, set in afterRender to signify that the component has been rendered
8404     boxReady : false,
8405     // private, used to defer height settings to subclasses
8406     deferHeight: false,
8407     /** @cfg {Number} width
8408      * width (optional) size of component
8409      */
8410      /** @cfg {Number} height
8411      * height (optional) size of component
8412      */
8413      
8414     /**
8415      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8416      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8417      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8418      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8419      * @return {Roo.BoxComponent} this
8420      */
8421     setSize : function(w, h){
8422         // support for standard size objects
8423         if(typeof w == 'object'){
8424             h = w.height;
8425             w = w.width;
8426         }
8427         // not rendered
8428         if(!this.boxReady){
8429             this.width = w;
8430             this.height = h;
8431             return this;
8432         }
8433
8434         // prevent recalcs when not needed
8435         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8436             return this;
8437         }
8438         this.lastSize = {width: w, height: h};
8439
8440         var adj = this.adjustSize(w, h);
8441         var aw = adj.width, ah = adj.height;
8442         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8443             var rz = this.getResizeEl();
8444             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8445                 rz.setSize(aw, ah);
8446             }else if(!this.deferHeight && ah !== undefined){
8447                 rz.setHeight(ah);
8448             }else if(aw !== undefined){
8449                 rz.setWidth(aw);
8450             }
8451             this.onResize(aw, ah, w, h);
8452             this.fireEvent('resize', this, aw, ah, w, h);
8453         }
8454         return this;
8455     },
8456
8457     /**
8458      * Gets the current size of the component's underlying element.
8459      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8460      */
8461     getSize : function(){
8462         return this.el.getSize();
8463     },
8464
8465     /**
8466      * Gets the current XY position of the component's underlying element.
8467      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8468      * @return {Array} The XY position of the element (e.g., [100, 200])
8469      */
8470     getPosition : function(local){
8471         if(local === true){
8472             return [this.el.getLeft(true), this.el.getTop(true)];
8473         }
8474         return this.xy || this.el.getXY();
8475     },
8476
8477     /**
8478      * Gets the current box measurements 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      * @returns {Object} box An object in the format {x, y, width, height}
8481      */
8482     getBox : function(local){
8483         var s = this.el.getSize();
8484         if(local){
8485             s.x = this.el.getLeft(true);
8486             s.y = this.el.getTop(true);
8487         }else{
8488             var xy = this.xy || this.el.getXY();
8489             s.x = xy[0];
8490             s.y = xy[1];
8491         }
8492         return s;
8493     },
8494
8495     /**
8496      * Sets the current box measurements of the component's underlying element.
8497      * @param {Object} box An object in the format {x, y, width, height}
8498      * @returns {Roo.BoxComponent} this
8499      */
8500     updateBox : function(box){
8501         this.setSize(box.width, box.height);
8502         this.setPagePosition(box.x, box.y);
8503         return this;
8504     },
8505
8506     // protected
8507     getResizeEl : function(){
8508         return this.resizeEl || this.el;
8509     },
8510
8511     // protected
8512     getPositionEl : function(){
8513         return this.positionEl || this.el;
8514     },
8515
8516     /**
8517      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8518      * This method fires the move event.
8519      * @param {Number} left The new left
8520      * @param {Number} top The new top
8521      * @returns {Roo.BoxComponent} this
8522      */
8523     setPosition : function(x, y){
8524         this.x = x;
8525         this.y = y;
8526         if(!this.boxReady){
8527             return this;
8528         }
8529         var adj = this.adjustPosition(x, y);
8530         var ax = adj.x, ay = adj.y;
8531
8532         var el = this.getPositionEl();
8533         if(ax !== undefined || ay !== undefined){
8534             if(ax !== undefined && ay !== undefined){
8535                 el.setLeftTop(ax, ay);
8536             }else if(ax !== undefined){
8537                 el.setLeft(ax);
8538             }else if(ay !== undefined){
8539                 el.setTop(ay);
8540             }
8541             this.onPosition(ax, ay);
8542             this.fireEvent('move', this, ax, ay);
8543         }
8544         return this;
8545     },
8546
8547     /**
8548      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8549      * This method fires the move event.
8550      * @param {Number} x The new x position
8551      * @param {Number} y The new y position
8552      * @returns {Roo.BoxComponent} this
8553      */
8554     setPagePosition : function(x, y){
8555         this.pageX = x;
8556         this.pageY = y;
8557         if(!this.boxReady){
8558             return;
8559         }
8560         if(x === undefined || y === undefined){ // cannot translate undefined points
8561             return;
8562         }
8563         var p = this.el.translatePoints(x, y);
8564         this.setPosition(p.left, p.top);
8565         return this;
8566     },
8567
8568     // private
8569     onRender : function(ct, position){
8570         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8571         if(this.resizeEl){
8572             this.resizeEl = Roo.get(this.resizeEl);
8573         }
8574         if(this.positionEl){
8575             this.positionEl = Roo.get(this.positionEl);
8576         }
8577     },
8578
8579     // private
8580     afterRender : function(){
8581         Roo.BoxComponent.superclass.afterRender.call(this);
8582         this.boxReady = true;
8583         this.setSize(this.width, this.height);
8584         if(this.x || this.y){
8585             this.setPosition(this.x, this.y);
8586         }
8587         if(this.pageX || this.pageY){
8588             this.setPagePosition(this.pageX, this.pageY);
8589         }
8590     },
8591
8592     /**
8593      * Force the component's size to recalculate based on the underlying element's current height and width.
8594      * @returns {Roo.BoxComponent} this
8595      */
8596     syncSize : function(){
8597         delete this.lastSize;
8598         this.setSize(this.el.getWidth(), this.el.getHeight());
8599         return this;
8600     },
8601
8602     /**
8603      * Called after the component is resized, this method is empty by default but can be implemented by any
8604      * subclass that needs to perform custom logic after a resize occurs.
8605      * @param {Number} adjWidth The box-adjusted width that was set
8606      * @param {Number} adjHeight The box-adjusted height that was set
8607      * @param {Number} rawWidth The width that was originally specified
8608      * @param {Number} rawHeight The height that was originally specified
8609      */
8610     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8611
8612     },
8613
8614     /**
8615      * Called after the component is moved, this method is empty by default but can be implemented by any
8616      * subclass that needs to perform custom logic after a move occurs.
8617      * @param {Number} x The new x position
8618      * @param {Number} y The new y position
8619      */
8620     onPosition : function(x, y){
8621
8622     },
8623
8624     // private
8625     adjustSize : function(w, h){
8626         if(this.autoWidth){
8627             w = 'auto';
8628         }
8629         if(this.autoHeight){
8630             h = 'auto';
8631         }
8632         return {width : w, height: h};
8633     },
8634
8635     // private
8636     adjustPosition : function(x, y){
8637         return {x : x, y: y};
8638     }
8639 });/*
8640  * Based on:
8641  * Ext JS Library 1.1.1
8642  * Copyright(c) 2006-2007, Ext JS, LLC.
8643  *
8644  * Originally Released Under LGPL - original licence link has changed is not relivant.
8645  *
8646  * Fork - LGPL
8647  * <script type="text/javascript">
8648  */
8649
8650
8651 /**
8652  * @class Roo.SplitBar
8653  * @extends Roo.util.Observable
8654  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8655  * <br><br>
8656  * Usage:
8657  * <pre><code>
8658 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8659                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8660 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8661 split.minSize = 100;
8662 split.maxSize = 600;
8663 split.animate = true;
8664 split.on('moved', splitterMoved);
8665 </code></pre>
8666  * @constructor
8667  * Create a new SplitBar
8668  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8669  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8670  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8671  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8672                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8673                         position of the SplitBar).
8674  */
8675 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8676     
8677     /** @private */
8678     this.el = Roo.get(dragElement, true);
8679     this.el.dom.unselectable = "on";
8680     /** @private */
8681     this.resizingEl = Roo.get(resizingElement, true);
8682
8683     /**
8684      * @private
8685      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8686      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8687      * @type Number
8688      */
8689     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8690     
8691     /**
8692      * The minimum size of the resizing element. (Defaults to 0)
8693      * @type Number
8694      */
8695     this.minSize = 0;
8696     
8697     /**
8698      * The maximum size of the resizing element. (Defaults to 2000)
8699      * @type Number
8700      */
8701     this.maxSize = 2000;
8702     
8703     /**
8704      * Whether to animate the transition to the new size
8705      * @type Boolean
8706      */
8707     this.animate = false;
8708     
8709     /**
8710      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8711      * @type Boolean
8712      */
8713     this.useShim = false;
8714     
8715     /** @private */
8716     this.shim = null;
8717     
8718     if(!existingProxy){
8719         /** @private */
8720         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8721     }else{
8722         this.proxy = Roo.get(existingProxy).dom;
8723     }
8724     /** @private */
8725     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8726     
8727     /** @private */
8728     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8729     
8730     /** @private */
8731     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8732     
8733     /** @private */
8734     this.dragSpecs = {};
8735     
8736     /**
8737      * @private The adapter to use to positon and resize elements
8738      */
8739     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8740     this.adapter.init(this);
8741     
8742     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8743         /** @private */
8744         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8745         this.el.addClass("x-splitbar-h");
8746     }else{
8747         /** @private */
8748         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8749         this.el.addClass("x-splitbar-v");
8750     }
8751     
8752     this.addEvents({
8753         /**
8754          * @event resize
8755          * Fires when the splitter is moved (alias for {@link #event-moved})
8756          * @param {Roo.SplitBar} this
8757          * @param {Number} newSize the new width or height
8758          */
8759         "resize" : true,
8760         /**
8761          * @event moved
8762          * Fires when the splitter is moved
8763          * @param {Roo.SplitBar} this
8764          * @param {Number} newSize the new width or height
8765          */
8766         "moved" : true,
8767         /**
8768          * @event beforeresize
8769          * Fires before the splitter is dragged
8770          * @param {Roo.SplitBar} this
8771          */
8772         "beforeresize" : true,
8773
8774         "beforeapply" : true
8775     });
8776
8777     Roo.util.Observable.call(this);
8778 };
8779
8780 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8781     onStartProxyDrag : function(x, y){
8782         this.fireEvent("beforeresize", this);
8783         if(!this.overlay){
8784             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8785             o.unselectable();
8786             o.enableDisplayMode("block");
8787             // all splitbars share the same overlay
8788             Roo.SplitBar.prototype.overlay = o;
8789         }
8790         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8791         this.overlay.show();
8792         Roo.get(this.proxy).setDisplayed("block");
8793         var size = this.adapter.getElementSize(this);
8794         this.activeMinSize = this.getMinimumSize();;
8795         this.activeMaxSize = this.getMaximumSize();;
8796         var c1 = size - this.activeMinSize;
8797         var c2 = Math.max(this.activeMaxSize - size, 0);
8798         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8799             this.dd.resetConstraints();
8800             this.dd.setXConstraint(
8801                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8802                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8803             );
8804             this.dd.setYConstraint(0, 0);
8805         }else{
8806             this.dd.resetConstraints();
8807             this.dd.setXConstraint(0, 0);
8808             this.dd.setYConstraint(
8809                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8810                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8811             );
8812          }
8813         this.dragSpecs.startSize = size;
8814         this.dragSpecs.startPoint = [x, y];
8815         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8816     },
8817     
8818     /** 
8819      * @private Called after the drag operation by the DDProxy
8820      */
8821     onEndProxyDrag : function(e){
8822         Roo.get(this.proxy).setDisplayed(false);
8823         var endPoint = Roo.lib.Event.getXY(e);
8824         if(this.overlay){
8825             this.overlay.hide();
8826         }
8827         var newSize;
8828         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8829             newSize = this.dragSpecs.startSize + 
8830                 (this.placement == Roo.SplitBar.LEFT ?
8831                     endPoint[0] - this.dragSpecs.startPoint[0] :
8832                     this.dragSpecs.startPoint[0] - endPoint[0]
8833                 );
8834         }else{
8835             newSize = this.dragSpecs.startSize + 
8836                 (this.placement == Roo.SplitBar.TOP ?
8837                     endPoint[1] - this.dragSpecs.startPoint[1] :
8838                     this.dragSpecs.startPoint[1] - endPoint[1]
8839                 );
8840         }
8841         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8842         if(newSize != this.dragSpecs.startSize){
8843             if(this.fireEvent('beforeapply', this, newSize) !== false){
8844                 this.adapter.setElementSize(this, newSize);
8845                 this.fireEvent("moved", this, newSize);
8846                 this.fireEvent("resize", this, newSize);
8847             }
8848         }
8849     },
8850     
8851     /**
8852      * Get the adapter this SplitBar uses
8853      * @return The adapter object
8854      */
8855     getAdapter : function(){
8856         return this.adapter;
8857     },
8858     
8859     /**
8860      * Set the adapter this SplitBar uses
8861      * @param {Object} adapter A SplitBar adapter object
8862      */
8863     setAdapter : function(adapter){
8864         this.adapter = adapter;
8865         this.adapter.init(this);
8866     },
8867     
8868     /**
8869      * Gets the minimum size for the resizing element
8870      * @return {Number} The minimum size
8871      */
8872     getMinimumSize : function(){
8873         return this.minSize;
8874     },
8875     
8876     /**
8877      * Sets the minimum size for the resizing element
8878      * @param {Number} minSize The minimum size
8879      */
8880     setMinimumSize : function(minSize){
8881         this.minSize = minSize;
8882     },
8883     
8884     /**
8885      * Gets the maximum size for the resizing element
8886      * @return {Number} The maximum size
8887      */
8888     getMaximumSize : function(){
8889         return this.maxSize;
8890     },
8891     
8892     /**
8893      * Sets the maximum size for the resizing element
8894      * @param {Number} maxSize The maximum size
8895      */
8896     setMaximumSize : function(maxSize){
8897         this.maxSize = maxSize;
8898     },
8899     
8900     /**
8901      * Sets the initialize size for the resizing element
8902      * @param {Number} size The initial size
8903      */
8904     setCurrentSize : function(size){
8905         var oldAnimate = this.animate;
8906         this.animate = false;
8907         this.adapter.setElementSize(this, size);
8908         this.animate = oldAnimate;
8909     },
8910     
8911     /**
8912      * Destroy this splitbar. 
8913      * @param {Boolean} removeEl True to remove the element
8914      */
8915     destroy : function(removeEl){
8916         if(this.shim){
8917             this.shim.remove();
8918         }
8919         this.dd.unreg();
8920         this.proxy.parentNode.removeChild(this.proxy);
8921         if(removeEl){
8922             this.el.remove();
8923         }
8924     }
8925 });
8926
8927 /**
8928  * @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.
8929  */
8930 Roo.SplitBar.createProxy = function(dir){
8931     var proxy = new Roo.Element(document.createElement("div"));
8932     proxy.unselectable();
8933     var cls = 'x-splitbar-proxy';
8934     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8935     document.body.appendChild(proxy.dom);
8936     return proxy.dom;
8937 };
8938
8939 /** 
8940  * @class Roo.SplitBar.BasicLayoutAdapter
8941  * Default Adapter. It assumes the splitter and resizing element are not positioned
8942  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8943  */
8944 Roo.SplitBar.BasicLayoutAdapter = function(){
8945 };
8946
8947 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8948     // do nothing for now
8949     init : function(s){
8950     
8951     },
8952     /**
8953      * Called before drag operations to get the current size of the resizing element. 
8954      * @param {Roo.SplitBar} s The SplitBar using this adapter
8955      */
8956      getElementSize : function(s){
8957         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8958             return s.resizingEl.getWidth();
8959         }else{
8960             return s.resizingEl.getHeight();
8961         }
8962     },
8963     
8964     /**
8965      * Called after drag operations to set the size of the resizing element.
8966      * @param {Roo.SplitBar} s The SplitBar using this adapter
8967      * @param {Number} newSize The new size to set
8968      * @param {Function} onComplete A function to be invoked when resizing is complete
8969      */
8970     setElementSize : function(s, newSize, onComplete){
8971         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8972             if(!s.animate){
8973                 s.resizingEl.setWidth(newSize);
8974                 if(onComplete){
8975                     onComplete(s, newSize);
8976                 }
8977             }else{
8978                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8979             }
8980         }else{
8981             
8982             if(!s.animate){
8983                 s.resizingEl.setHeight(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }
8991     }
8992 };
8993
8994 /** 
8995  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8996  * @extends Roo.SplitBar.BasicLayoutAdapter
8997  * Adapter that  moves the splitter element to align with the resized sizing element. 
8998  * Used with an absolute positioned SplitBar.
8999  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9000  * document.body, make sure you assign an id to the body element.
9001  */
9002 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9003     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9004     this.container = Roo.get(container);
9005 };
9006
9007 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9008     init : function(s){
9009         this.basic.init(s);
9010     },
9011     
9012     getElementSize : function(s){
9013         return this.basic.getElementSize(s);
9014     },
9015     
9016     setElementSize : function(s, newSize, onComplete){
9017         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9018     },
9019     
9020     moveSplitter : function(s){
9021         var yes = Roo.SplitBar;
9022         switch(s.placement){
9023             case yes.LEFT:
9024                 s.el.setX(s.resizingEl.getRight());
9025                 break;
9026             case yes.RIGHT:
9027                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9028                 break;
9029             case yes.TOP:
9030                 s.el.setY(s.resizingEl.getBottom());
9031                 break;
9032             case yes.BOTTOM:
9033                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9034                 break;
9035         }
9036     }
9037 };
9038
9039 /**
9040  * Orientation constant - Create a vertical SplitBar
9041  * @static
9042  * @type Number
9043  */
9044 Roo.SplitBar.VERTICAL = 1;
9045
9046 /**
9047  * Orientation constant - Create a horizontal SplitBar
9048  * @static
9049  * @type Number
9050  */
9051 Roo.SplitBar.HORIZONTAL = 2;
9052
9053 /**
9054  * Placement constant - The resizing element is to the left of the splitter element
9055  * @static
9056  * @type Number
9057  */
9058 Roo.SplitBar.LEFT = 1;
9059
9060 /**
9061  * Placement constant - The resizing element is to the right of the splitter element
9062  * @static
9063  * @type Number
9064  */
9065 Roo.SplitBar.RIGHT = 2;
9066
9067 /**
9068  * Placement constant - The resizing element is positioned above the splitter element
9069  * @static
9070  * @type Number
9071  */
9072 Roo.SplitBar.TOP = 3;
9073
9074 /**
9075  * Placement constant - The resizing element is positioned under splitter element
9076  * @static
9077  * @type Number
9078  */
9079 Roo.SplitBar.BOTTOM = 4;
9080 /*
9081  * Based on:
9082  * Ext JS Library 1.1.1
9083  * Copyright(c) 2006-2007, Ext JS, LLC.
9084  *
9085  * Originally Released Under LGPL - original licence link has changed is not relivant.
9086  *
9087  * Fork - LGPL
9088  * <script type="text/javascript">
9089  */
9090
9091 /**
9092  * @class Roo.View
9093  * @extends Roo.util.Observable
9094  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9095  * This class also supports single and multi selection modes. <br>
9096  * Create a data model bound view:
9097  <pre><code>
9098  var store = new Roo.data.Store(...);
9099
9100  var view = new Roo.View({
9101     el : "my-element",
9102     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9103  
9104     singleSelect: true,
9105     selectedClass: "ydataview-selected",
9106     store: store
9107  });
9108
9109  // listen for node click?
9110  view.on("click", function(vw, index, node, e){
9111  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9112  });
9113
9114  // load XML data
9115  dataModel.load("foobar.xml");
9116  </code></pre>
9117  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9118  * <br><br>
9119  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9120  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9121  * 
9122  * Note: old style constructor is still suported (container, template, config)
9123  * 
9124  * @constructor
9125  * Create a new View
9126  * @param {Object} config The config object
9127  * 
9128  */
9129 Roo.View = function(config, depreciated_tpl, depreciated_config){
9130     
9131     if (typeof(depreciated_tpl) == 'undefined') {
9132         // new way.. - universal constructor.
9133         Roo.apply(this, config);
9134         this.el  = Roo.get(this.el);
9135     } else {
9136         // old format..
9137         this.el  = Roo.get(config);
9138         this.tpl = depreciated_tpl;
9139         Roo.apply(this, depreciated_config);
9140     }
9141      
9142     
9143     if(typeof(this.tpl) == "string"){
9144         this.tpl = new Roo.Template(this.tpl);
9145     } else {
9146         // support xtype ctors..
9147         this.tpl = new Roo.factory(this.tpl, Roo);
9148     }
9149     
9150     
9151     this.tpl.compile();
9152    
9153
9154      
9155     /** @private */
9156     this.addEvents({
9157         /**
9158          * @event beforeclick
9159          * Fires before a click is processed. Returns false to cancel the default action.
9160          * @param {Roo.View} this
9161          * @param {Number} index The index of the target node
9162          * @param {HTMLElement} node The target node
9163          * @param {Roo.EventObject} e The raw event object
9164          */
9165             "beforeclick" : true,
9166         /**
9167          * @event click
9168          * Fires when a template node is clicked.
9169          * @param {Roo.View} this
9170          * @param {Number} index The index of the target node
9171          * @param {HTMLElement} node The target node
9172          * @param {Roo.EventObject} e The raw event object
9173          */
9174             "click" : true,
9175         /**
9176          * @event dblclick
9177          * Fires when a template node is double clicked.
9178          * @param {Roo.View} this
9179          * @param {Number} index The index of the target node
9180          * @param {HTMLElement} node The target node
9181          * @param {Roo.EventObject} e The raw event object
9182          */
9183             "dblclick" : true,
9184         /**
9185          * @event contextmenu
9186          * Fires when a template node is right clicked.
9187          * @param {Roo.View} this
9188          * @param {Number} index The index of the target node
9189          * @param {HTMLElement} node The target node
9190          * @param {Roo.EventObject} e The raw event object
9191          */
9192             "contextmenu" : true,
9193         /**
9194          * @event selectionchange
9195          * Fires when the selected nodes change.
9196          * @param {Roo.View} this
9197          * @param {Array} selections Array of the selected nodes
9198          */
9199             "selectionchange" : true,
9200     
9201         /**
9202          * @event beforeselect
9203          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9204          * @param {Roo.View} this
9205          * @param {HTMLElement} node The node to be selected
9206          * @param {Array} selections Array of currently selected nodes
9207          */
9208             "beforeselect" : true,
9209         /**
9210          * @event preparedata
9211          * Fires on every row to render, to allow you to change the data.
9212          * @param {Roo.View} this
9213          * @param {Object} data to be rendered (change this)
9214          * @param {Number} row being rendered
9215          * @param {Roo.data.Record} record being rendered.
9216          */
9217           "preparedata" : true
9218         });
9219
9220     this.el.on({
9221         "click": this.onClick,
9222         "dblclick": this.onDblClick,
9223         "contextmenu": this.onContextMenu,
9224         scope:this
9225     });
9226
9227     this.selections = [];
9228     this.nodes = [];
9229     this.cmp = new Roo.CompositeElementLite([]);
9230     if(this.store){
9231         this.store = Roo.factory(this.store, Roo.data);
9232         this.setStore(this.store, true);
9233     }
9234     Roo.View.superclass.constructor.call(this);
9235 };
9236
9237 Roo.extend(Roo.View, Roo.util.Observable, {
9238     
9239      /**
9240      * @cfg {Roo.data.Store} store Data store to load data from.
9241      */
9242     store : false,
9243     
9244     /**
9245      * @cfg {String|Roo.Element} el The container element.
9246      */
9247     el : '',
9248     
9249     /**
9250      * @cfg {String|Roo.Template} tpl The template used by this View 
9251      */
9252     tpl : false,
9253     
9254     /**
9255      * @cfg {String} selectedClass The css class to add to selected nodes
9256      */
9257     selectedClass : "x-view-selected",
9258      /**
9259      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9260      */
9261     emptyText : "",
9262     /**
9263      * @cfg {Boolean} multiSelect Allow multiple selection
9264      */
9265     
9266     multiSelect : false,
9267     /**
9268      * @cfg {Boolean} singleSelect Allow single selection
9269      */
9270     singleSelect:  false,
9271     
9272     /**
9273      * Returns the element this view is bound to.
9274      * @return {Roo.Element}
9275      */
9276     getEl : function(){
9277         return this.el;
9278     },
9279
9280     /**
9281      * Refreshes the view.
9282      */
9283     refresh : function(){
9284         var t = this.tpl;
9285         this.clearSelections();
9286         this.el.update("");
9287         var html = [];
9288         var records = this.store.getRange();
9289         if(records.length < 1){
9290             this.el.update(this.emptyText);
9291             return;
9292         }
9293         for(var i = 0, len = records.length; i < len; i++){
9294             var data = this.prepareData(records[i].data, i, records[i]);
9295             this.fireEvent("preparedata", this, data, i, records[i]);
9296             html[html.length] = t.apply(data);
9297         }
9298         this.el.update(html.join(""));
9299         this.nodes = this.el.dom.childNodes;
9300         this.updateIndexes(0);
9301     },
9302
9303     /**
9304      * Function to override to reformat the data that is sent to
9305      * the template for each node.
9306      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9307      * a JSON object for an UpdateManager bound view).
9308      */
9309     prepareData : function(data){
9310         return data;
9311     },
9312
9313     onUpdate : function(ds, record){
9314         this.clearSelections();
9315         var index = this.store.indexOf(record);
9316         var n = this.nodes[index];
9317         this.tpl.insertBefore(n, this.prepareData(record.data));
9318         n.parentNode.removeChild(n);
9319         this.updateIndexes(index, index);
9320     },
9321
9322     onAdd : function(ds, records, index){
9323         this.clearSelections();
9324         if(this.nodes.length == 0){
9325             this.refresh();
9326             return;
9327         }
9328         var n = this.nodes[index];
9329         for(var i = 0, len = records.length; i < len; i++){
9330             var d = this.prepareData(records[i].data);
9331             if(n){
9332                 this.tpl.insertBefore(n, d);
9333             }else{
9334                 this.tpl.append(this.el, d);
9335             }
9336         }
9337         this.updateIndexes(index);
9338     },
9339
9340     onRemove : function(ds, record, index){
9341         this.clearSelections();
9342         this.el.dom.removeChild(this.nodes[index]);
9343         this.updateIndexes(index);
9344     },
9345
9346     /**
9347      * Refresh an individual node.
9348      * @param {Number} index
9349      */
9350     refreshNode : function(index){
9351         this.onUpdate(this.store, this.store.getAt(index));
9352     },
9353
9354     updateIndexes : function(startIndex, endIndex){
9355         var ns = this.nodes;
9356         startIndex = startIndex || 0;
9357         endIndex = endIndex || ns.length - 1;
9358         for(var i = startIndex; i <= endIndex; i++){
9359             ns[i].nodeIndex = i;
9360         }
9361     },
9362
9363     /**
9364      * Changes the data store this view uses and refresh the view.
9365      * @param {Store} store
9366      */
9367     setStore : function(store, initial){
9368         if(!initial && this.store){
9369             this.store.un("datachanged", this.refresh);
9370             this.store.un("add", this.onAdd);
9371             this.store.un("remove", this.onRemove);
9372             this.store.un("update", this.onUpdate);
9373             this.store.un("clear", this.refresh);
9374         }
9375         if(store){
9376           
9377             store.on("datachanged", this.refresh, this);
9378             store.on("add", this.onAdd, this);
9379             store.on("remove", this.onRemove, this);
9380             store.on("update", this.onUpdate, this);
9381             store.on("clear", this.refresh, this);
9382         }
9383         
9384         if(store){
9385             this.refresh();
9386         }
9387     },
9388
9389     /**
9390      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9391      * @param {HTMLElement} node
9392      * @return {HTMLElement} The template node
9393      */
9394     findItemFromChild : function(node){
9395         var el = this.el.dom;
9396         if(!node || node.parentNode == el){
9397                     return node;
9398             }
9399             var p = node.parentNode;
9400             while(p && p != el){
9401             if(p.parentNode == el){
9402                 return p;
9403             }
9404             p = p.parentNode;
9405         }
9406             return null;
9407     },
9408
9409     /** @ignore */
9410     onClick : function(e){
9411         var item = this.findItemFromChild(e.getTarget());
9412         if(item){
9413             var index = this.indexOf(item);
9414             if(this.onItemClick(item, index, e) !== false){
9415                 this.fireEvent("click", this, index, item, e);
9416             }
9417         }else{
9418             this.clearSelections();
9419         }
9420     },
9421
9422     /** @ignore */
9423     onContextMenu : function(e){
9424         var item = this.findItemFromChild(e.getTarget());
9425         if(item){
9426             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9427         }
9428     },
9429
9430     /** @ignore */
9431     onDblClick : function(e){
9432         var item = this.findItemFromChild(e.getTarget());
9433         if(item){
9434             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9435         }
9436     },
9437
9438     onItemClick : function(item, index, e){
9439         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9440             return false;
9441         }
9442         if(this.multiSelect || this.singleSelect){
9443             if(this.multiSelect && e.shiftKey && this.lastSelection){
9444                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9445             }else{
9446                 this.select(item, this.multiSelect && e.ctrlKey);
9447                 this.lastSelection = item;
9448             }
9449             e.preventDefault();
9450         }
9451         return true;
9452     },
9453
9454     /**
9455      * Get the number of selected nodes.
9456      * @return {Number}
9457      */
9458     getSelectionCount : function(){
9459         return this.selections.length;
9460     },
9461
9462     /**
9463      * Get the currently selected nodes.
9464      * @return {Array} An array of HTMLElements
9465      */
9466     getSelectedNodes : function(){
9467         return this.selections;
9468     },
9469
9470     /**
9471      * Get the indexes of the selected nodes.
9472      * @return {Array}
9473      */
9474     getSelectedIndexes : function(){
9475         var indexes = [], s = this.selections;
9476         for(var i = 0, len = s.length; i < len; i++){
9477             indexes.push(s[i].nodeIndex);
9478         }
9479         return indexes;
9480     },
9481
9482     /**
9483      * Clear all selections
9484      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9485      */
9486     clearSelections : function(suppressEvent){
9487         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9488             this.cmp.elements = this.selections;
9489             this.cmp.removeClass(this.selectedClass);
9490             this.selections = [];
9491             if(!suppressEvent){
9492                 this.fireEvent("selectionchange", this, this.selections);
9493             }
9494         }
9495     },
9496
9497     /**
9498      * Returns true if the passed node is selected
9499      * @param {HTMLElement/Number} node The node or node index
9500      * @return {Boolean}
9501      */
9502     isSelected : function(node){
9503         var s = this.selections;
9504         if(s.length < 1){
9505             return false;
9506         }
9507         node = this.getNode(node);
9508         return s.indexOf(node) !== -1;
9509     },
9510
9511     /**
9512      * Selects nodes.
9513      * @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
9514      * @param {Boolean} keepExisting (optional) true to keep existing selections
9515      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9516      */
9517     select : function(nodeInfo, keepExisting, suppressEvent){
9518         if(nodeInfo instanceof Array){
9519             if(!keepExisting){
9520                 this.clearSelections(true);
9521             }
9522             for(var i = 0, len = nodeInfo.length; i < len; i++){
9523                 this.select(nodeInfo[i], true, true);
9524             }
9525         } else{
9526             var node = this.getNode(nodeInfo);
9527             if(node && !this.isSelected(node)){
9528                 if(!keepExisting){
9529                     this.clearSelections(true);
9530                 }
9531                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9532                     Roo.fly(node).addClass(this.selectedClass);
9533                     this.selections.push(node);
9534                     if(!suppressEvent){
9535                         this.fireEvent("selectionchange", this, this.selections);
9536                     }
9537                 }
9538             }
9539         }
9540     },
9541
9542     /**
9543      * Gets a template node.
9544      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9545      * @return {HTMLElement} The node or null if it wasn't found
9546      */
9547     getNode : function(nodeInfo){
9548         if(typeof nodeInfo == "string"){
9549             return document.getElementById(nodeInfo);
9550         }else if(typeof nodeInfo == "number"){
9551             return this.nodes[nodeInfo];
9552         }
9553         return nodeInfo;
9554     },
9555
9556     /**
9557      * Gets a range template nodes.
9558      * @param {Number} startIndex
9559      * @param {Number} endIndex
9560      * @return {Array} An array of nodes
9561      */
9562     getNodes : function(start, end){
9563         var ns = this.nodes;
9564         start = start || 0;
9565         end = typeof end == "undefined" ? ns.length - 1 : end;
9566         var nodes = [];
9567         if(start <= end){
9568             for(var i = start; i <= end; i++){
9569                 nodes.push(ns[i]);
9570             }
9571         } else{
9572             for(var i = start; i >= end; i--){
9573                 nodes.push(ns[i]);
9574             }
9575         }
9576         return nodes;
9577     },
9578
9579     /**
9580      * Finds the index of the passed node
9581      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9582      * @return {Number} The index of the node or -1
9583      */
9584     indexOf : function(node){
9585         node = this.getNode(node);
9586         if(typeof node.nodeIndex == "number"){
9587             return node.nodeIndex;
9588         }
9589         var ns = this.nodes;
9590         for(var i = 0, len = ns.length; i < len; i++){
9591             if(ns[i] == node){
9592                 return i;
9593             }
9594         }
9595         return -1;
9596     }
9597 });
9598 /*
9599  * Based on:
9600  * Ext JS Library 1.1.1
9601  * Copyright(c) 2006-2007, Ext JS, LLC.
9602  *
9603  * Originally Released Under LGPL - original licence link has changed is not relivant.
9604  *
9605  * Fork - LGPL
9606  * <script type="text/javascript">
9607  */
9608
9609 /**
9610  * @class Roo.JsonView
9611  * @extends Roo.View
9612  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9613 <pre><code>
9614 var view = new Roo.JsonView({
9615     container: "my-element",
9616     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9617     multiSelect: true, 
9618     jsonRoot: "data" 
9619 });
9620
9621 // listen for node click?
9622 view.on("click", function(vw, index, node, e){
9623     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9624 });
9625
9626 // direct load of JSON data
9627 view.load("foobar.php");
9628
9629 // Example from my blog list
9630 var tpl = new Roo.Template(
9631     '&lt;div class="entry"&gt;' +
9632     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9633     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9634     "&lt;/div&gt;&lt;hr /&gt;"
9635 );
9636
9637 var moreView = new Roo.JsonView({
9638     container :  "entry-list", 
9639     template : tpl,
9640     jsonRoot: "posts"
9641 });
9642 moreView.on("beforerender", this.sortEntries, this);
9643 moreView.load({
9644     url: "/blog/get-posts.php",
9645     params: "allposts=true",
9646     text: "Loading Blog Entries..."
9647 });
9648 </code></pre>
9649
9650 * Note: old code is supported with arguments : (container, template, config)
9651
9652
9653  * @constructor
9654  * Create a new JsonView
9655  * 
9656  * @param {Object} config The config object
9657  * 
9658  */
9659 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9660     
9661     
9662     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9663
9664     var um = this.el.getUpdateManager();
9665     um.setRenderer(this);
9666     um.on("update", this.onLoad, this);
9667     um.on("failure", this.onLoadException, this);
9668
9669     /**
9670      * @event beforerender
9671      * Fires before rendering of the downloaded JSON data.
9672      * @param {Roo.JsonView} this
9673      * @param {Object} data The JSON data loaded
9674      */
9675     /**
9676      * @event load
9677      * Fires when data is loaded.
9678      * @param {Roo.JsonView} this
9679      * @param {Object} data The JSON data loaded
9680      * @param {Object} response The raw Connect response object
9681      */
9682     /**
9683      * @event loadexception
9684      * Fires when loading fails.
9685      * @param {Roo.JsonView} this
9686      * @param {Object} response The raw Connect response object
9687      */
9688     this.addEvents({
9689         'beforerender' : true,
9690         'load' : true,
9691         'loadexception' : true
9692     });
9693 };
9694 Roo.extend(Roo.JsonView, Roo.View, {
9695     /**
9696      * @type {String} The root property in the loaded JSON object that contains the data
9697      */
9698     jsonRoot : "",
9699
9700     /**
9701      * Refreshes the view.
9702      */
9703     refresh : function(){
9704         this.clearSelections();
9705         this.el.update("");
9706         var html = [];
9707         var o = this.jsonData;
9708         if(o && o.length > 0){
9709             for(var i = 0, len = o.length; i < len; i++){
9710                 var data = this.prepareData(o[i], i, o);
9711                 html[html.length] = this.tpl.apply(data);
9712             }
9713         }else{
9714             html.push(this.emptyText);
9715         }
9716         this.el.update(html.join(""));
9717         this.nodes = this.el.dom.childNodes;
9718         this.updateIndexes(0);
9719     },
9720
9721     /**
9722      * 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.
9723      * @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:
9724      <pre><code>
9725      view.load({
9726          url: "your-url.php",
9727          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9728          callback: yourFunction,
9729          scope: yourObject, //(optional scope)
9730          discardUrl: false,
9731          nocache: false,
9732          text: "Loading...",
9733          timeout: 30,
9734          scripts: false
9735      });
9736      </code></pre>
9737      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9738      * 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.
9739      * @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}
9740      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9741      * @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.
9742      */
9743     load : function(){
9744         var um = this.el.getUpdateManager();
9745         um.update.apply(um, arguments);
9746     },
9747
9748     render : function(el, response){
9749         this.clearSelections();
9750         this.el.update("");
9751         var o;
9752         try{
9753             o = Roo.util.JSON.decode(response.responseText);
9754             if(this.jsonRoot){
9755                 
9756                 o = o[this.jsonRoot];
9757             }
9758         } catch(e){
9759         }
9760         /**
9761          * The current JSON data or null
9762          */
9763         this.jsonData = o;
9764         this.beforeRender();
9765         this.refresh();
9766     },
9767
9768 /**
9769  * Get the number of records in the current JSON dataset
9770  * @return {Number}
9771  */
9772     getCount : function(){
9773         return this.jsonData ? this.jsonData.length : 0;
9774     },
9775
9776 /**
9777  * Returns the JSON object for the specified node(s)
9778  * @param {HTMLElement/Array} node The node or an array of nodes
9779  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9780  * you get the JSON object for the node
9781  */
9782     getNodeData : function(node){
9783         if(node instanceof Array){
9784             var data = [];
9785             for(var i = 0, len = node.length; i < len; i++){
9786                 data.push(this.getNodeData(node[i]));
9787             }
9788             return data;
9789         }
9790         return this.jsonData[this.indexOf(node)] || null;
9791     },
9792
9793     beforeRender : function(){
9794         this.snapshot = this.jsonData;
9795         if(this.sortInfo){
9796             this.sort.apply(this, this.sortInfo);
9797         }
9798         this.fireEvent("beforerender", this, this.jsonData);
9799     },
9800
9801     onLoad : function(el, o){
9802         this.fireEvent("load", this, this.jsonData, o);
9803     },
9804
9805     onLoadException : function(el, o){
9806         this.fireEvent("loadexception", this, o);
9807     },
9808
9809 /**
9810  * Filter the data by a specific property.
9811  * @param {String} property A property on your JSON objects
9812  * @param {String/RegExp} value Either string that the property values
9813  * should start with, or a RegExp to test against the property
9814  */
9815     filter : function(property, value){
9816         if(this.jsonData){
9817             var data = [];
9818             var ss = this.snapshot;
9819             if(typeof value == "string"){
9820                 var vlen = value.length;
9821                 if(vlen == 0){
9822                     this.clearFilter();
9823                     return;
9824                 }
9825                 value = value.toLowerCase();
9826                 for(var i = 0, len = ss.length; i < len; i++){
9827                     var o = ss[i];
9828                     if(o[property].substr(0, vlen).toLowerCase() == value){
9829                         data.push(o);
9830                     }
9831                 }
9832             } else if(value.exec){ // regex?
9833                 for(var i = 0, len = ss.length; i < len; i++){
9834                     var o = ss[i];
9835                     if(value.test(o[property])){
9836                         data.push(o);
9837                     }
9838                 }
9839             } else{
9840                 return;
9841             }
9842             this.jsonData = data;
9843             this.refresh();
9844         }
9845     },
9846
9847 /**
9848  * Filter by a function. The passed function will be called with each
9849  * object in the current dataset. If the function returns true the value is kept,
9850  * otherwise it is filtered.
9851  * @param {Function} fn
9852  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9853  */
9854     filterBy : function(fn, scope){
9855         if(this.jsonData){
9856             var data = [];
9857             var ss = this.snapshot;
9858             for(var i = 0, len = ss.length; i < len; i++){
9859                 var o = ss[i];
9860                 if(fn.call(scope || this, o)){
9861                     data.push(o);
9862                 }
9863             }
9864             this.jsonData = data;
9865             this.refresh();
9866         }
9867     },
9868
9869 /**
9870  * Clears the current filter.
9871  */
9872     clearFilter : function(){
9873         if(this.snapshot && this.jsonData != this.snapshot){
9874             this.jsonData = this.snapshot;
9875             this.refresh();
9876         }
9877     },
9878
9879
9880 /**
9881  * Sorts the data for this view and refreshes it.
9882  * @param {String} property A property on your JSON objects to sort on
9883  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9884  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9885  */
9886     sort : function(property, dir, sortType){
9887         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9888         if(this.jsonData){
9889             var p = property;
9890             var dsc = dir && dir.toLowerCase() == "desc";
9891             var f = function(o1, o2){
9892                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9893                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9894                 ;
9895                 if(v1 < v2){
9896                     return dsc ? +1 : -1;
9897                 } else if(v1 > v2){
9898                     return dsc ? -1 : +1;
9899                 } else{
9900                     return 0;
9901                 }
9902             };
9903             this.jsonData.sort(f);
9904             this.refresh();
9905             if(this.jsonData != this.snapshot){
9906                 this.snapshot.sort(f);
9907             }
9908         }
9909     }
9910 });/*
9911  * Based on:
9912  * Ext JS Library 1.1.1
9913  * Copyright(c) 2006-2007, Ext JS, LLC.
9914  *
9915  * Originally Released Under LGPL - original licence link has changed is not relivant.
9916  *
9917  * Fork - LGPL
9918  * <script type="text/javascript">
9919  */
9920  
9921
9922 /**
9923  * @class Roo.ColorPalette
9924  * @extends Roo.Component
9925  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9926  * Here's an example of typical usage:
9927  * <pre><code>
9928 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9929 cp.render('my-div');
9930
9931 cp.on('select', function(palette, selColor){
9932     // do something with selColor
9933 });
9934 </code></pre>
9935  * @constructor
9936  * Create a new ColorPalette
9937  * @param {Object} config The config object
9938  */
9939 Roo.ColorPalette = function(config){
9940     Roo.ColorPalette.superclass.constructor.call(this, config);
9941     this.addEvents({
9942         /**
9943              * @event select
9944              * Fires when a color is selected
9945              * @param {ColorPalette} this
9946              * @param {String} color The 6-digit color hex code (without the # symbol)
9947              */
9948         select: true
9949     });
9950
9951     if(this.handler){
9952         this.on("select", this.handler, this.scope, true);
9953     }
9954 };
9955 Roo.extend(Roo.ColorPalette, Roo.Component, {
9956     /**
9957      * @cfg {String} itemCls
9958      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9959      */
9960     itemCls : "x-color-palette",
9961     /**
9962      * @cfg {String} value
9963      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9964      * the hex codes are case-sensitive.
9965      */
9966     value : null,
9967     clickEvent:'click',
9968     // private
9969     ctype: "Roo.ColorPalette",
9970
9971     /**
9972      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9973      */
9974     allowReselect : false,
9975
9976     /**
9977      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9978      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9979      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9980      * of colors with the width setting until the box is symmetrical.</p>
9981      * <p>You can override individual colors if needed:</p>
9982      * <pre><code>
9983 var cp = new Roo.ColorPalette();
9984 cp.colors[0] = "FF0000";  // change the first box to red
9985 </code></pre>
9986
9987 Or you can provide a custom array of your own for complete control:
9988 <pre><code>
9989 var cp = new Roo.ColorPalette();
9990 cp.colors = ["000000", "993300", "333300"];
9991 </code></pre>
9992      * @type Array
9993      */
9994     colors : [
9995         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9996         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9997         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9998         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9999         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10000     ],
10001
10002     // private
10003     onRender : function(container, position){
10004         var t = new Roo.MasterTemplate(
10005             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10006         );
10007         var c = this.colors;
10008         for(var i = 0, len = c.length; i < len; i++){
10009             t.add([c[i]]);
10010         }
10011         var el = document.createElement("div");
10012         el.className = this.itemCls;
10013         t.overwrite(el);
10014         container.dom.insertBefore(el, position);
10015         this.el = Roo.get(el);
10016         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10017         if(this.clickEvent != 'click'){
10018             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10019         }
10020     },
10021
10022     // private
10023     afterRender : function(){
10024         Roo.ColorPalette.superclass.afterRender.call(this);
10025         if(this.value){
10026             var s = this.value;
10027             this.value = null;
10028             this.select(s);
10029         }
10030     },
10031
10032     // private
10033     handleClick : function(e, t){
10034         e.preventDefault();
10035         if(!this.disabled){
10036             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10037             this.select(c.toUpperCase());
10038         }
10039     },
10040
10041     /**
10042      * Selects the specified color in the palette (fires the select event)
10043      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10044      */
10045     select : function(color){
10046         color = color.replace("#", "");
10047         if(color != this.value || this.allowReselect){
10048             var el = this.el;
10049             if(this.value){
10050                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10051             }
10052             el.child("a.color-"+color).addClass("x-color-palette-sel");
10053             this.value = color;
10054             this.fireEvent("select", this, color);
10055         }
10056     }
10057 });/*
10058  * Based on:
10059  * Ext JS Library 1.1.1
10060  * Copyright(c) 2006-2007, Ext JS, LLC.
10061  *
10062  * Originally Released Under LGPL - original licence link has changed is not relivant.
10063  *
10064  * Fork - LGPL
10065  * <script type="text/javascript">
10066  */
10067  
10068 /**
10069  * @class Roo.DatePicker
10070  * @extends Roo.Component
10071  * Simple date picker class.
10072  * @constructor
10073  * Create a new DatePicker
10074  * @param {Object} config The config object
10075  */
10076 Roo.DatePicker = function(config){
10077     Roo.DatePicker.superclass.constructor.call(this, config);
10078
10079     this.value = config && config.value ?
10080                  config.value.clearTime() : new Date().clearTime();
10081
10082     this.addEvents({
10083         /**
10084              * @event select
10085              * Fires when a date is selected
10086              * @param {DatePicker} this
10087              * @param {Date} date The selected date
10088              */
10089         select: true
10090     });
10091
10092     if(this.handler){
10093         this.on("select", this.handler,  this.scope || this);
10094     }
10095     // build the disabledDatesRE
10096     if(!this.disabledDatesRE && this.disabledDates){
10097         var dd = this.disabledDates;
10098         var re = "(?:";
10099         for(var i = 0; i < dd.length; i++){
10100             re += dd[i];
10101             if(i != dd.length-1) re += "|";
10102         }
10103         this.disabledDatesRE = new RegExp(re + ")");
10104     }
10105 };
10106
10107 Roo.extend(Roo.DatePicker, Roo.Component, {
10108     /**
10109      * @cfg {String} todayText
10110      * The text to display on the button that selects the current date (defaults to "Today")
10111      */
10112     todayText : "Today",
10113     /**
10114      * @cfg {String} okText
10115      * The text to display on the ok button
10116      */
10117     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10118     /**
10119      * @cfg {String} cancelText
10120      * The text to display on the cancel button
10121      */
10122     cancelText : "Cancel",
10123     /**
10124      * @cfg {String} todayTip
10125      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10126      */
10127     todayTip : "{0} (Spacebar)",
10128     /**
10129      * @cfg {Date} minDate
10130      * Minimum allowable date (JavaScript date object, defaults to null)
10131      */
10132     minDate : null,
10133     /**
10134      * @cfg {Date} maxDate
10135      * Maximum allowable date (JavaScript date object, defaults to null)
10136      */
10137     maxDate : null,
10138     /**
10139      * @cfg {String} minText
10140      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10141      */
10142     minText : "This date is before the minimum date",
10143     /**
10144      * @cfg {String} maxText
10145      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10146      */
10147     maxText : "This date is after the maximum date",
10148     /**
10149      * @cfg {String} format
10150      * The default date format string which can be overriden for localization support.  The format must be
10151      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10152      */
10153     format : "m/d/y",
10154     /**
10155      * @cfg {Array} disabledDays
10156      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10157      */
10158     disabledDays : null,
10159     /**
10160      * @cfg {String} disabledDaysText
10161      * The tooltip to display when the date falls on a disabled day (defaults to "")
10162      */
10163     disabledDaysText : "",
10164     /**
10165      * @cfg {RegExp} disabledDatesRE
10166      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10167      */
10168     disabledDatesRE : null,
10169     /**
10170      * @cfg {String} disabledDatesText
10171      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10172      */
10173     disabledDatesText : "",
10174     /**
10175      * @cfg {Boolean} constrainToViewport
10176      * True to constrain the date picker to the viewport (defaults to true)
10177      */
10178     constrainToViewport : true,
10179     /**
10180      * @cfg {Array} monthNames
10181      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10182      */
10183     monthNames : Date.monthNames,
10184     /**
10185      * @cfg {Array} dayNames
10186      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10187      */
10188     dayNames : Date.dayNames,
10189     /**
10190      * @cfg {String} nextText
10191      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10192      */
10193     nextText: 'Next Month (Control+Right)',
10194     /**
10195      * @cfg {String} prevText
10196      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10197      */
10198     prevText: 'Previous Month (Control+Left)',
10199     /**
10200      * @cfg {String} monthYearText
10201      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10202      */
10203     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10204     /**
10205      * @cfg {Number} startDay
10206      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10207      */
10208     startDay : 0,
10209     /**
10210      * @cfg {Bool} showClear
10211      * Show a clear button (usefull for date form elements that can be blank.)
10212      */
10213     
10214     showClear: false,
10215     
10216     /**
10217      * Sets the value of the date field
10218      * @param {Date} value The date to set
10219      */
10220     setValue : function(value){
10221         var old = this.value;
10222         this.value = value.clearTime(true);
10223         if(this.el){
10224             this.update(this.value);
10225         }
10226     },
10227
10228     /**
10229      * Gets the current selected value of the date field
10230      * @return {Date} The selected date
10231      */
10232     getValue : function(){
10233         return this.value;
10234     },
10235
10236     // private
10237     focus : function(){
10238         if(this.el){
10239             this.update(this.activeDate);
10240         }
10241     },
10242
10243     // private
10244     onRender : function(container, position){
10245         var m = [
10246              '<table cellspacing="0">',
10247                 '<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>',
10248                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10249         var dn = this.dayNames;
10250         for(var i = 0; i < 7; i++){
10251             var d = this.startDay+i;
10252             if(d > 6){
10253                 d = d-7;
10254             }
10255             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10256         }
10257         m[m.length] = "</tr></thead><tbody><tr>";
10258         for(var i = 0; i < 42; i++) {
10259             if(i % 7 == 0 && i != 0){
10260                 m[m.length] = "</tr><tr>";
10261             }
10262             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10263         }
10264         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10265             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10266
10267         var el = document.createElement("div");
10268         el.className = "x-date-picker";
10269         el.innerHTML = m.join("");
10270
10271         container.dom.insertBefore(el, position);
10272
10273         this.el = Roo.get(el);
10274         this.eventEl = Roo.get(el.firstChild);
10275
10276         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10277             handler: this.showPrevMonth,
10278             scope: this,
10279             preventDefault:true,
10280             stopDefault:true
10281         });
10282
10283         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10284             handler: this.showNextMonth,
10285             scope: this,
10286             preventDefault:true,
10287             stopDefault:true
10288         });
10289
10290         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10291
10292         this.monthPicker = this.el.down('div.x-date-mp');
10293         this.monthPicker.enableDisplayMode('block');
10294         
10295         var kn = new Roo.KeyNav(this.eventEl, {
10296             "left" : function(e){
10297                 e.ctrlKey ?
10298                     this.showPrevMonth() :
10299                     this.update(this.activeDate.add("d", -1));
10300             },
10301
10302             "right" : function(e){
10303                 e.ctrlKey ?
10304                     this.showNextMonth() :
10305                     this.update(this.activeDate.add("d", 1));
10306             },
10307
10308             "up" : function(e){
10309                 e.ctrlKey ?
10310                     this.showNextYear() :
10311                     this.update(this.activeDate.add("d", -7));
10312             },
10313
10314             "down" : function(e){
10315                 e.ctrlKey ?
10316                     this.showPrevYear() :
10317                     this.update(this.activeDate.add("d", 7));
10318             },
10319
10320             "pageUp" : function(e){
10321                 this.showNextMonth();
10322             },
10323
10324             "pageDown" : function(e){
10325                 this.showPrevMonth();
10326             },
10327
10328             "enter" : function(e){
10329                 e.stopPropagation();
10330                 return true;
10331             },
10332
10333             scope : this
10334         });
10335
10336         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10337
10338         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10339
10340         this.el.unselectable();
10341         
10342         this.cells = this.el.select("table.x-date-inner tbody td");
10343         this.textNodes = this.el.query("table.x-date-inner tbody span");
10344
10345         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10346             text: "&#160;",
10347             tooltip: this.monthYearText
10348         });
10349
10350         this.mbtn.on('click', this.showMonthPicker, this);
10351         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10352
10353
10354         var today = (new Date()).dateFormat(this.format);
10355         
10356         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10357         if (this.showClear) {
10358             baseTb.add( new Roo.Toolbar.Fill());
10359         }
10360         baseTb.add({
10361             text: String.format(this.todayText, today),
10362             tooltip: String.format(this.todayTip, today),
10363             handler: this.selectToday,
10364             scope: this
10365         });
10366         
10367         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10368             
10369         //});
10370         if (this.showClear) {
10371             
10372             baseTb.add( new Roo.Toolbar.Fill());
10373             baseTb.add({
10374                 text: '&#160;',
10375                 cls: 'x-btn-icon x-btn-clear',
10376                 handler: function() {
10377                     //this.value = '';
10378                     this.fireEvent("select", this, '');
10379                 },
10380                 scope: this
10381             });
10382         }
10383         
10384         
10385         if(Roo.isIE){
10386             this.el.repaint();
10387         }
10388         this.update(this.value);
10389     },
10390
10391     createMonthPicker : function(){
10392         if(!this.monthPicker.dom.firstChild){
10393             var buf = ['<table border="0" cellspacing="0">'];
10394             for(var i = 0; i < 6; i++){
10395                 buf.push(
10396                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10397                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10398                     i == 0 ?
10399                     '<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>' :
10400                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10401                 );
10402             }
10403             buf.push(
10404                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10405                     this.okText,
10406                     '</button><button type="button" class="x-date-mp-cancel">',
10407                     this.cancelText,
10408                     '</button></td></tr>',
10409                 '</table>'
10410             );
10411             this.monthPicker.update(buf.join(''));
10412             this.monthPicker.on('click', this.onMonthClick, this);
10413             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10414
10415             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10416             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10417
10418             this.mpMonths.each(function(m, a, i){
10419                 i += 1;
10420                 if((i%2) == 0){
10421                     m.dom.xmonth = 5 + Math.round(i * .5);
10422                 }else{
10423                     m.dom.xmonth = Math.round((i-1) * .5);
10424                 }
10425             });
10426         }
10427     },
10428
10429     showMonthPicker : function(){
10430         this.createMonthPicker();
10431         var size = this.el.getSize();
10432         this.monthPicker.setSize(size);
10433         this.monthPicker.child('table').setSize(size);
10434
10435         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10436         this.updateMPMonth(this.mpSelMonth);
10437         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10438         this.updateMPYear(this.mpSelYear);
10439
10440         this.monthPicker.slideIn('t', {duration:.2});
10441     },
10442
10443     updateMPYear : function(y){
10444         this.mpyear = y;
10445         var ys = this.mpYears.elements;
10446         for(var i = 1; i <= 10; i++){
10447             var td = ys[i-1], y2;
10448             if((i%2) == 0){
10449                 y2 = y + Math.round(i * .5);
10450                 td.firstChild.innerHTML = y2;
10451                 td.xyear = y2;
10452             }else{
10453                 y2 = y - (5-Math.round(i * .5));
10454                 td.firstChild.innerHTML = y2;
10455                 td.xyear = y2;
10456             }
10457             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10458         }
10459     },
10460
10461     updateMPMonth : function(sm){
10462         this.mpMonths.each(function(m, a, i){
10463             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10464         });
10465     },
10466
10467     selectMPMonth: function(m){
10468         
10469     },
10470
10471     onMonthClick : function(e, t){
10472         e.stopEvent();
10473         var el = new Roo.Element(t), pn;
10474         if(el.is('button.x-date-mp-cancel')){
10475             this.hideMonthPicker();
10476         }
10477         else if(el.is('button.x-date-mp-ok')){
10478             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10479             this.hideMonthPicker();
10480         }
10481         else if(pn = el.up('td.x-date-mp-month', 2)){
10482             this.mpMonths.removeClass('x-date-mp-sel');
10483             pn.addClass('x-date-mp-sel');
10484             this.mpSelMonth = pn.dom.xmonth;
10485         }
10486         else if(pn = el.up('td.x-date-mp-year', 2)){
10487             this.mpYears.removeClass('x-date-mp-sel');
10488             pn.addClass('x-date-mp-sel');
10489             this.mpSelYear = pn.dom.xyear;
10490         }
10491         else if(el.is('a.x-date-mp-prev')){
10492             this.updateMPYear(this.mpyear-10);
10493         }
10494         else if(el.is('a.x-date-mp-next')){
10495             this.updateMPYear(this.mpyear+10);
10496         }
10497     },
10498
10499     onMonthDblClick : function(e, t){
10500         e.stopEvent();
10501         var el = new Roo.Element(t), pn;
10502         if(pn = el.up('td.x-date-mp-month', 2)){
10503             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10504             this.hideMonthPicker();
10505         }
10506         else if(pn = el.up('td.x-date-mp-year', 2)){
10507             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10508             this.hideMonthPicker();
10509         }
10510     },
10511
10512     hideMonthPicker : function(disableAnim){
10513         if(this.monthPicker){
10514             if(disableAnim === true){
10515                 this.monthPicker.hide();
10516             }else{
10517                 this.monthPicker.slideOut('t', {duration:.2});
10518             }
10519         }
10520     },
10521
10522     // private
10523     showPrevMonth : function(e){
10524         this.update(this.activeDate.add("mo", -1));
10525     },
10526
10527     // private
10528     showNextMonth : function(e){
10529         this.update(this.activeDate.add("mo", 1));
10530     },
10531
10532     // private
10533     showPrevYear : function(){
10534         this.update(this.activeDate.add("y", -1));
10535     },
10536
10537     // private
10538     showNextYear : function(){
10539         this.update(this.activeDate.add("y", 1));
10540     },
10541
10542     // private
10543     handleMouseWheel : function(e){
10544         var delta = e.getWheelDelta();
10545         if(delta > 0){
10546             this.showPrevMonth();
10547             e.stopEvent();
10548         } else if(delta < 0){
10549             this.showNextMonth();
10550             e.stopEvent();
10551         }
10552     },
10553
10554     // private
10555     handleDateClick : function(e, t){
10556         e.stopEvent();
10557         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10558             this.setValue(new Date(t.dateValue));
10559             this.fireEvent("select", this, this.value);
10560         }
10561     },
10562
10563     // private
10564     selectToday : function(){
10565         this.setValue(new Date().clearTime());
10566         this.fireEvent("select", this, this.value);
10567     },
10568
10569     // private
10570     update : function(date){
10571         var vd = this.activeDate;
10572         this.activeDate = date;
10573         if(vd && this.el){
10574             var t = date.getTime();
10575             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10576                 this.cells.removeClass("x-date-selected");
10577                 this.cells.each(function(c){
10578                    if(c.dom.firstChild.dateValue == t){
10579                        c.addClass("x-date-selected");
10580                        setTimeout(function(){
10581                             try{c.dom.firstChild.focus();}catch(e){}
10582                        }, 50);
10583                        return false;
10584                    }
10585                 });
10586                 return;
10587             }
10588         }
10589         var days = date.getDaysInMonth();
10590         var firstOfMonth = date.getFirstDateOfMonth();
10591         var startingPos = firstOfMonth.getDay()-this.startDay;
10592
10593         if(startingPos <= this.startDay){
10594             startingPos += 7;
10595         }
10596
10597         var pm = date.add("mo", -1);
10598         var prevStart = pm.getDaysInMonth()-startingPos;
10599
10600         var cells = this.cells.elements;
10601         var textEls = this.textNodes;
10602         days += startingPos;
10603
10604         // convert everything to numbers so it's fast
10605         var day = 86400000;
10606         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10607         var today = new Date().clearTime().getTime();
10608         var sel = date.clearTime().getTime();
10609         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10610         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10611         var ddMatch = this.disabledDatesRE;
10612         var ddText = this.disabledDatesText;
10613         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10614         var ddaysText = this.disabledDaysText;
10615         var format = this.format;
10616
10617         var setCellClass = function(cal, cell){
10618             cell.title = "";
10619             var t = d.getTime();
10620             cell.firstChild.dateValue = t;
10621             if(t == today){
10622                 cell.className += " x-date-today";
10623                 cell.title = cal.todayText;
10624             }
10625             if(t == sel){
10626                 cell.className += " x-date-selected";
10627                 setTimeout(function(){
10628                     try{cell.firstChild.focus();}catch(e){}
10629                 }, 50);
10630             }
10631             // disabling
10632             if(t < min) {
10633                 cell.className = " x-date-disabled";
10634                 cell.title = cal.minText;
10635                 return;
10636             }
10637             if(t > max) {
10638                 cell.className = " x-date-disabled";
10639                 cell.title = cal.maxText;
10640                 return;
10641             }
10642             if(ddays){
10643                 if(ddays.indexOf(d.getDay()) != -1){
10644                     cell.title = ddaysText;
10645                     cell.className = " x-date-disabled";
10646                 }
10647             }
10648             if(ddMatch && format){
10649                 var fvalue = d.dateFormat(format);
10650                 if(ddMatch.test(fvalue)){
10651                     cell.title = ddText.replace("%0", fvalue);
10652                     cell.className = " x-date-disabled";
10653                 }
10654             }
10655         };
10656
10657         var i = 0;
10658         for(; i < startingPos; i++) {
10659             textEls[i].innerHTML = (++prevStart);
10660             d.setDate(d.getDate()+1);
10661             cells[i].className = "x-date-prevday";
10662             setCellClass(this, cells[i]);
10663         }
10664         for(; i < days; i++){
10665             intDay = i - startingPos + 1;
10666             textEls[i].innerHTML = (intDay);
10667             d.setDate(d.getDate()+1);
10668             cells[i].className = "x-date-active";
10669             setCellClass(this, cells[i]);
10670         }
10671         var extraDays = 0;
10672         for(; i < 42; i++) {
10673              textEls[i].innerHTML = (++extraDays);
10674              d.setDate(d.getDate()+1);
10675              cells[i].className = "x-date-nextday";
10676              setCellClass(this, cells[i]);
10677         }
10678
10679         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10680
10681         if(!this.internalRender){
10682             var main = this.el.dom.firstChild;
10683             var w = main.offsetWidth;
10684             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10685             Roo.fly(main).setWidth(w);
10686             this.internalRender = true;
10687             // opera does not respect the auto grow header center column
10688             // then, after it gets a width opera refuses to recalculate
10689             // without a second pass
10690             if(Roo.isOpera && !this.secondPass){
10691                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10692                 this.secondPass = true;
10693                 this.update.defer(10, this, [date]);
10694             }
10695         }
10696     }
10697 });        /*
10698  * Based on:
10699  * Ext JS Library 1.1.1
10700  * Copyright(c) 2006-2007, Ext JS, LLC.
10701  *
10702  * Originally Released Under LGPL - original licence link has changed is not relivant.
10703  *
10704  * Fork - LGPL
10705  * <script type="text/javascript">
10706  */
10707 /**
10708  * @class Roo.TabPanel
10709  * @extends Roo.util.Observable
10710  * A lightweight tab container.
10711  * <br><br>
10712  * Usage:
10713  * <pre><code>
10714 // basic tabs 1, built from existing content
10715 var tabs = new Roo.TabPanel("tabs1");
10716 tabs.addTab("script", "View Script");
10717 tabs.addTab("markup", "View Markup");
10718 tabs.activate("script");
10719
10720 // more advanced tabs, built from javascript
10721 var jtabs = new Roo.TabPanel("jtabs");
10722 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10723
10724 // set up the UpdateManager
10725 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10726 var updater = tab2.getUpdateManager();
10727 updater.setDefaultUrl("ajax1.htm");
10728 tab2.on('activate', updater.refresh, updater, true);
10729
10730 // Use setUrl for Ajax loading
10731 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10732 tab3.setUrl("ajax2.htm", null, true);
10733
10734 // Disabled tab
10735 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10736 tab4.disable();
10737
10738 jtabs.activate("jtabs-1");
10739  * </code></pre>
10740  * @constructor
10741  * Create a new TabPanel.
10742  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10743  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10744  */
10745 Roo.TabPanel = function(container, config){
10746     /**
10747     * The container element for this TabPanel.
10748     * @type Roo.Element
10749     */
10750     this.el = Roo.get(container, true);
10751     if(config){
10752         if(typeof config == "boolean"){
10753             this.tabPosition = config ? "bottom" : "top";
10754         }else{
10755             Roo.apply(this, config);
10756         }
10757     }
10758     if(this.tabPosition == "bottom"){
10759         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10760         this.el.addClass("x-tabs-bottom");
10761     }
10762     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10763     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10764     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10765     if(Roo.isIE){
10766         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10767     }
10768     if(this.tabPosition != "bottom"){
10769         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10770          * @type Roo.Element
10771          */
10772         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10773         this.el.addClass("x-tabs-top");
10774     }
10775     this.items = [];
10776
10777     this.bodyEl.setStyle("position", "relative");
10778
10779     this.active = null;
10780     this.activateDelegate = this.activate.createDelegate(this);
10781
10782     this.addEvents({
10783         /**
10784          * @event tabchange
10785          * Fires when the active tab changes
10786          * @param {Roo.TabPanel} this
10787          * @param {Roo.TabPanelItem} activePanel The new active tab
10788          */
10789         "tabchange": true,
10790         /**
10791          * @event beforetabchange
10792          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10793          * @param {Roo.TabPanel} this
10794          * @param {Object} e Set cancel to true on this object to cancel the tab change
10795          * @param {Roo.TabPanelItem} tab The tab being changed to
10796          */
10797         "beforetabchange" : true
10798     });
10799
10800     Roo.EventManager.onWindowResize(this.onResize, this);
10801     this.cpad = this.el.getPadding("lr");
10802     this.hiddenCount = 0;
10803
10804
10805     // toolbar on the tabbar support...
10806     if (this.toolbar) {
10807         var tcfg = this.toolbar;
10808         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10809         this.toolbar = new Roo.Toolbar(tcfg);
10810         if (Roo.isSafari) {
10811             var tbl = tcfg.container.child('table', true);
10812             tbl.setAttribute('width', '100%');
10813         }
10814         
10815     }
10816    
10817
10818
10819     Roo.TabPanel.superclass.constructor.call(this);
10820 };
10821
10822 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10823     /*
10824      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10825      */
10826     tabPosition : "top",
10827     /*
10828      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10829      */
10830     currentTabWidth : 0,
10831     /*
10832      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10833      */
10834     minTabWidth : 40,
10835     /*
10836      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10837      */
10838     maxTabWidth : 250,
10839     /*
10840      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10841      */
10842     preferredTabWidth : 175,
10843     /*
10844      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10845      */
10846     resizeTabs : false,
10847     /*
10848      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10849      */
10850     monitorResize : true,
10851     /*
10852      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10853      */
10854     toolbar : false,
10855
10856     /**
10857      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10858      * @param {String} id The id of the div to use <b>or create</b>
10859      * @param {String} text The text for the tab
10860      * @param {String} content (optional) Content to put in the TabPanelItem body
10861      * @param {Boolean} closable (optional) True to create a close icon on the tab
10862      * @return {Roo.TabPanelItem} The created TabPanelItem
10863      */
10864     addTab : function(id, text, content, closable){
10865         var item = new Roo.TabPanelItem(this, id, text, closable);
10866         this.addTabItem(item);
10867         if(content){
10868             item.setContent(content);
10869         }
10870         return item;
10871     },
10872
10873     /**
10874      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10875      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10876      * @return {Roo.TabPanelItem}
10877      */
10878     getTab : function(id){
10879         return this.items[id];
10880     },
10881
10882     /**
10883      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10884      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10885      */
10886     hideTab : function(id){
10887         var t = this.items[id];
10888         if(!t.isHidden()){
10889            t.setHidden(true);
10890            this.hiddenCount++;
10891            this.autoSizeTabs();
10892         }
10893     },
10894
10895     /**
10896      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10897      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10898      */
10899     unhideTab : function(id){
10900         var t = this.items[id];
10901         if(t.isHidden()){
10902            t.setHidden(false);
10903            this.hiddenCount--;
10904            this.autoSizeTabs();
10905         }
10906     },
10907
10908     /**
10909      * Adds an existing {@link Roo.TabPanelItem}.
10910      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10911      */
10912     addTabItem : function(item){
10913         this.items[item.id] = item;
10914         this.items.push(item);
10915         if(this.resizeTabs){
10916            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10917            this.autoSizeTabs();
10918         }else{
10919             item.autoSize();
10920         }
10921     },
10922
10923     /**
10924      * Removes a {@link Roo.TabPanelItem}.
10925      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10926      */
10927     removeTab : function(id){
10928         var items = this.items;
10929         var tab = items[id];
10930         if(!tab) { return; }
10931         var index = items.indexOf(tab);
10932         if(this.active == tab && items.length > 1){
10933             var newTab = this.getNextAvailable(index);
10934             if(newTab) {
10935                 newTab.activate();
10936             }
10937         }
10938         this.stripEl.dom.removeChild(tab.pnode.dom);
10939         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10940             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10941         }
10942         items.splice(index, 1);
10943         delete this.items[tab.id];
10944         tab.fireEvent("close", tab);
10945         tab.purgeListeners();
10946         this.autoSizeTabs();
10947     },
10948
10949     getNextAvailable : function(start){
10950         var items = this.items;
10951         var index = start;
10952         // look for a next tab that will slide over to
10953         // replace the one being removed
10954         while(index < items.length){
10955             var item = items[++index];
10956             if(item && !item.isHidden()){
10957                 return item;
10958             }
10959         }
10960         // if one isn't found select the previous tab (on the left)
10961         index = start;
10962         while(index >= 0){
10963             var item = items[--index];
10964             if(item && !item.isHidden()){
10965                 return item;
10966             }
10967         }
10968         return null;
10969     },
10970
10971     /**
10972      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10973      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10974      */
10975     disableTab : function(id){
10976         var tab = this.items[id];
10977         if(tab && this.active != tab){
10978             tab.disable();
10979         }
10980     },
10981
10982     /**
10983      * Enables a {@link Roo.TabPanelItem} that is disabled.
10984      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10985      */
10986     enableTab : function(id){
10987         var tab = this.items[id];
10988         tab.enable();
10989     },
10990
10991     /**
10992      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10993      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10994      * @return {Roo.TabPanelItem} The TabPanelItem.
10995      */
10996     activate : function(id){
10997         var tab = this.items[id];
10998         if(!tab){
10999             return null;
11000         }
11001         if(tab == this.active || tab.disabled){
11002             return tab;
11003         }
11004         var e = {};
11005         this.fireEvent("beforetabchange", this, e, tab);
11006         if(e.cancel !== true && !tab.disabled){
11007             if(this.active){
11008                 this.active.hide();
11009             }
11010             this.active = this.items[id];
11011             this.active.show();
11012             this.fireEvent("tabchange", this, this.active);
11013         }
11014         return tab;
11015     },
11016
11017     /**
11018      * Gets the active {@link Roo.TabPanelItem}.
11019      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11020      */
11021     getActiveTab : function(){
11022         return this.active;
11023     },
11024
11025     /**
11026      * Updates the tab body element to fit the height of the container element
11027      * for overflow scrolling
11028      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11029      */
11030     syncHeight : function(targetHeight){
11031         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11032         var bm = this.bodyEl.getMargins();
11033         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11034         this.bodyEl.setHeight(newHeight);
11035         return newHeight;
11036     },
11037
11038     onResize : function(){
11039         if(this.monitorResize){
11040             this.autoSizeTabs();
11041         }
11042     },
11043
11044     /**
11045      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11046      */
11047     beginUpdate : function(){
11048         this.updating = true;
11049     },
11050
11051     /**
11052      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11053      */
11054     endUpdate : function(){
11055         this.updating = false;
11056         this.autoSizeTabs();
11057     },
11058
11059     /**
11060      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11061      */
11062     autoSizeTabs : function(){
11063         var count = this.items.length;
11064         var vcount = count - this.hiddenCount;
11065         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11066         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11067         var availWidth = Math.floor(w / vcount);
11068         var b = this.stripBody;
11069         if(b.getWidth() > w){
11070             var tabs = this.items;
11071             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11072             if(availWidth < this.minTabWidth){
11073                 /*if(!this.sleft){    // incomplete scrolling code
11074                     this.createScrollButtons();
11075                 }
11076                 this.showScroll();
11077                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11078             }
11079         }else{
11080             if(this.currentTabWidth < this.preferredTabWidth){
11081                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11082             }
11083         }
11084     },
11085
11086     /**
11087      * Returns the number of tabs in this TabPanel.
11088      * @return {Number}
11089      */
11090      getCount : function(){
11091          return this.items.length;
11092      },
11093
11094     /**
11095      * Resizes all the tabs to the passed width
11096      * @param {Number} The new width
11097      */
11098     setTabWidth : function(width){
11099         this.currentTabWidth = width;
11100         for(var i = 0, len = this.items.length; i < len; i++) {
11101                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11102         }
11103     },
11104
11105     /**
11106      * Destroys this TabPanel
11107      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11108      */
11109     destroy : function(removeEl){
11110         Roo.EventManager.removeResizeListener(this.onResize, this);
11111         for(var i = 0, len = this.items.length; i < len; i++){
11112             this.items[i].purgeListeners();
11113         }
11114         if(removeEl === true){
11115             this.el.update("");
11116             this.el.remove();
11117         }
11118     }
11119 });
11120
11121 /**
11122  * @class Roo.TabPanelItem
11123  * @extends Roo.util.Observable
11124  * Represents an individual item (tab plus body) in a TabPanel.
11125  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11126  * @param {String} id The id of this TabPanelItem
11127  * @param {String} text The text for the tab of this TabPanelItem
11128  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11129  */
11130 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11131     /**
11132      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11133      * @type Roo.TabPanel
11134      */
11135     this.tabPanel = tabPanel;
11136     /**
11137      * The id for this TabPanelItem
11138      * @type String
11139      */
11140     this.id = id;
11141     /** @private */
11142     this.disabled = false;
11143     /** @private */
11144     this.text = text;
11145     /** @private */
11146     this.loaded = false;
11147     this.closable = closable;
11148
11149     /**
11150      * The body element for this TabPanelItem.
11151      * @type Roo.Element
11152      */
11153     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11154     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11155     this.bodyEl.setStyle("display", "block");
11156     this.bodyEl.setStyle("zoom", "1");
11157     this.hideAction();
11158
11159     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11160     /** @private */
11161     this.el = Roo.get(els.el, true);
11162     this.inner = Roo.get(els.inner, true);
11163     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11164     this.pnode = Roo.get(els.el.parentNode, true);
11165     this.el.on("mousedown", this.onTabMouseDown, this);
11166     this.el.on("click", this.onTabClick, this);
11167     /** @private */
11168     if(closable){
11169         var c = Roo.get(els.close, true);
11170         c.dom.title = this.closeText;
11171         c.addClassOnOver("close-over");
11172         c.on("click", this.closeClick, this);
11173      }
11174
11175     this.addEvents({
11176          /**
11177          * @event activate
11178          * Fires when this tab becomes the active tab.
11179          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11180          * @param {Roo.TabPanelItem} this
11181          */
11182         "activate": true,
11183         /**
11184          * @event beforeclose
11185          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11186          * @param {Roo.TabPanelItem} this
11187          * @param {Object} e Set cancel to true on this object to cancel the close.
11188          */
11189         "beforeclose": true,
11190         /**
11191          * @event close
11192          * Fires when this tab is closed.
11193          * @param {Roo.TabPanelItem} this
11194          */
11195          "close": true,
11196         /**
11197          * @event deactivate
11198          * Fires when this tab is no longer the active tab.
11199          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11200          * @param {Roo.TabPanelItem} this
11201          */
11202          "deactivate" : true
11203     });
11204     this.hidden = false;
11205
11206     Roo.TabPanelItem.superclass.constructor.call(this);
11207 };
11208
11209 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11210     purgeListeners : function(){
11211        Roo.util.Observable.prototype.purgeListeners.call(this);
11212        this.el.removeAllListeners();
11213     },
11214     /**
11215      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11216      */
11217     show : function(){
11218         this.pnode.addClass("on");
11219         this.showAction();
11220         if(Roo.isOpera){
11221             this.tabPanel.stripWrap.repaint();
11222         }
11223         this.fireEvent("activate", this.tabPanel, this);
11224     },
11225
11226     /**
11227      * Returns true if this tab is the active tab.
11228      * @return {Boolean}
11229      */
11230     isActive : function(){
11231         return this.tabPanel.getActiveTab() == this;
11232     },
11233
11234     /**
11235      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11236      */
11237     hide : function(){
11238         this.pnode.removeClass("on");
11239         this.hideAction();
11240         this.fireEvent("deactivate", this.tabPanel, this);
11241     },
11242
11243     hideAction : function(){
11244         this.bodyEl.hide();
11245         this.bodyEl.setStyle("position", "absolute");
11246         this.bodyEl.setLeft("-20000px");
11247         this.bodyEl.setTop("-20000px");
11248     },
11249
11250     showAction : function(){
11251         this.bodyEl.setStyle("position", "relative");
11252         this.bodyEl.setTop("");
11253         this.bodyEl.setLeft("");
11254         this.bodyEl.show();
11255     },
11256
11257     /**
11258      * Set the tooltip for the tab.
11259      * @param {String} tooltip The tab's tooltip
11260      */
11261     setTooltip : function(text){
11262         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11263             this.textEl.dom.qtip = text;
11264             this.textEl.dom.removeAttribute('title');
11265         }else{
11266             this.textEl.dom.title = text;
11267         }
11268     },
11269
11270     onTabClick : function(e){
11271         e.preventDefault();
11272         this.tabPanel.activate(this.id);
11273     },
11274
11275     onTabMouseDown : function(e){
11276         e.preventDefault();
11277         this.tabPanel.activate(this.id);
11278     },
11279
11280     getWidth : function(){
11281         return this.inner.getWidth();
11282     },
11283
11284     setWidth : function(width){
11285         var iwidth = width - this.pnode.getPadding("lr");
11286         this.inner.setWidth(iwidth);
11287         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11288         this.pnode.setWidth(width);
11289     },
11290
11291     /**
11292      * Show or hide the tab
11293      * @param {Boolean} hidden True to hide or false to show.
11294      */
11295     setHidden : function(hidden){
11296         this.hidden = hidden;
11297         this.pnode.setStyle("display", hidden ? "none" : "");
11298     },
11299
11300     /**
11301      * Returns true if this tab is "hidden"
11302      * @return {Boolean}
11303      */
11304     isHidden : function(){
11305         return this.hidden;
11306     },
11307
11308     /**
11309      * Returns the text for this tab
11310      * @return {String}
11311      */
11312     getText : function(){
11313         return this.text;
11314     },
11315
11316     autoSize : function(){
11317         //this.el.beginMeasure();
11318         this.textEl.setWidth(1);
11319         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11320         //this.el.endMeasure();
11321     },
11322
11323     /**
11324      * Sets the text for the tab (Note: this also sets the tooltip text)
11325      * @param {String} text The tab's text and tooltip
11326      */
11327     setText : function(text){
11328         this.text = text;
11329         this.textEl.update(text);
11330         this.setTooltip(text);
11331         if(!this.tabPanel.resizeTabs){
11332             this.autoSize();
11333         }
11334     },
11335     /**
11336      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11337      */
11338     activate : function(){
11339         this.tabPanel.activate(this.id);
11340     },
11341
11342     /**
11343      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11344      */
11345     disable : function(){
11346         if(this.tabPanel.active != this){
11347             this.disabled = true;
11348             this.pnode.addClass("disabled");
11349         }
11350     },
11351
11352     /**
11353      * Enables this TabPanelItem if it was previously disabled.
11354      */
11355     enable : function(){
11356         this.disabled = false;
11357         this.pnode.removeClass("disabled");
11358     },
11359
11360     /**
11361      * Sets the content for this TabPanelItem.
11362      * @param {String} content The content
11363      * @param {Boolean} loadScripts true to look for and load scripts
11364      */
11365     setContent : function(content, loadScripts){
11366         this.bodyEl.update(content, loadScripts);
11367     },
11368
11369     /**
11370      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11371      * @return {Roo.UpdateManager} The UpdateManager
11372      */
11373     getUpdateManager : function(){
11374         return this.bodyEl.getUpdateManager();
11375     },
11376
11377     /**
11378      * Set a URL to be used to load the content for this TabPanelItem.
11379      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11380      * @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)
11381      * @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)
11382      * @return {Roo.UpdateManager} The UpdateManager
11383      */
11384     setUrl : function(url, params, loadOnce){
11385         if(this.refreshDelegate){
11386             this.un('activate', this.refreshDelegate);
11387         }
11388         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11389         this.on("activate", this.refreshDelegate);
11390         return this.bodyEl.getUpdateManager();
11391     },
11392
11393     /** @private */
11394     _handleRefresh : function(url, params, loadOnce){
11395         if(!loadOnce || !this.loaded){
11396             var updater = this.bodyEl.getUpdateManager();
11397             updater.update(url, params, this._setLoaded.createDelegate(this));
11398         }
11399     },
11400
11401     /**
11402      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11403      *   Will fail silently if the setUrl method has not been called.
11404      *   This does not activate the panel, just updates its content.
11405      */
11406     refresh : function(){
11407         if(this.refreshDelegate){
11408            this.loaded = false;
11409            this.refreshDelegate();
11410         }
11411     },
11412
11413     /** @private */
11414     _setLoaded : function(){
11415         this.loaded = true;
11416     },
11417
11418     /** @private */
11419     closeClick : function(e){
11420         var o = {};
11421         e.stopEvent();
11422         this.fireEvent("beforeclose", this, o);
11423         if(o.cancel !== true){
11424             this.tabPanel.removeTab(this.id);
11425         }
11426     },
11427     /**
11428      * The text displayed in the tooltip for the close icon.
11429      * @type String
11430      */
11431     closeText : "Close this tab"
11432 });
11433
11434 /** @private */
11435 Roo.TabPanel.prototype.createStrip = function(container){
11436     var strip = document.createElement("div");
11437     strip.className = "x-tabs-wrap";
11438     container.appendChild(strip);
11439     return strip;
11440 };
11441 /** @private */
11442 Roo.TabPanel.prototype.createStripList = function(strip){
11443     // div wrapper for retard IE
11444     // returns the "tr" element.
11445     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11446         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11447         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11448     return strip.firstChild.firstChild.firstChild.firstChild;
11449 };
11450 /** @private */
11451 Roo.TabPanel.prototype.createBody = function(container){
11452     var body = document.createElement("div");
11453     Roo.id(body, "tab-body");
11454     Roo.fly(body).addClass("x-tabs-body");
11455     container.appendChild(body);
11456     return body;
11457 };
11458 /** @private */
11459 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11460     var body = Roo.getDom(id);
11461     if(!body){
11462         body = document.createElement("div");
11463         body.id = id;
11464     }
11465     Roo.fly(body).addClass("x-tabs-item-body");
11466     bodyEl.insertBefore(body, bodyEl.firstChild);
11467     return body;
11468 };
11469 /** @private */
11470 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11471     var td = document.createElement("td");
11472     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11473     //stripEl.appendChild(td);
11474     if(closable){
11475         td.className = "x-tabs-closable";
11476         if(!this.closeTpl){
11477             this.closeTpl = new Roo.Template(
11478                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11479                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11480                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11481             );
11482         }
11483         var el = this.closeTpl.overwrite(td, {"text": text});
11484         var close = el.getElementsByTagName("div")[0];
11485         var inner = el.getElementsByTagName("em")[0];
11486         return {"el": el, "close": close, "inner": inner};
11487     } else {
11488         if(!this.tabTpl){
11489             this.tabTpl = new Roo.Template(
11490                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11491                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11492             );
11493         }
11494         var el = this.tabTpl.overwrite(td, {"text": text});
11495         var inner = el.getElementsByTagName("em")[0];
11496         return {"el": el, "inner": inner};
11497     }
11498 };/*
11499  * Based on:
11500  * Ext JS Library 1.1.1
11501  * Copyright(c) 2006-2007, Ext JS, LLC.
11502  *
11503  * Originally Released Under LGPL - original licence link has changed is not relivant.
11504  *
11505  * Fork - LGPL
11506  * <script type="text/javascript">
11507  */
11508
11509 /**
11510  * @class Roo.Button
11511  * @extends Roo.util.Observable
11512  * Simple Button class
11513  * @cfg {String} text The button text
11514  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11515  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11516  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11517  * @cfg {Object} scope The scope of the handler
11518  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11519  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11520  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11521  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11522  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11523  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11524    applies if enableToggle = true)
11525  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11526  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11527   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11528  * @constructor
11529  * Create a new button
11530  * @param {Object} config The config object
11531  */
11532 Roo.Button = function(renderTo, config)
11533 {
11534     if (!config) {
11535         config = renderTo;
11536         renderTo = config.renderTo || false;
11537     }
11538     
11539     Roo.apply(this, config);
11540     this.addEvents({
11541         /**
11542              * @event click
11543              * Fires when this button is clicked
11544              * @param {Button} this
11545              * @param {EventObject} e The click event
11546              */
11547             "click" : true,
11548         /**
11549              * @event toggle
11550              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11551              * @param {Button} this
11552              * @param {Boolean} pressed
11553              */
11554             "toggle" : true,
11555         /**
11556              * @event mouseover
11557              * Fires when the mouse hovers over the button
11558              * @param {Button} this
11559              * @param {Event} e The event object
11560              */
11561         'mouseover' : true,
11562         /**
11563              * @event mouseout
11564              * Fires when the mouse exits the button
11565              * @param {Button} this
11566              * @param {Event} e The event object
11567              */
11568         'mouseout': true,
11569          /**
11570              * @event render
11571              * Fires when the button is rendered
11572              * @param {Button} this
11573              */
11574         'render': true
11575     });
11576     if(this.menu){
11577         this.menu = Roo.menu.MenuMgr.get(this.menu);
11578     }
11579     // register listeners first!!  - so render can be captured..
11580     Roo.util.Observable.call(this);
11581     if(renderTo){
11582         this.render(renderTo);
11583     }
11584     
11585   
11586 };
11587
11588 Roo.extend(Roo.Button, Roo.util.Observable, {
11589     /**
11590      * 
11591      */
11592     
11593     /**
11594      * Read-only. True if this button is hidden
11595      * @type Boolean
11596      */
11597     hidden : false,
11598     /**
11599      * Read-only. True if this button is disabled
11600      * @type Boolean
11601      */
11602     disabled : false,
11603     /**
11604      * Read-only. True if this button is pressed (only if enableToggle = true)
11605      * @type Boolean
11606      */
11607     pressed : false,
11608
11609     /**
11610      * @cfg {Number} tabIndex 
11611      * The DOM tabIndex for this button (defaults to undefined)
11612      */
11613     tabIndex : undefined,
11614
11615     /**
11616      * @cfg {Boolean} enableToggle
11617      * True to enable pressed/not pressed toggling (defaults to false)
11618      */
11619     enableToggle: false,
11620     /**
11621      * @cfg {Mixed} menu
11622      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11623      */
11624     menu : undefined,
11625     /**
11626      * @cfg {String} menuAlign
11627      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11628      */
11629     menuAlign : "tl-bl?",
11630
11631     /**
11632      * @cfg {String} iconCls
11633      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11634      */
11635     iconCls : undefined,
11636     /**
11637      * @cfg {String} type
11638      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11639      */
11640     type : 'button',
11641
11642     // private
11643     menuClassTarget: 'tr',
11644
11645     /**
11646      * @cfg {String} clickEvent
11647      * The type of event to map to the button's event handler (defaults to 'click')
11648      */
11649     clickEvent : 'click',
11650
11651     /**
11652      * @cfg {Boolean} handleMouseEvents
11653      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11654      */
11655     handleMouseEvents : true,
11656
11657     /**
11658      * @cfg {String} tooltipType
11659      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11660      */
11661     tooltipType : 'qtip',
11662
11663     /**
11664      * @cfg {String} cls
11665      * A CSS class to apply to the button's main element.
11666      */
11667     
11668     /**
11669      * @cfg {Roo.Template} template (Optional)
11670      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11671      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11672      * require code modifications if required elements (e.g. a button) aren't present.
11673      */
11674
11675     // private
11676     render : function(renderTo){
11677         var btn;
11678         if(this.hideParent){
11679             this.parentEl = Roo.get(renderTo);
11680         }
11681         if(!this.dhconfig){
11682             if(!this.template){
11683                 if(!Roo.Button.buttonTemplate){
11684                     // hideous table template
11685                     Roo.Button.buttonTemplate = new Roo.Template(
11686                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11687                         '<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>',
11688                         "</tr></tbody></table>");
11689                 }
11690                 this.template = Roo.Button.buttonTemplate;
11691             }
11692             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11693             var btnEl = btn.child("button:first");
11694             btnEl.on('focus', this.onFocus, this);
11695             btnEl.on('blur', this.onBlur, this);
11696             if(this.cls){
11697                 btn.addClass(this.cls);
11698             }
11699             if(this.icon){
11700                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11701             }
11702             if(this.iconCls){
11703                 btnEl.addClass(this.iconCls);
11704                 if(!this.cls){
11705                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11706                 }
11707             }
11708             if(this.tabIndex !== undefined){
11709                 btnEl.dom.tabIndex = this.tabIndex;
11710             }
11711             if(this.tooltip){
11712                 if(typeof this.tooltip == 'object'){
11713                     Roo.QuickTips.tips(Roo.apply({
11714                           target: btnEl.id
11715                     }, this.tooltip));
11716                 } else {
11717                     btnEl.dom[this.tooltipType] = this.tooltip;
11718                 }
11719             }
11720         }else{
11721             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11722         }
11723         this.el = btn;
11724         if(this.id){
11725             this.el.dom.id = this.el.id = this.id;
11726         }
11727         if(this.menu){
11728             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11729             this.menu.on("show", this.onMenuShow, this);
11730             this.menu.on("hide", this.onMenuHide, this);
11731         }
11732         btn.addClass("x-btn");
11733         if(Roo.isIE && !Roo.isIE7){
11734             this.autoWidth.defer(1, this);
11735         }else{
11736             this.autoWidth();
11737         }
11738         if(this.handleMouseEvents){
11739             btn.on("mouseover", this.onMouseOver, this);
11740             btn.on("mouseout", this.onMouseOut, this);
11741             btn.on("mousedown", this.onMouseDown, this);
11742         }
11743         btn.on(this.clickEvent, this.onClick, this);
11744         //btn.on("mouseup", this.onMouseUp, this);
11745         if(this.hidden){
11746             this.hide();
11747         }
11748         if(this.disabled){
11749             this.disable();
11750         }
11751         Roo.ButtonToggleMgr.register(this);
11752         if(this.pressed){
11753             this.el.addClass("x-btn-pressed");
11754         }
11755         if(this.repeat){
11756             var repeater = new Roo.util.ClickRepeater(btn,
11757                 typeof this.repeat == "object" ? this.repeat : {}
11758             );
11759             repeater.on("click", this.onClick,  this);
11760         }
11761         
11762         this.fireEvent('render', this);
11763         
11764     },
11765     /**
11766      * Returns the button's underlying element
11767      * @return {Roo.Element} The element
11768      */
11769     getEl : function(){
11770         return this.el;  
11771     },
11772     
11773     /**
11774      * Destroys this Button and removes any listeners.
11775      */
11776     destroy : function(){
11777         Roo.ButtonToggleMgr.unregister(this);
11778         this.el.removeAllListeners();
11779         this.purgeListeners();
11780         this.el.remove();
11781     },
11782
11783     // private
11784     autoWidth : function(){
11785         if(this.el){
11786             this.el.setWidth("auto");
11787             if(Roo.isIE7 && Roo.isStrict){
11788                 var ib = this.el.child('button');
11789                 if(ib && ib.getWidth() > 20){
11790                     ib.clip();
11791                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11792                 }
11793             }
11794             if(this.minWidth){
11795                 if(this.hidden){
11796                     this.el.beginMeasure();
11797                 }
11798                 if(this.el.getWidth() < this.minWidth){
11799                     this.el.setWidth(this.minWidth);
11800                 }
11801                 if(this.hidden){
11802                     this.el.endMeasure();
11803                 }
11804             }
11805         }
11806     },
11807
11808     /**
11809      * Assigns this button's click handler
11810      * @param {Function} handler The function to call when the button is clicked
11811      * @param {Object} scope (optional) Scope for the function passed in
11812      */
11813     setHandler : function(handler, scope){
11814         this.handler = handler;
11815         this.scope = scope;  
11816     },
11817     
11818     /**
11819      * Sets this button's text
11820      * @param {String} text The button text
11821      */
11822     setText : function(text){
11823         this.text = text;
11824         if(this.el){
11825             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11826         }
11827         this.autoWidth();
11828     },
11829     
11830     /**
11831      * Gets the text for this button
11832      * @return {String} The button text
11833      */
11834     getText : function(){
11835         return this.text;  
11836     },
11837     
11838     /**
11839      * Show this button
11840      */
11841     show: function(){
11842         this.hidden = false;
11843         if(this.el){
11844             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11845         }
11846     },
11847     
11848     /**
11849      * Hide this button
11850      */
11851     hide: function(){
11852         this.hidden = true;
11853         if(this.el){
11854             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11855         }
11856     },
11857     
11858     /**
11859      * Convenience function for boolean show/hide
11860      * @param {Boolean} visible True to show, false to hide
11861      */
11862     setVisible: function(visible){
11863         if(visible) {
11864             this.show();
11865         }else{
11866             this.hide();
11867         }
11868     },
11869     
11870     /**
11871      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11872      * @param {Boolean} state (optional) Force a particular state
11873      */
11874     toggle : function(state){
11875         state = state === undefined ? !this.pressed : state;
11876         if(state != this.pressed){
11877             if(state){
11878                 this.el.addClass("x-btn-pressed");
11879                 this.pressed = true;
11880                 this.fireEvent("toggle", this, true);
11881             }else{
11882                 this.el.removeClass("x-btn-pressed");
11883                 this.pressed = false;
11884                 this.fireEvent("toggle", this, false);
11885             }
11886             if(this.toggleHandler){
11887                 this.toggleHandler.call(this.scope || this, this, state);
11888             }
11889         }
11890     },
11891     
11892     /**
11893      * Focus the button
11894      */
11895     focus : function(){
11896         this.el.child('button:first').focus();
11897     },
11898     
11899     /**
11900      * Disable this button
11901      */
11902     disable : function(){
11903         if(this.el){
11904             this.el.addClass("x-btn-disabled");
11905         }
11906         this.disabled = true;
11907     },
11908     
11909     /**
11910      * Enable this button
11911      */
11912     enable : function(){
11913         if(this.el){
11914             this.el.removeClass("x-btn-disabled");
11915         }
11916         this.disabled = false;
11917     },
11918
11919     /**
11920      * Convenience function for boolean enable/disable
11921      * @param {Boolean} enabled True to enable, false to disable
11922      */
11923     setDisabled : function(v){
11924         this[v !== true ? "enable" : "disable"]();
11925     },
11926
11927     // private
11928     onClick : function(e){
11929         if(e){
11930             e.preventDefault();
11931         }
11932         if(e.button != 0){
11933             return;
11934         }
11935         if(!this.disabled){
11936             if(this.enableToggle){
11937                 this.toggle();
11938             }
11939             if(this.menu && !this.menu.isVisible()){
11940                 this.menu.show(this.el, this.menuAlign);
11941             }
11942             this.fireEvent("click", this, e);
11943             if(this.handler){
11944                 this.el.removeClass("x-btn-over");
11945                 this.handler.call(this.scope || this, this, e);
11946             }
11947         }
11948     },
11949     // private
11950     onMouseOver : function(e){
11951         if(!this.disabled){
11952             this.el.addClass("x-btn-over");
11953             this.fireEvent('mouseover', this, e);
11954         }
11955     },
11956     // private
11957     onMouseOut : function(e){
11958         if(!e.within(this.el,  true)){
11959             this.el.removeClass("x-btn-over");
11960             this.fireEvent('mouseout', this, e);
11961         }
11962     },
11963     // private
11964     onFocus : function(e){
11965         if(!this.disabled){
11966             this.el.addClass("x-btn-focus");
11967         }
11968     },
11969     // private
11970     onBlur : function(e){
11971         this.el.removeClass("x-btn-focus");
11972     },
11973     // private
11974     onMouseDown : function(e){
11975         if(!this.disabled && e.button == 0){
11976             this.el.addClass("x-btn-click");
11977             Roo.get(document).on('mouseup', this.onMouseUp, this);
11978         }
11979     },
11980     // private
11981     onMouseUp : function(e){
11982         if(e.button == 0){
11983             this.el.removeClass("x-btn-click");
11984             Roo.get(document).un('mouseup', this.onMouseUp, this);
11985         }
11986     },
11987     // private
11988     onMenuShow : function(e){
11989         this.el.addClass("x-btn-menu-active");
11990     },
11991     // private
11992     onMenuHide : function(e){
11993         this.el.removeClass("x-btn-menu-active");
11994     }   
11995 });
11996
11997 // Private utility class used by Button
11998 Roo.ButtonToggleMgr = function(){
11999    var groups = {};
12000    
12001    function toggleGroup(btn, state){
12002        if(state){
12003            var g = groups[btn.toggleGroup];
12004            for(var i = 0, l = g.length; i < l; i++){
12005                if(g[i] != btn){
12006                    g[i].toggle(false);
12007                }
12008            }
12009        }
12010    }
12011    
12012    return {
12013        register : function(btn){
12014            if(!btn.toggleGroup){
12015                return;
12016            }
12017            var g = groups[btn.toggleGroup];
12018            if(!g){
12019                g = groups[btn.toggleGroup] = [];
12020            }
12021            g.push(btn);
12022            btn.on("toggle", toggleGroup);
12023        },
12024        
12025        unregister : function(btn){
12026            if(!btn.toggleGroup){
12027                return;
12028            }
12029            var g = groups[btn.toggleGroup];
12030            if(g){
12031                g.remove(btn);
12032                btn.un("toggle", toggleGroup);
12033            }
12034        }
12035    };
12036 }();/*
12037  * Based on:
12038  * Ext JS Library 1.1.1
12039  * Copyright(c) 2006-2007, Ext JS, LLC.
12040  *
12041  * Originally Released Under LGPL - original licence link has changed is not relivant.
12042  *
12043  * Fork - LGPL
12044  * <script type="text/javascript">
12045  */
12046  
12047 /**
12048  * @class Roo.SplitButton
12049  * @extends Roo.Button
12050  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12051  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12052  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12053  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12054  * @cfg {String} arrowTooltip The title attribute of the arrow
12055  * @constructor
12056  * Create a new menu button
12057  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12058  * @param {Object} config The config object
12059  */
12060 Roo.SplitButton = function(renderTo, config){
12061     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12062     /**
12063      * @event arrowclick
12064      * Fires when this button's arrow is clicked
12065      * @param {SplitButton} this
12066      * @param {EventObject} e The click event
12067      */
12068     this.addEvents({"arrowclick":true});
12069 };
12070
12071 Roo.extend(Roo.SplitButton, Roo.Button, {
12072     render : function(renderTo){
12073         // this is one sweet looking template!
12074         var tpl = new Roo.Template(
12075             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12076             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12077             '<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>',
12078             "</tbody></table></td><td>",
12079             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12080             '<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>',
12081             "</tbody></table></td></tr></table>"
12082         );
12083         var btn = tpl.append(renderTo, [this.text, this.type], true);
12084         var btnEl = btn.child("button");
12085         if(this.cls){
12086             btn.addClass(this.cls);
12087         }
12088         if(this.icon){
12089             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12090         }
12091         if(this.iconCls){
12092             btnEl.addClass(this.iconCls);
12093             if(!this.cls){
12094                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12095             }
12096         }
12097         this.el = btn;
12098         if(this.handleMouseEvents){
12099             btn.on("mouseover", this.onMouseOver, this);
12100             btn.on("mouseout", this.onMouseOut, this);
12101             btn.on("mousedown", this.onMouseDown, this);
12102             btn.on("mouseup", this.onMouseUp, this);
12103         }
12104         btn.on(this.clickEvent, this.onClick, this);
12105         if(this.tooltip){
12106             if(typeof this.tooltip == 'object'){
12107                 Roo.QuickTips.tips(Roo.apply({
12108                       target: btnEl.id
12109                 }, this.tooltip));
12110             } else {
12111                 btnEl.dom[this.tooltipType] = this.tooltip;
12112             }
12113         }
12114         if(this.arrowTooltip){
12115             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12116         }
12117         if(this.hidden){
12118             this.hide();
12119         }
12120         if(this.disabled){
12121             this.disable();
12122         }
12123         if(this.pressed){
12124             this.el.addClass("x-btn-pressed");
12125         }
12126         if(Roo.isIE && !Roo.isIE7){
12127             this.autoWidth.defer(1, this);
12128         }else{
12129             this.autoWidth();
12130         }
12131         if(this.menu){
12132             this.menu.on("show", this.onMenuShow, this);
12133             this.menu.on("hide", this.onMenuHide, this);
12134         }
12135         this.fireEvent('render', this);
12136     },
12137
12138     // private
12139     autoWidth : function(){
12140         if(this.el){
12141             var tbl = this.el.child("table:first");
12142             var tbl2 = this.el.child("table:last");
12143             this.el.setWidth("auto");
12144             tbl.setWidth("auto");
12145             if(Roo.isIE7 && Roo.isStrict){
12146                 var ib = this.el.child('button:first');
12147                 if(ib && ib.getWidth() > 20){
12148                     ib.clip();
12149                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12150                 }
12151             }
12152             if(this.minWidth){
12153                 if(this.hidden){
12154                     this.el.beginMeasure();
12155                 }
12156                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12157                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12158                 }
12159                 if(this.hidden){
12160                     this.el.endMeasure();
12161                 }
12162             }
12163             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12164         } 
12165     },
12166     /**
12167      * Sets this button's click handler
12168      * @param {Function} handler The function to call when the button is clicked
12169      * @param {Object} scope (optional) Scope for the function passed above
12170      */
12171     setHandler : function(handler, scope){
12172         this.handler = handler;
12173         this.scope = scope;  
12174     },
12175     
12176     /**
12177      * Sets this button's arrow click handler
12178      * @param {Function} handler The function to call when the arrow is clicked
12179      * @param {Object} scope (optional) Scope for the function passed above
12180      */
12181     setArrowHandler : function(handler, scope){
12182         this.arrowHandler = handler;
12183         this.scope = scope;  
12184     },
12185     
12186     /**
12187      * Focus the button
12188      */
12189     focus : function(){
12190         if(this.el){
12191             this.el.child("button:first").focus();
12192         }
12193     },
12194
12195     // private
12196     onClick : function(e){
12197         e.preventDefault();
12198         if(!this.disabled){
12199             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12200                 if(this.menu && !this.menu.isVisible()){
12201                     this.menu.show(this.el, this.menuAlign);
12202                 }
12203                 this.fireEvent("arrowclick", this, e);
12204                 if(this.arrowHandler){
12205                     this.arrowHandler.call(this.scope || this, this, e);
12206                 }
12207             }else{
12208                 this.fireEvent("click", this, e);
12209                 if(this.handler){
12210                     this.handler.call(this.scope || this, this, e);
12211                 }
12212             }
12213         }
12214     },
12215     // private
12216     onMouseDown : function(e){
12217         if(!this.disabled){
12218             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12219         }
12220     },
12221     // private
12222     onMouseUp : function(e){
12223         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12224     }   
12225 });
12226
12227
12228 // backwards compat
12229 Roo.MenuButton = Roo.SplitButton;/*
12230  * Based on:
12231  * Ext JS Library 1.1.1
12232  * Copyright(c) 2006-2007, Ext JS, LLC.
12233  *
12234  * Originally Released Under LGPL - original licence link has changed is not relivant.
12235  *
12236  * Fork - LGPL
12237  * <script type="text/javascript">
12238  */
12239
12240 /**
12241  * @class Roo.Toolbar
12242  * Basic Toolbar class.
12243  * @constructor
12244  * Creates a new Toolbar
12245  * @param {Object} config The config object
12246  */ 
12247 Roo.Toolbar = function(container, buttons, config)
12248 {
12249     /// old consturctor format still supported..
12250     if(container instanceof Array){ // omit the container for later rendering
12251         buttons = container;
12252         config = buttons;
12253         container = null;
12254     }
12255     if (typeof(container) == 'object' && container.xtype) {
12256         config = container;
12257         container = config.container;
12258         buttons = config.buttons; // not really - use items!!
12259     }
12260     var xitems = [];
12261     if (config && config.items) {
12262         xitems = config.items;
12263         delete config.items;
12264     }
12265     Roo.apply(this, config);
12266     this.buttons = buttons;
12267     
12268     if(container){
12269         this.render(container);
12270     }
12271     Roo.each(xitems, function(b) {
12272         this.add(b);
12273     }, this);
12274     
12275 };
12276
12277 Roo.Toolbar.prototype = {
12278     /**
12279      * @cfg {Roo.data.Store} items
12280      * array of button configs or elements to add
12281      */
12282     
12283     /**
12284      * @cfg {String/HTMLElement/Element} container
12285      * The id or element that will contain the toolbar
12286      */
12287     // private
12288     render : function(ct){
12289         this.el = Roo.get(ct);
12290         if(this.cls){
12291             this.el.addClass(this.cls);
12292         }
12293         // using a table allows for vertical alignment
12294         // 100% width is needed by Safari...
12295         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12296         this.tr = this.el.child("tr", true);
12297         var autoId = 0;
12298         this.items = new Roo.util.MixedCollection(false, function(o){
12299             return o.id || ("item" + (++autoId));
12300         });
12301         if(this.buttons){
12302             this.add.apply(this, this.buttons);
12303             delete this.buttons;
12304         }
12305     },
12306
12307     /**
12308      * Adds element(s) to the toolbar -- this function takes a variable number of 
12309      * arguments of mixed type and adds them to the toolbar.
12310      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12311      * <ul>
12312      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12313      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12314      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12315      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12316      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12317      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12318      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12319      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12320      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12321      * </ul>
12322      * @param {Mixed} arg2
12323      * @param {Mixed} etc.
12324      */
12325     add : function(){
12326         var a = arguments, l = a.length;
12327         for(var i = 0; i < l; i++){
12328             this._add(a[i]);
12329         }
12330     },
12331     // private..
12332     _add : function(el) {
12333         
12334         if (el.xtype) {
12335             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12336         }
12337         
12338         if (el.applyTo){ // some kind of form field
12339             return this.addField(el);
12340         } 
12341         if (el.render){ // some kind of Toolbar.Item
12342             return this.addItem(el);
12343         }
12344         if (typeof el == "string"){ // string
12345             if(el == "separator" || el == "-"){
12346                 return this.addSeparator();
12347             }
12348             if (el == " "){
12349                 return this.addSpacer();
12350             }
12351             if(el == "->"){
12352                 return this.addFill();
12353             }
12354             return this.addText(el);
12355             
12356         }
12357         if(el.tagName){ // element
12358             return this.addElement(el);
12359         }
12360         if(typeof el == "object"){ // must be button config?
12361             return this.addButton(el);
12362         }
12363         // and now what?!?!
12364         return false;
12365         
12366     },
12367     
12368     /**
12369      * Add an Xtype element
12370      * @param {Object} xtype Xtype Object
12371      * @return {Object} created Object
12372      */
12373     addxtype : function(e){
12374         return this.add(e);  
12375     },
12376     
12377     /**
12378      * Returns the Element for this toolbar.
12379      * @return {Roo.Element}
12380      */
12381     getEl : function(){
12382         return this.el;  
12383     },
12384     
12385     /**
12386      * Adds a separator
12387      * @return {Roo.Toolbar.Item} The separator item
12388      */
12389     addSeparator : function(){
12390         return this.addItem(new Roo.Toolbar.Separator());
12391     },
12392
12393     /**
12394      * Adds a spacer element
12395      * @return {Roo.Toolbar.Spacer} The spacer item
12396      */
12397     addSpacer : function(){
12398         return this.addItem(new Roo.Toolbar.Spacer());
12399     },
12400
12401     /**
12402      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12403      * @return {Roo.Toolbar.Fill} The fill item
12404      */
12405     addFill : function(){
12406         return this.addItem(new Roo.Toolbar.Fill());
12407     },
12408
12409     /**
12410      * Adds any standard HTML element to the toolbar
12411      * @param {String/HTMLElement/Element} el The element or id of the element to add
12412      * @return {Roo.Toolbar.Item} The element's item
12413      */
12414     addElement : function(el){
12415         return this.addItem(new Roo.Toolbar.Item(el));
12416     },
12417     /**
12418      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12419      * @type Roo.util.MixedCollection  
12420      */
12421     items : false,
12422      
12423     /**
12424      * Adds any Toolbar.Item or subclass
12425      * @param {Roo.Toolbar.Item} item
12426      * @return {Roo.Toolbar.Item} The item
12427      */
12428     addItem : function(item){
12429         var td = this.nextBlock();
12430         item.render(td);
12431         this.items.add(item);
12432         return item;
12433     },
12434     
12435     /**
12436      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12437      * @param {Object/Array} config A button config or array of configs
12438      * @return {Roo.Toolbar.Button/Array}
12439      */
12440     addButton : function(config){
12441         if(config instanceof Array){
12442             var buttons = [];
12443             for(var i = 0, len = config.length; i < len; i++) {
12444                 buttons.push(this.addButton(config[i]));
12445             }
12446             return buttons;
12447         }
12448         var b = config;
12449         if(!(config instanceof Roo.Toolbar.Button)){
12450             b = config.split ?
12451                 new Roo.Toolbar.SplitButton(config) :
12452                 new Roo.Toolbar.Button(config);
12453         }
12454         var td = this.nextBlock();
12455         b.render(td);
12456         this.items.add(b);
12457         return b;
12458     },
12459     
12460     /**
12461      * Adds text to the toolbar
12462      * @param {String} text The text to add
12463      * @return {Roo.Toolbar.Item} The element's item
12464      */
12465     addText : function(text){
12466         return this.addItem(new Roo.Toolbar.TextItem(text));
12467     },
12468     
12469     /**
12470      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12471      * @param {Number} index The index where the item is to be inserted
12472      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12473      * @return {Roo.Toolbar.Button/Item}
12474      */
12475     insertButton : function(index, item){
12476         if(item instanceof Array){
12477             var buttons = [];
12478             for(var i = 0, len = item.length; i < len; i++) {
12479                buttons.push(this.insertButton(index + i, item[i]));
12480             }
12481             return buttons;
12482         }
12483         if (!(item instanceof Roo.Toolbar.Button)){
12484            item = new Roo.Toolbar.Button(item);
12485         }
12486         var td = document.createElement("td");
12487         this.tr.insertBefore(td, this.tr.childNodes[index]);
12488         item.render(td);
12489         this.items.insert(index, item);
12490         return item;
12491     },
12492     
12493     /**
12494      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12495      * @param {Object} config
12496      * @return {Roo.Toolbar.Item} The element's item
12497      */
12498     addDom : function(config, returnEl){
12499         var td = this.nextBlock();
12500         Roo.DomHelper.overwrite(td, config);
12501         var ti = new Roo.Toolbar.Item(td.firstChild);
12502         ti.render(td);
12503         this.items.add(ti);
12504         return ti;
12505     },
12506
12507     /**
12508      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12509      * @type Roo.util.MixedCollection  
12510      */
12511     fields : false,
12512     
12513     /**
12514      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12515      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12516      * @param {Roo.form.Field} field
12517      * @return {Roo.ToolbarItem}
12518      */
12519      
12520       
12521     addField : function(field) {
12522         if (!this.fields) {
12523             var autoId = 0;
12524             this.fields = new Roo.util.MixedCollection(false, function(o){
12525                 return o.id || ("item" + (++autoId));
12526             });
12527
12528         }
12529         
12530         var td = this.nextBlock();
12531         field.render(td);
12532         var ti = new Roo.Toolbar.Item(td.firstChild);
12533         ti.render(td);
12534         this.items.add(ti);
12535         this.fields.add(field);
12536         return ti;
12537     },
12538     /**
12539      * Hide the toolbar
12540      * @method hide
12541      */
12542      
12543       
12544     hide : function()
12545     {
12546         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12547         this.el.child('div').hide();
12548     },
12549     /**
12550      * Show the toolbar
12551      * @method show
12552      */
12553     show : function()
12554     {
12555         this.el.child('div').show();
12556     },
12557       
12558     // private
12559     nextBlock : function(){
12560         var td = document.createElement("td");
12561         this.tr.appendChild(td);
12562         return td;
12563     },
12564
12565     // private
12566     destroy : function(){
12567         if(this.items){ // rendered?
12568             Roo.destroy.apply(Roo, this.items.items);
12569         }
12570         if(this.fields){ // rendered?
12571             Roo.destroy.apply(Roo, this.fields.items);
12572         }
12573         Roo.Element.uncache(this.el, this.tr);
12574     }
12575 };
12576
12577 /**
12578  * @class Roo.Toolbar.Item
12579  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12580  * @constructor
12581  * Creates a new Item
12582  * @param {HTMLElement} el 
12583  */
12584 Roo.Toolbar.Item = function(el){
12585     this.el = Roo.getDom(el);
12586     this.id = Roo.id(this.el);
12587     this.hidden = false;
12588 };
12589
12590 Roo.Toolbar.Item.prototype = {
12591     
12592     /**
12593      * Get this item's HTML Element
12594      * @return {HTMLElement}
12595      */
12596     getEl : function(){
12597        return this.el;  
12598     },
12599
12600     // private
12601     render : function(td){
12602         this.td = td;
12603         td.appendChild(this.el);
12604     },
12605     
12606     /**
12607      * Removes and destroys this item.
12608      */
12609     destroy : function(){
12610         this.td.parentNode.removeChild(this.td);
12611     },
12612     
12613     /**
12614      * Shows this item.
12615      */
12616     show: function(){
12617         this.hidden = false;
12618         this.td.style.display = "";
12619     },
12620     
12621     /**
12622      * Hides this item.
12623      */
12624     hide: function(){
12625         this.hidden = true;
12626         this.td.style.display = "none";
12627     },
12628     
12629     /**
12630      * Convenience function for boolean show/hide.
12631      * @param {Boolean} visible true to show/false to hide
12632      */
12633     setVisible: function(visible){
12634         if(visible) {
12635             this.show();
12636         }else{
12637             this.hide();
12638         }
12639     },
12640     
12641     /**
12642      * Try to focus this item.
12643      */
12644     focus : function(){
12645         Roo.fly(this.el).focus();
12646     },
12647     
12648     /**
12649      * Disables this item.
12650      */
12651     disable : function(){
12652         Roo.fly(this.td).addClass("x-item-disabled");
12653         this.disabled = true;
12654         this.el.disabled = true;
12655     },
12656     
12657     /**
12658      * Enables this item.
12659      */
12660     enable : function(){
12661         Roo.fly(this.td).removeClass("x-item-disabled");
12662         this.disabled = false;
12663         this.el.disabled = false;
12664     }
12665 };
12666
12667
12668 /**
12669  * @class Roo.Toolbar.Separator
12670  * @extends Roo.Toolbar.Item
12671  * A simple toolbar separator class
12672  * @constructor
12673  * Creates a new Separator
12674  */
12675 Roo.Toolbar.Separator = function(){
12676     var s = document.createElement("span");
12677     s.className = "ytb-sep";
12678     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12679 };
12680 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12681     enable:Roo.emptyFn,
12682     disable:Roo.emptyFn,
12683     focus:Roo.emptyFn
12684 });
12685
12686 /**
12687  * @class Roo.Toolbar.Spacer
12688  * @extends Roo.Toolbar.Item
12689  * A simple element that adds extra horizontal space to a toolbar.
12690  * @constructor
12691  * Creates a new Spacer
12692  */
12693 Roo.Toolbar.Spacer = function(){
12694     var s = document.createElement("div");
12695     s.className = "ytb-spacer";
12696     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12697 };
12698 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12699     enable:Roo.emptyFn,
12700     disable:Roo.emptyFn,
12701     focus:Roo.emptyFn
12702 });
12703
12704 /**
12705  * @class Roo.Toolbar.Fill
12706  * @extends Roo.Toolbar.Spacer
12707  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12708  * @constructor
12709  * Creates a new Spacer
12710  */
12711 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12712     // private
12713     render : function(td){
12714         td.style.width = '100%';
12715         Roo.Toolbar.Fill.superclass.render.call(this, td);
12716     }
12717 });
12718
12719 /**
12720  * @class Roo.Toolbar.TextItem
12721  * @extends Roo.Toolbar.Item
12722  * A simple class that renders text directly into a toolbar.
12723  * @constructor
12724  * Creates a new TextItem
12725  * @param {String} text
12726  */
12727 Roo.Toolbar.TextItem = function(text){
12728     if (typeof(text) == 'object') {
12729         text = text.text;
12730     }
12731     var s = document.createElement("span");
12732     s.className = "ytb-text";
12733     s.innerHTML = text;
12734     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12735 };
12736 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12737     enable:Roo.emptyFn,
12738     disable:Roo.emptyFn,
12739     focus:Roo.emptyFn
12740 });
12741
12742 /**
12743  * @class Roo.Toolbar.Button
12744  * @extends Roo.Button
12745  * A button that renders into a toolbar.
12746  * @constructor
12747  * Creates a new Button
12748  * @param {Object} config A standard {@link Roo.Button} config object
12749  */
12750 Roo.Toolbar.Button = function(config){
12751     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12752 };
12753 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12754     render : function(td){
12755         this.td = td;
12756         Roo.Toolbar.Button.superclass.render.call(this, td);
12757     },
12758     
12759     /**
12760      * Removes and destroys this button
12761      */
12762     destroy : function(){
12763         Roo.Toolbar.Button.superclass.destroy.call(this);
12764         this.td.parentNode.removeChild(this.td);
12765     },
12766     
12767     /**
12768      * Shows this button
12769      */
12770     show: function(){
12771         this.hidden = false;
12772         this.td.style.display = "";
12773     },
12774     
12775     /**
12776      * Hides this button
12777      */
12778     hide: function(){
12779         this.hidden = true;
12780         this.td.style.display = "none";
12781     },
12782
12783     /**
12784      * Disables this item
12785      */
12786     disable : function(){
12787         Roo.fly(this.td).addClass("x-item-disabled");
12788         this.disabled = true;
12789     },
12790
12791     /**
12792      * Enables this item
12793      */
12794     enable : function(){
12795         Roo.fly(this.td).removeClass("x-item-disabled");
12796         this.disabled = false;
12797     }
12798 });
12799 // backwards compat
12800 Roo.ToolbarButton = Roo.Toolbar.Button;
12801
12802 /**
12803  * @class Roo.Toolbar.SplitButton
12804  * @extends Roo.SplitButton
12805  * A menu button that renders into a toolbar.
12806  * @constructor
12807  * Creates a new SplitButton
12808  * @param {Object} config A standard {@link Roo.SplitButton} config object
12809  */
12810 Roo.Toolbar.SplitButton = function(config){
12811     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12812 };
12813 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12814     render : function(td){
12815         this.td = td;
12816         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12817     },
12818     
12819     /**
12820      * Removes and destroys this button
12821      */
12822     destroy : function(){
12823         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12824         this.td.parentNode.removeChild(this.td);
12825     },
12826     
12827     /**
12828      * Shows this button
12829      */
12830     show: function(){
12831         this.hidden = false;
12832         this.td.style.display = "";
12833     },
12834     
12835     /**
12836      * Hides this button
12837      */
12838     hide: function(){
12839         this.hidden = true;
12840         this.td.style.display = "none";
12841     }
12842 });
12843
12844 // backwards compat
12845 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12846  * Based on:
12847  * Ext JS Library 1.1.1
12848  * Copyright(c) 2006-2007, Ext JS, LLC.
12849  *
12850  * Originally Released Under LGPL - original licence link has changed is not relivant.
12851  *
12852  * Fork - LGPL
12853  * <script type="text/javascript">
12854  */
12855  
12856 /**
12857  * @class Roo.PagingToolbar
12858  * @extends Roo.Toolbar
12859  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12860  * @constructor
12861  * Create a new PagingToolbar
12862  * @param {Object} config The config object
12863  */
12864 Roo.PagingToolbar = function(el, ds, config)
12865 {
12866     // old args format still supported... - xtype is prefered..
12867     if (typeof(el) == 'object' && el.xtype) {
12868         // created from xtype...
12869         config = el;
12870         ds = el.dataSource;
12871         el = config.container;
12872     }
12873     var items = [];
12874     if (config.items) {
12875         items = config.items;
12876         config.items = [];
12877     }
12878     
12879     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12880     this.ds = ds;
12881     this.cursor = 0;
12882     this.renderButtons(this.el);
12883     this.bind(ds);
12884     
12885     // supprot items array.
12886    
12887     Roo.each(items, function(e) {
12888         this.add(Roo.factory(e));
12889     },this);
12890     
12891 };
12892
12893 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12894     /**
12895      * @cfg {Roo.data.Store} dataSource
12896      * The underlying data store providing the paged data
12897      */
12898     /**
12899      * @cfg {String/HTMLElement/Element} container
12900      * container The id or element that will contain the toolbar
12901      */
12902     /**
12903      * @cfg {Boolean} displayInfo
12904      * True to display the displayMsg (defaults to false)
12905      */
12906     /**
12907      * @cfg {Number} pageSize
12908      * The number of records to display per page (defaults to 20)
12909      */
12910     pageSize: 20,
12911     /**
12912      * @cfg {String} displayMsg
12913      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12914      */
12915     displayMsg : 'Displaying {0} - {1} of {2}',
12916     /**
12917      * @cfg {String} emptyMsg
12918      * The message to display when no records are found (defaults to "No data to display")
12919      */
12920     emptyMsg : 'No data to display',
12921     /**
12922      * Customizable piece of the default paging text (defaults to "Page")
12923      * @type String
12924      */
12925     beforePageText : "Page",
12926     /**
12927      * Customizable piece of the default paging text (defaults to "of %0")
12928      * @type String
12929      */
12930     afterPageText : "of {0}",
12931     /**
12932      * Customizable piece of the default paging text (defaults to "First Page")
12933      * @type String
12934      */
12935     firstText : "First Page",
12936     /**
12937      * Customizable piece of the default paging text (defaults to "Previous Page")
12938      * @type String
12939      */
12940     prevText : "Previous Page",
12941     /**
12942      * Customizable piece of the default paging text (defaults to "Next Page")
12943      * @type String
12944      */
12945     nextText : "Next Page",
12946     /**
12947      * Customizable piece of the default paging text (defaults to "Last Page")
12948      * @type String
12949      */
12950     lastText : "Last Page",
12951     /**
12952      * Customizable piece of the default paging text (defaults to "Refresh")
12953      * @type String
12954      */
12955     refreshText : "Refresh",
12956
12957     // private
12958     renderButtons : function(el){
12959         Roo.PagingToolbar.superclass.render.call(this, el);
12960         this.first = this.addButton({
12961             tooltip: this.firstText,
12962             cls: "x-btn-icon x-grid-page-first",
12963             disabled: true,
12964             handler: this.onClick.createDelegate(this, ["first"])
12965         });
12966         this.prev = this.addButton({
12967             tooltip: this.prevText,
12968             cls: "x-btn-icon x-grid-page-prev",
12969             disabled: true,
12970             handler: this.onClick.createDelegate(this, ["prev"])
12971         });
12972         //this.addSeparator();
12973         this.add(this.beforePageText);
12974         this.field = Roo.get(this.addDom({
12975            tag: "input",
12976            type: "text",
12977            size: "3",
12978            value: "1",
12979            cls: "x-grid-page-number"
12980         }).el);
12981         this.field.on("keydown", this.onPagingKeydown, this);
12982         this.field.on("focus", function(){this.dom.select();});
12983         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12984         this.field.setHeight(18);
12985         //this.addSeparator();
12986         this.next = this.addButton({
12987             tooltip: this.nextText,
12988             cls: "x-btn-icon x-grid-page-next",
12989             disabled: true,
12990             handler: this.onClick.createDelegate(this, ["next"])
12991         });
12992         this.last = this.addButton({
12993             tooltip: this.lastText,
12994             cls: "x-btn-icon x-grid-page-last",
12995             disabled: true,
12996             handler: this.onClick.createDelegate(this, ["last"])
12997         });
12998         //this.addSeparator();
12999         this.loading = this.addButton({
13000             tooltip: this.refreshText,
13001             cls: "x-btn-icon x-grid-loading",
13002             handler: this.onClick.createDelegate(this, ["refresh"])
13003         });
13004
13005         if(this.displayInfo){
13006             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13007         }
13008     },
13009
13010     // private
13011     updateInfo : function(){
13012         if(this.displayEl){
13013             var count = this.ds.getCount();
13014             var msg = count == 0 ?
13015                 this.emptyMsg :
13016                 String.format(
13017                     this.displayMsg,
13018                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13019                 );
13020             this.displayEl.update(msg);
13021         }
13022     },
13023
13024     // private
13025     onLoad : function(ds, r, o){
13026        this.cursor = o.params ? o.params.start : 0;
13027        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13028
13029        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13030        this.field.dom.value = ap;
13031        this.first.setDisabled(ap == 1);
13032        this.prev.setDisabled(ap == 1);
13033        this.next.setDisabled(ap == ps);
13034        this.last.setDisabled(ap == ps);
13035        this.loading.enable();
13036        this.updateInfo();
13037     },
13038
13039     // private
13040     getPageData : function(){
13041         var total = this.ds.getTotalCount();
13042         return {
13043             total : total,
13044             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13045             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13046         };
13047     },
13048
13049     // private
13050     onLoadError : function(){
13051         this.loading.enable();
13052     },
13053
13054     // private
13055     onPagingKeydown : function(e){
13056         var k = e.getKey();
13057         var d = this.getPageData();
13058         if(k == e.RETURN){
13059             var v = this.field.dom.value, pageNum;
13060             if(!v || isNaN(pageNum = parseInt(v, 10))){
13061                 this.field.dom.value = d.activePage;
13062                 return;
13063             }
13064             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13065             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13066             e.stopEvent();
13067         }
13068         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))
13069         {
13070           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13071           this.field.dom.value = pageNum;
13072           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13073           e.stopEvent();
13074         }
13075         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13076         {
13077           var v = this.field.dom.value, pageNum; 
13078           var increment = (e.shiftKey) ? 10 : 1;
13079           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13080             increment *= -1;
13081           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13082             this.field.dom.value = d.activePage;
13083             return;
13084           }
13085           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13086           {
13087             this.field.dom.value = parseInt(v, 10) + increment;
13088             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13089             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13090           }
13091           e.stopEvent();
13092         }
13093     },
13094
13095     // private
13096     beforeLoad : function(){
13097         if(this.loading){
13098             this.loading.disable();
13099         }
13100     },
13101
13102     // private
13103     onClick : function(which){
13104         var ds = this.ds;
13105         switch(which){
13106             case "first":
13107                 ds.load({params:{start: 0, limit: this.pageSize}});
13108             break;
13109             case "prev":
13110                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13111             break;
13112             case "next":
13113                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13114             break;
13115             case "last":
13116                 var total = ds.getTotalCount();
13117                 var extra = total % this.pageSize;
13118                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13119                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13120             break;
13121             case "refresh":
13122                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13123             break;
13124         }
13125     },
13126
13127     /**
13128      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13129      * @param {Roo.data.Store} store The data store to unbind
13130      */
13131     unbind : function(ds){
13132         ds.un("beforeload", this.beforeLoad, this);
13133         ds.un("load", this.onLoad, this);
13134         ds.un("loadexception", this.onLoadError, this);
13135         ds.un("remove", this.updateInfo, this);
13136         ds.un("add", this.updateInfo, this);
13137         this.ds = undefined;
13138     },
13139
13140     /**
13141      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13142      * @param {Roo.data.Store} store The data store to bind
13143      */
13144     bind : function(ds){
13145         ds.on("beforeload", this.beforeLoad, this);
13146         ds.on("load", this.onLoad, this);
13147         ds.on("loadexception", this.onLoadError, this);
13148         ds.on("remove", this.updateInfo, this);
13149         ds.on("add", this.updateInfo, this);
13150         this.ds = ds;
13151     }
13152 });/*
13153  * Based on:
13154  * Ext JS Library 1.1.1
13155  * Copyright(c) 2006-2007, Ext JS, LLC.
13156  *
13157  * Originally Released Under LGPL - original licence link has changed is not relivant.
13158  *
13159  * Fork - LGPL
13160  * <script type="text/javascript">
13161  */
13162
13163 /**
13164  * @class Roo.Resizable
13165  * @extends Roo.util.Observable
13166  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13167  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13168  * 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
13169  * the element will be wrapped for you automatically.</p>
13170  * <p>Here is the list of valid resize handles:</p>
13171  * <pre>
13172 Value   Description
13173 ------  -------------------
13174  'n'     north
13175  's'     south
13176  'e'     east
13177  'w'     west
13178  'nw'    northwest
13179  'sw'    southwest
13180  'se'    southeast
13181  'ne'    northeast
13182  'hd'    horizontal drag
13183  'all'   all
13184 </pre>
13185  * <p>Here's an example showing the creation of a typical Resizable:</p>
13186  * <pre><code>
13187 var resizer = new Roo.Resizable("element-id", {
13188     handles: 'all',
13189     minWidth: 200,
13190     minHeight: 100,
13191     maxWidth: 500,
13192     maxHeight: 400,
13193     pinned: true
13194 });
13195 resizer.on("resize", myHandler);
13196 </code></pre>
13197  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13198  * resizer.east.setDisplayed(false);</p>
13199  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13200  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13201  * resize operation's new size (defaults to [0, 0])
13202  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13203  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13204  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13205  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13206  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13207  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13208  * @cfg {Number} width The width of the element in pixels (defaults to null)
13209  * @cfg {Number} height The height of the element in pixels (defaults to null)
13210  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13211  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13212  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13213  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13214  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13215  * in favor of the handles config option (defaults to false)
13216  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13217  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13218  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13219  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13220  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13221  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13222  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13223  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13224  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13225  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13226  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13227  * @constructor
13228  * Create a new resizable component
13229  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13230  * @param {Object} config configuration options
13231   */
13232 Roo.Resizable = function(el, config)
13233 {
13234     this.el = Roo.get(el);
13235
13236     if(config && config.wrap){
13237         config.resizeChild = this.el;
13238         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13239         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13240         this.el.setStyle("overflow", "hidden");
13241         this.el.setPositioning(config.resizeChild.getPositioning());
13242         config.resizeChild.clearPositioning();
13243         if(!config.width || !config.height){
13244             var csize = config.resizeChild.getSize();
13245             this.el.setSize(csize.width, csize.height);
13246         }
13247         if(config.pinned && !config.adjustments){
13248             config.adjustments = "auto";
13249         }
13250     }
13251
13252     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13253     this.proxy.unselectable();
13254     this.proxy.enableDisplayMode('block');
13255
13256     Roo.apply(this, config);
13257
13258     if(this.pinned){
13259         this.disableTrackOver = true;
13260         this.el.addClass("x-resizable-pinned");
13261     }
13262     // if the element isn't positioned, make it relative
13263     var position = this.el.getStyle("position");
13264     if(position != "absolute" && position != "fixed"){
13265         this.el.setStyle("position", "relative");
13266     }
13267     if(!this.handles){ // no handles passed, must be legacy style
13268         this.handles = 's,e,se';
13269         if(this.multiDirectional){
13270             this.handles += ',n,w';
13271         }
13272     }
13273     if(this.handles == "all"){
13274         this.handles = "n s e w ne nw se sw";
13275     }
13276     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13277     var ps = Roo.Resizable.positions;
13278     for(var i = 0, len = hs.length; i < len; i++){
13279         if(hs[i] && ps[hs[i]]){
13280             var pos = ps[hs[i]];
13281             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13282         }
13283     }
13284     // legacy
13285     this.corner = this.southeast;
13286     
13287     // updateBox = the box can move..
13288     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13289         this.updateBox = true;
13290     }
13291
13292     this.activeHandle = null;
13293
13294     if(this.resizeChild){
13295         if(typeof this.resizeChild == "boolean"){
13296             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13297         }else{
13298             this.resizeChild = Roo.get(this.resizeChild, true);
13299         }
13300     }
13301     
13302     if(this.adjustments == "auto"){
13303         var rc = this.resizeChild;
13304         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13305         if(rc && (hw || hn)){
13306             rc.position("relative");
13307             rc.setLeft(hw ? hw.el.getWidth() : 0);
13308             rc.setTop(hn ? hn.el.getHeight() : 0);
13309         }
13310         this.adjustments = [
13311             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13312             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13313         ];
13314     }
13315
13316     if(this.draggable){
13317         this.dd = this.dynamic ?
13318             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13319         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13320     }
13321
13322     // public events
13323     this.addEvents({
13324         /**
13325          * @event beforeresize
13326          * Fired before resize is allowed. Set enabled to false to cancel resize.
13327          * @param {Roo.Resizable} this
13328          * @param {Roo.EventObject} e The mousedown event
13329          */
13330         "beforeresize" : true,
13331         /**
13332          * @event resize
13333          * Fired after a resize.
13334          * @param {Roo.Resizable} this
13335          * @param {Number} width The new width
13336          * @param {Number} height The new height
13337          * @param {Roo.EventObject} e The mouseup event
13338          */
13339         "resize" : true
13340     });
13341
13342     if(this.width !== null && this.height !== null){
13343         this.resizeTo(this.width, this.height);
13344     }else{
13345         this.updateChildSize();
13346     }
13347     if(Roo.isIE){
13348         this.el.dom.style.zoom = 1;
13349     }
13350     Roo.Resizable.superclass.constructor.call(this);
13351 };
13352
13353 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13354         resizeChild : false,
13355         adjustments : [0, 0],
13356         minWidth : 5,
13357         minHeight : 5,
13358         maxWidth : 10000,
13359         maxHeight : 10000,
13360         enabled : true,
13361         animate : false,
13362         duration : .35,
13363         dynamic : false,
13364         handles : false,
13365         multiDirectional : false,
13366         disableTrackOver : false,
13367         easing : 'easeOutStrong',
13368         widthIncrement : 0,
13369         heightIncrement : 0,
13370         pinned : false,
13371         width : null,
13372         height : null,
13373         preserveRatio : false,
13374         transparent: false,
13375         minX: 0,
13376         minY: 0,
13377         draggable: false,
13378
13379         /**
13380          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13381          */
13382         constrainTo: undefined,
13383         /**
13384          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13385          */
13386         resizeRegion: undefined,
13387
13388
13389     /**
13390      * Perform a manual resize
13391      * @param {Number} width
13392      * @param {Number} height
13393      */
13394     resizeTo : function(width, height){
13395         this.el.setSize(width, height);
13396         this.updateChildSize();
13397         this.fireEvent("resize", this, width, height, null);
13398     },
13399
13400     // private
13401     startSizing : function(e, handle){
13402         this.fireEvent("beforeresize", this, e);
13403         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13404
13405             if(!this.overlay){
13406                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13407                 this.overlay.unselectable();
13408                 this.overlay.enableDisplayMode("block");
13409                 this.overlay.on("mousemove", this.onMouseMove, this);
13410                 this.overlay.on("mouseup", this.onMouseUp, this);
13411             }
13412             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13413
13414             this.resizing = true;
13415             this.startBox = this.el.getBox();
13416             this.startPoint = e.getXY();
13417             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13418                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13419
13420             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13421             this.overlay.show();
13422
13423             if(this.constrainTo) {
13424                 var ct = Roo.get(this.constrainTo);
13425                 this.resizeRegion = ct.getRegion().adjust(
13426                     ct.getFrameWidth('t'),
13427                     ct.getFrameWidth('l'),
13428                     -ct.getFrameWidth('b'),
13429                     -ct.getFrameWidth('r')
13430                 );
13431             }
13432
13433             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13434             this.proxy.show();
13435             this.proxy.setBox(this.startBox);
13436             if(!this.dynamic){
13437                 this.proxy.setStyle('visibility', 'visible');
13438             }
13439         }
13440     },
13441
13442     // private
13443     onMouseDown : function(handle, e){
13444         if(this.enabled){
13445             e.stopEvent();
13446             this.activeHandle = handle;
13447             this.startSizing(e, handle);
13448         }
13449     },
13450
13451     // private
13452     onMouseUp : function(e){
13453         var size = this.resizeElement();
13454         this.resizing = false;
13455         this.handleOut();
13456         this.overlay.hide();
13457         this.proxy.hide();
13458         this.fireEvent("resize", this, size.width, size.height, e);
13459     },
13460
13461     // private
13462     updateChildSize : function(){
13463         if(this.resizeChild){
13464             var el = this.el;
13465             var child = this.resizeChild;
13466             var adj = this.adjustments;
13467             if(el.dom.offsetWidth){
13468                 var b = el.getSize(true);
13469                 child.setSize(b.width+adj[0], b.height+adj[1]);
13470             }
13471             // Second call here for IE
13472             // The first call enables instant resizing and
13473             // the second call corrects scroll bars if they
13474             // exist
13475             if(Roo.isIE){
13476                 setTimeout(function(){
13477                     if(el.dom.offsetWidth){
13478                         var b = el.getSize(true);
13479                         child.setSize(b.width+adj[0], b.height+adj[1]);
13480                     }
13481                 }, 10);
13482             }
13483         }
13484     },
13485
13486     // private
13487     snap : function(value, inc, min){
13488         if(!inc || !value) return value;
13489         var newValue = value;
13490         var m = value % inc;
13491         if(m > 0){
13492             if(m > (inc/2)){
13493                 newValue = value + (inc-m);
13494             }else{
13495                 newValue = value - m;
13496             }
13497         }
13498         return Math.max(min, newValue);
13499     },
13500
13501     // private
13502     resizeElement : function(){
13503         var box = this.proxy.getBox();
13504         if(this.updateBox){
13505             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13506         }else{
13507             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13508         }
13509         this.updateChildSize();
13510         if(!this.dynamic){
13511             this.proxy.hide();
13512         }
13513         return box;
13514     },
13515
13516     // private
13517     constrain : function(v, diff, m, mx){
13518         if(v - diff < m){
13519             diff = v - m;
13520         }else if(v - diff > mx){
13521             diff = mx - v;
13522         }
13523         return diff;
13524     },
13525
13526     // private
13527     onMouseMove : function(e){
13528         if(this.enabled){
13529             try{// try catch so if something goes wrong the user doesn't get hung
13530
13531             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13532                 return;
13533             }
13534
13535             //var curXY = this.startPoint;
13536             var curSize = this.curSize || this.startBox;
13537             var x = this.startBox.x, y = this.startBox.y;
13538             var ox = x, oy = y;
13539             var w = curSize.width, h = curSize.height;
13540             var ow = w, oh = h;
13541             var mw = this.minWidth, mh = this.minHeight;
13542             var mxw = this.maxWidth, mxh = this.maxHeight;
13543             var wi = this.widthIncrement;
13544             var hi = this.heightIncrement;
13545
13546             var eventXY = e.getXY();
13547             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13548             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13549
13550             var pos = this.activeHandle.position;
13551
13552             switch(pos){
13553                 case "east":
13554                     w += diffX;
13555                     w = Math.min(Math.max(mw, w), mxw);
13556                     break;
13557              
13558                 case "south":
13559                     h += diffY;
13560                     h = Math.min(Math.max(mh, h), mxh);
13561                     break;
13562                 case "southeast":
13563                     w += diffX;
13564                     h += diffY;
13565                     w = Math.min(Math.max(mw, w), mxw);
13566                     h = Math.min(Math.max(mh, h), mxh);
13567                     break;
13568                 case "north":
13569                     diffY = this.constrain(h, diffY, mh, mxh);
13570                     y += diffY;
13571                     h -= diffY;
13572                     break;
13573                 case "hdrag":
13574                     
13575                     if (wi) {
13576                         var adiffX = Math.abs(diffX);
13577                         var sub = (adiffX % wi); // how much 
13578                         if (sub > (wi/2)) { // far enough to snap
13579                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13580                         } else {
13581                             // remove difference.. 
13582                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13583                         }
13584                     }
13585                     x += diffX;
13586                     x = Math.max(this.minX, x);
13587                     break;
13588                 case "west":
13589                     diffX = this.constrain(w, diffX, mw, mxw);
13590                     x += diffX;
13591                     w -= diffX;
13592                     break;
13593                 case "northeast":
13594                     w += diffX;
13595                     w = Math.min(Math.max(mw, w), mxw);
13596                     diffY = this.constrain(h, diffY, mh, mxh);
13597                     y += diffY;
13598                     h -= diffY;
13599                     break;
13600                 case "northwest":
13601                     diffX = this.constrain(w, diffX, mw, mxw);
13602                     diffY = this.constrain(h, diffY, mh, mxh);
13603                     y += diffY;
13604                     h -= diffY;
13605                     x += diffX;
13606                     w -= diffX;
13607                     break;
13608                case "southwest":
13609                     diffX = this.constrain(w, diffX, mw, mxw);
13610                     h += diffY;
13611                     h = Math.min(Math.max(mh, h), mxh);
13612                     x += diffX;
13613                     w -= diffX;
13614                     break;
13615             }
13616
13617             var sw = this.snap(w, wi, mw);
13618             var sh = this.snap(h, hi, mh);
13619             if(sw != w || sh != h){
13620                 switch(pos){
13621                     case "northeast":
13622                         y -= sh - h;
13623                     break;
13624                     case "north":
13625                         y -= sh - h;
13626                         break;
13627                     case "southwest":
13628                         x -= sw - w;
13629                     break;
13630                     case "west":
13631                         x -= sw - w;
13632                         break;
13633                     case "northwest":
13634                         x -= sw - w;
13635                         y -= sh - h;
13636                     break;
13637                 }
13638                 w = sw;
13639                 h = sh;
13640             }
13641
13642             if(this.preserveRatio){
13643                 switch(pos){
13644                     case "southeast":
13645                     case "east":
13646                         h = oh * (w/ow);
13647                         h = Math.min(Math.max(mh, h), mxh);
13648                         w = ow * (h/oh);
13649                        break;
13650                     case "south":
13651                         w = ow * (h/oh);
13652                         w = Math.min(Math.max(mw, w), mxw);
13653                         h = oh * (w/ow);
13654                         break;
13655                     case "northeast":
13656                         w = ow * (h/oh);
13657                         w = Math.min(Math.max(mw, w), mxw);
13658                         h = oh * (w/ow);
13659                     break;
13660                     case "north":
13661                         var tw = w;
13662                         w = ow * (h/oh);
13663                         w = Math.min(Math.max(mw, w), mxw);
13664                         h = oh * (w/ow);
13665                         x += (tw - w) / 2;
13666                         break;
13667                     case "southwest":
13668                         h = oh * (w/ow);
13669                         h = Math.min(Math.max(mh, h), mxh);
13670                         var tw = w;
13671                         w = ow * (h/oh);
13672                         x += tw - w;
13673                         break;
13674                     case "west":
13675                         var th = h;
13676                         h = oh * (w/ow);
13677                         h = Math.min(Math.max(mh, h), mxh);
13678                         y += (th - h) / 2;
13679                         var tw = w;
13680                         w = ow * (h/oh);
13681                         x += tw - w;
13682                        break;
13683                     case "northwest":
13684                         var tw = w;
13685                         var th = h;
13686                         h = oh * (w/ow);
13687                         h = Math.min(Math.max(mh, h), mxh);
13688                         w = ow * (h/oh);
13689                         y += th - h;
13690                         x += tw - w;
13691                        break;
13692
13693                 }
13694             }
13695             if (pos == 'hdrag') {
13696                 w = ow;
13697             }
13698             this.proxy.setBounds(x, y, w, h);
13699             if(this.dynamic){
13700                 this.resizeElement();
13701             }
13702             }catch(e){}
13703         }
13704     },
13705
13706     // private
13707     handleOver : function(){
13708         if(this.enabled){
13709             this.el.addClass("x-resizable-over");
13710         }
13711     },
13712
13713     // private
13714     handleOut : function(){
13715         if(!this.resizing){
13716             this.el.removeClass("x-resizable-over");
13717         }
13718     },
13719
13720     /**
13721      * Returns the element this component is bound to.
13722      * @return {Roo.Element}
13723      */
13724     getEl : function(){
13725         return this.el;
13726     },
13727
13728     /**
13729      * Returns the resizeChild element (or null).
13730      * @return {Roo.Element}
13731      */
13732     getResizeChild : function(){
13733         return this.resizeChild;
13734     },
13735
13736     /**
13737      * Destroys this resizable. If the element was wrapped and
13738      * removeEl is not true then the element remains.
13739      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13740      */
13741     destroy : function(removeEl){
13742         this.proxy.remove();
13743         if(this.overlay){
13744             this.overlay.removeAllListeners();
13745             this.overlay.remove();
13746         }
13747         var ps = Roo.Resizable.positions;
13748         for(var k in ps){
13749             if(typeof ps[k] != "function" && this[ps[k]]){
13750                 var h = this[ps[k]];
13751                 h.el.removeAllListeners();
13752                 h.el.remove();
13753             }
13754         }
13755         if(removeEl){
13756             this.el.update("");
13757             this.el.remove();
13758         }
13759     }
13760 });
13761
13762 // private
13763 // hash to map config positions to true positions
13764 Roo.Resizable.positions = {
13765     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13766     hd: "hdrag"
13767 };
13768
13769 // private
13770 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13771     if(!this.tpl){
13772         // only initialize the template if resizable is used
13773         var tpl = Roo.DomHelper.createTemplate(
13774             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13775         );
13776         tpl.compile();
13777         Roo.Resizable.Handle.prototype.tpl = tpl;
13778     }
13779     this.position = pos;
13780     this.rz = rz;
13781     // show north drag fro topdra
13782     var handlepos = pos == 'hdrag' ? 'north' : pos;
13783     
13784     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13785     if (pos == 'hdrag') {
13786         this.el.setStyle('cursor', 'pointer');
13787     }
13788     this.el.unselectable();
13789     if(transparent){
13790         this.el.setOpacity(0);
13791     }
13792     this.el.on("mousedown", this.onMouseDown, this);
13793     if(!disableTrackOver){
13794         this.el.on("mouseover", this.onMouseOver, this);
13795         this.el.on("mouseout", this.onMouseOut, this);
13796     }
13797 };
13798
13799 // private
13800 Roo.Resizable.Handle.prototype = {
13801     afterResize : function(rz){
13802         // do nothing
13803     },
13804     // private
13805     onMouseDown : function(e){
13806         this.rz.onMouseDown(this, e);
13807     },
13808     // private
13809     onMouseOver : function(e){
13810         this.rz.handleOver(this, e);
13811     },
13812     // private
13813     onMouseOut : function(e){
13814         this.rz.handleOut(this, e);
13815     }
13816 };/*
13817  * Based on:
13818  * Ext JS Library 1.1.1
13819  * Copyright(c) 2006-2007, Ext JS, LLC.
13820  *
13821  * Originally Released Under LGPL - original licence link has changed is not relivant.
13822  *
13823  * Fork - LGPL
13824  * <script type="text/javascript">
13825  */
13826
13827 /**
13828  * @class Roo.Editor
13829  * @extends Roo.Component
13830  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13831  * @constructor
13832  * Create a new Editor
13833  * @param {Roo.form.Field} field The Field object (or descendant)
13834  * @param {Object} config The config object
13835  */
13836 Roo.Editor = function(field, config){
13837     Roo.Editor.superclass.constructor.call(this, config);
13838     this.field = field;
13839     this.addEvents({
13840         /**
13841              * @event beforestartedit
13842              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13843              * false from the handler of this event.
13844              * @param {Editor} this
13845              * @param {Roo.Element} boundEl The underlying element bound to this editor
13846              * @param {Mixed} value The field value being set
13847              */
13848         "beforestartedit" : true,
13849         /**
13850              * @event startedit
13851              * Fires when this editor is displayed
13852              * @param {Roo.Element} boundEl The underlying element bound to this editor
13853              * @param {Mixed} value The starting field value
13854              */
13855         "startedit" : true,
13856         /**
13857              * @event beforecomplete
13858              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13859              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13860              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13861              * event will not fire since no edit actually occurred.
13862              * @param {Editor} this
13863              * @param {Mixed} value The current field value
13864              * @param {Mixed} startValue The original field value
13865              */
13866         "beforecomplete" : true,
13867         /**
13868              * @event complete
13869              * Fires after editing is complete and any changed value has been written to the underlying field.
13870              * @param {Editor} this
13871              * @param {Mixed} value The current field value
13872              * @param {Mixed} startValue The original field value
13873              */
13874         "complete" : true,
13875         /**
13876          * @event specialkey
13877          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13878          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13879          * @param {Roo.form.Field} this
13880          * @param {Roo.EventObject} e The event object
13881          */
13882         "specialkey" : true
13883     });
13884 };
13885
13886 Roo.extend(Roo.Editor, Roo.Component, {
13887     /**
13888      * @cfg {Boolean/String} autosize
13889      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13890      * or "height" to adopt the height only (defaults to false)
13891      */
13892     /**
13893      * @cfg {Boolean} revertInvalid
13894      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13895      * validation fails (defaults to true)
13896      */
13897     /**
13898      * @cfg {Boolean} ignoreNoChange
13899      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13900      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13901      * will never be ignored.
13902      */
13903     /**
13904      * @cfg {Boolean} hideEl
13905      * False to keep the bound element visible while the editor is displayed (defaults to true)
13906      */
13907     /**
13908      * @cfg {Mixed} value
13909      * The data value of the underlying field (defaults to "")
13910      */
13911     value : "",
13912     /**
13913      * @cfg {String} alignment
13914      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13915      */
13916     alignment: "c-c?",
13917     /**
13918      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13919      * for bottom-right shadow (defaults to "frame")
13920      */
13921     shadow : "frame",
13922     /**
13923      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13924      */
13925     constrain : false,
13926     /**
13927      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13928      */
13929     completeOnEnter : false,
13930     /**
13931      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13932      */
13933     cancelOnEsc : false,
13934     /**
13935      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13936      */
13937     updateEl : false,
13938
13939     // private
13940     onRender : function(ct, position){
13941         this.el = new Roo.Layer({
13942             shadow: this.shadow,
13943             cls: "x-editor",
13944             parentEl : ct,
13945             shim : this.shim,
13946             shadowOffset:4,
13947             id: this.id,
13948             constrain: this.constrain
13949         });
13950         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13951         if(this.field.msgTarget != 'title'){
13952             this.field.msgTarget = 'qtip';
13953         }
13954         this.field.render(this.el);
13955         if(Roo.isGecko){
13956             this.field.el.dom.setAttribute('autocomplete', 'off');
13957         }
13958         this.field.on("specialkey", this.onSpecialKey, this);
13959         if(this.swallowKeys){
13960             this.field.el.swallowEvent(['keydown','keypress']);
13961         }
13962         this.field.show();
13963         this.field.on("blur", this.onBlur, this);
13964         if(this.field.grow){
13965             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13966         }
13967     },
13968
13969     onSpecialKey : function(field, e)
13970     {
13971         //Roo.log('editor onSpecialKey');
13972         if(this.completeOnEnter && e.getKey() == e.ENTER){
13973             e.stopEvent();
13974             this.completeEdit();
13975             return;
13976         }
13977         // do not fire special key otherwise it might hide close the editor...
13978         if(e.getKey() == e.ENTER){    
13979             return;
13980         }
13981         if(this.cancelOnEsc && e.getKey() == e.ESC){
13982             this.cancelEdit();
13983             return;
13984         } 
13985         this.fireEvent('specialkey', field, e);
13986     
13987     },
13988
13989     /**
13990      * Starts the editing process and shows the editor.
13991      * @param {String/HTMLElement/Element} el The element to edit
13992      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13993       * to the innerHTML of el.
13994      */
13995     startEdit : function(el, value){
13996         if(this.editing){
13997             this.completeEdit();
13998         }
13999         this.boundEl = Roo.get(el);
14000         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14001         if(!this.rendered){
14002             this.render(this.parentEl || document.body);
14003         }
14004         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14005             return;
14006         }
14007         this.startValue = v;
14008         this.field.setValue(v);
14009         if(this.autoSize){
14010             var sz = this.boundEl.getSize();
14011             switch(this.autoSize){
14012                 case "width":
14013                 this.setSize(sz.width,  "");
14014                 break;
14015                 case "height":
14016                 this.setSize("",  sz.height);
14017                 break;
14018                 default:
14019                 this.setSize(sz.width,  sz.height);
14020             }
14021         }
14022         this.el.alignTo(this.boundEl, this.alignment);
14023         this.editing = true;
14024         if(Roo.QuickTips){
14025             Roo.QuickTips.disable();
14026         }
14027         this.show();
14028     },
14029
14030     /**
14031      * Sets the height and width of this editor.
14032      * @param {Number} width The new width
14033      * @param {Number} height The new height
14034      */
14035     setSize : function(w, h){
14036         this.field.setSize(w, h);
14037         if(this.el){
14038             this.el.sync();
14039         }
14040     },
14041
14042     /**
14043      * Realigns the editor to the bound field based on the current alignment config value.
14044      */
14045     realign : function(){
14046         this.el.alignTo(this.boundEl, this.alignment);
14047     },
14048
14049     /**
14050      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14051      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14052      */
14053     completeEdit : function(remainVisible){
14054         if(!this.editing){
14055             return;
14056         }
14057         var v = this.getValue();
14058         if(this.revertInvalid !== false && !this.field.isValid()){
14059             v = this.startValue;
14060             this.cancelEdit(true);
14061         }
14062         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14063             this.editing = false;
14064             this.hide();
14065             return;
14066         }
14067         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14068             this.editing = false;
14069             if(this.updateEl && this.boundEl){
14070                 this.boundEl.update(v);
14071             }
14072             if(remainVisible !== true){
14073                 this.hide();
14074             }
14075             this.fireEvent("complete", this, v, this.startValue);
14076         }
14077     },
14078
14079     // private
14080     onShow : function(){
14081         this.el.show();
14082         if(this.hideEl !== false){
14083             this.boundEl.hide();
14084         }
14085         this.field.show();
14086         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14087             this.fixIEFocus = true;
14088             this.deferredFocus.defer(50, this);
14089         }else{
14090             this.field.focus();
14091         }
14092         this.fireEvent("startedit", this.boundEl, this.startValue);
14093     },
14094
14095     deferredFocus : function(){
14096         if(this.editing){
14097             this.field.focus();
14098         }
14099     },
14100
14101     /**
14102      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14103      * reverted to the original starting value.
14104      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14105      * cancel (defaults to false)
14106      */
14107     cancelEdit : function(remainVisible){
14108         if(this.editing){
14109             this.setValue(this.startValue);
14110             if(remainVisible !== true){
14111                 this.hide();
14112             }
14113         }
14114     },
14115
14116     // private
14117     onBlur : function(){
14118         if(this.allowBlur !== true && this.editing){
14119             this.completeEdit();
14120         }
14121     },
14122
14123     // private
14124     onHide : function(){
14125         if(this.editing){
14126             this.completeEdit();
14127             return;
14128         }
14129         this.field.blur();
14130         if(this.field.collapse){
14131             this.field.collapse();
14132         }
14133         this.el.hide();
14134         if(this.hideEl !== false){
14135             this.boundEl.show();
14136         }
14137         if(Roo.QuickTips){
14138             Roo.QuickTips.enable();
14139         }
14140     },
14141
14142     /**
14143      * Sets the data value of the editor
14144      * @param {Mixed} value Any valid value supported by the underlying field
14145      */
14146     setValue : function(v){
14147         this.field.setValue(v);
14148     },
14149
14150     /**
14151      * Gets the data value of the editor
14152      * @return {Mixed} The data value
14153      */
14154     getValue : function(){
14155         return this.field.getValue();
14156     }
14157 });/*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167  
14168 /**
14169  * @class Roo.BasicDialog
14170  * @extends Roo.util.Observable
14171  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14172  * <pre><code>
14173 var dlg = new Roo.BasicDialog("my-dlg", {
14174     height: 200,
14175     width: 300,
14176     minHeight: 100,
14177     minWidth: 150,
14178     modal: true,
14179     proxyDrag: true,
14180     shadow: true
14181 });
14182 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14183 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14184 dlg.addButton('Cancel', dlg.hide, dlg);
14185 dlg.show();
14186 </code></pre>
14187   <b>A Dialog should always be a direct child of the body element.</b>
14188  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14189  * @cfg {String} title Default text to display in the title bar (defaults to null)
14190  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14191  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14192  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14193  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14194  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14195  * (defaults to null with no animation)
14196  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14197  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14198  * property for valid values (defaults to 'all')
14199  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14200  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14201  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14202  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14203  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14204  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14205  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14206  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14207  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14208  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14209  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14210  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14211  * draggable = true (defaults to false)
14212  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14213  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14214  * shadow (defaults to false)
14215  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14216  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14217  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14218  * @cfg {Array} buttons Array of buttons
14219  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14220  * @constructor
14221  * Create a new BasicDialog.
14222  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14223  * @param {Object} config Configuration options
14224  */
14225 Roo.BasicDialog = function(el, config){
14226     this.el = Roo.get(el);
14227     var dh = Roo.DomHelper;
14228     if(!this.el && config && config.autoCreate){
14229         if(typeof config.autoCreate == "object"){
14230             if(!config.autoCreate.id){
14231                 config.autoCreate.id = el;
14232             }
14233             this.el = dh.append(document.body,
14234                         config.autoCreate, true);
14235         }else{
14236             this.el = dh.append(document.body,
14237                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14238         }
14239     }
14240     el = this.el;
14241     el.setDisplayed(true);
14242     el.hide = this.hideAction;
14243     this.id = el.id;
14244     el.addClass("x-dlg");
14245
14246     Roo.apply(this, config);
14247
14248     this.proxy = el.createProxy("x-dlg-proxy");
14249     this.proxy.hide = this.hideAction;
14250     this.proxy.setOpacity(.5);
14251     this.proxy.hide();
14252
14253     if(config.width){
14254         el.setWidth(config.width);
14255     }
14256     if(config.height){
14257         el.setHeight(config.height);
14258     }
14259     this.size = el.getSize();
14260     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14261         this.xy = [config.x,config.y];
14262     }else{
14263         this.xy = el.getCenterXY(true);
14264     }
14265     /** The header element @type Roo.Element */
14266     this.header = el.child("> .x-dlg-hd");
14267     /** The body element @type Roo.Element */
14268     this.body = el.child("> .x-dlg-bd");
14269     /** The footer element @type Roo.Element */
14270     this.footer = el.child("> .x-dlg-ft");
14271
14272     if(!this.header){
14273         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14274     }
14275     if(!this.body){
14276         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14277     }
14278
14279     this.header.unselectable();
14280     if(this.title){
14281         this.header.update(this.title);
14282     }
14283     // this element allows the dialog to be focused for keyboard event
14284     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14285     this.focusEl.swallowEvent("click", true);
14286
14287     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14288
14289     // wrap the body and footer for special rendering
14290     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14291     if(this.footer){
14292         this.bwrap.dom.appendChild(this.footer.dom);
14293     }
14294
14295     this.bg = this.el.createChild({
14296         tag: "div", cls:"x-dlg-bg",
14297         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14298     });
14299     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14300
14301
14302     if(this.autoScroll !== false && !this.autoTabs){
14303         this.body.setStyle("overflow", "auto");
14304     }
14305
14306     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14307
14308     if(this.closable !== false){
14309         this.el.addClass("x-dlg-closable");
14310         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14311         this.close.on("click", this.closeClick, this);
14312         this.close.addClassOnOver("x-dlg-close-over");
14313     }
14314     if(this.collapsible !== false){
14315         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14316         this.collapseBtn.on("click", this.collapseClick, this);
14317         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14318         this.header.on("dblclick", this.collapseClick, this);
14319     }
14320     if(this.resizable !== false){
14321         this.el.addClass("x-dlg-resizable");
14322         this.resizer = new Roo.Resizable(el, {
14323             minWidth: this.minWidth || 80,
14324             minHeight:this.minHeight || 80,
14325             handles: this.resizeHandles || "all",
14326             pinned: true
14327         });
14328         this.resizer.on("beforeresize", this.beforeResize, this);
14329         this.resizer.on("resize", this.onResize, this);
14330     }
14331     if(this.draggable !== false){
14332         el.addClass("x-dlg-draggable");
14333         if (!this.proxyDrag) {
14334             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14335         }
14336         else {
14337             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14338         }
14339         dd.setHandleElId(this.header.id);
14340         dd.endDrag = this.endMove.createDelegate(this);
14341         dd.startDrag = this.startMove.createDelegate(this);
14342         dd.onDrag = this.onDrag.createDelegate(this);
14343         dd.scroll = false;
14344         this.dd = dd;
14345     }
14346     if(this.modal){
14347         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14348         this.mask.enableDisplayMode("block");
14349         this.mask.hide();
14350         this.el.addClass("x-dlg-modal");
14351     }
14352     if(this.shadow){
14353         this.shadow = new Roo.Shadow({
14354             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14355             offset : this.shadowOffset
14356         });
14357     }else{
14358         this.shadowOffset = 0;
14359     }
14360     if(Roo.useShims && this.shim !== false){
14361         this.shim = this.el.createShim();
14362         this.shim.hide = this.hideAction;
14363         this.shim.hide();
14364     }else{
14365         this.shim = false;
14366     }
14367     if(this.autoTabs){
14368         this.initTabs();
14369     }
14370     if (this.buttons) { 
14371         var bts= this.buttons;
14372         this.buttons = [];
14373         Roo.each(bts, function(b) {
14374             this.addButton(b);
14375         }, this);
14376     }
14377     
14378     
14379     this.addEvents({
14380         /**
14381          * @event keydown
14382          * Fires when a key is pressed
14383          * @param {Roo.BasicDialog} this
14384          * @param {Roo.EventObject} e
14385          */
14386         "keydown" : true,
14387         /**
14388          * @event move
14389          * Fires when this dialog is moved by the user.
14390          * @param {Roo.BasicDialog} this
14391          * @param {Number} x The new page X
14392          * @param {Number} y The new page Y
14393          */
14394         "move" : true,
14395         /**
14396          * @event resize
14397          * Fires when this dialog is resized by the user.
14398          * @param {Roo.BasicDialog} this
14399          * @param {Number} width The new width
14400          * @param {Number} height The new height
14401          */
14402         "resize" : true,
14403         /**
14404          * @event beforehide
14405          * Fires before this dialog is hidden.
14406          * @param {Roo.BasicDialog} this
14407          */
14408         "beforehide" : true,
14409         /**
14410          * @event hide
14411          * Fires when this dialog is hidden.
14412          * @param {Roo.BasicDialog} this
14413          */
14414         "hide" : true,
14415         /**
14416          * @event beforeshow
14417          * Fires before this dialog is shown.
14418          * @param {Roo.BasicDialog} this
14419          */
14420         "beforeshow" : true,
14421         /**
14422          * @event show
14423          * Fires when this dialog is shown.
14424          * @param {Roo.BasicDialog} this
14425          */
14426         "show" : true
14427     });
14428     el.on("keydown", this.onKeyDown, this);
14429     el.on("mousedown", this.toFront, this);
14430     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14431     this.el.hide();
14432     Roo.DialogManager.register(this);
14433     Roo.BasicDialog.superclass.constructor.call(this);
14434 };
14435
14436 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14437     shadowOffset: Roo.isIE ? 6 : 5,
14438     minHeight: 80,
14439     minWidth: 200,
14440     minButtonWidth: 75,
14441     defaultButton: null,
14442     buttonAlign: "right",
14443     tabTag: 'div',
14444     firstShow: true,
14445
14446     /**
14447      * Sets the dialog title text
14448      * @param {String} text The title text to display
14449      * @return {Roo.BasicDialog} this
14450      */
14451     setTitle : function(text){
14452         this.header.update(text);
14453         return this;
14454     },
14455
14456     // private
14457     closeClick : function(){
14458         this.hide();
14459     },
14460
14461     // private
14462     collapseClick : function(){
14463         this[this.collapsed ? "expand" : "collapse"]();
14464     },
14465
14466     /**
14467      * Collapses the dialog to its minimized state (only the title bar is visible).
14468      * Equivalent to the user clicking the collapse dialog button.
14469      */
14470     collapse : function(){
14471         if(!this.collapsed){
14472             this.collapsed = true;
14473             this.el.addClass("x-dlg-collapsed");
14474             this.restoreHeight = this.el.getHeight();
14475             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14476         }
14477     },
14478
14479     /**
14480      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14481      * clicking the expand dialog button.
14482      */
14483     expand : function(){
14484         if(this.collapsed){
14485             this.collapsed = false;
14486             this.el.removeClass("x-dlg-collapsed");
14487             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14488         }
14489     },
14490
14491     /**
14492      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14493      * @return {Roo.TabPanel} The tabs component
14494      */
14495     initTabs : function(){
14496         var tabs = this.getTabs();
14497         while(tabs.getTab(0)){
14498             tabs.removeTab(0);
14499         }
14500         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14501             var dom = el.dom;
14502             tabs.addTab(Roo.id(dom), dom.title);
14503             dom.title = "";
14504         });
14505         tabs.activate(0);
14506         return tabs;
14507     },
14508
14509     // private
14510     beforeResize : function(){
14511         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14512     },
14513
14514     // private
14515     onResize : function(){
14516         this.refreshSize();
14517         this.syncBodyHeight();
14518         this.adjustAssets();
14519         this.focus();
14520         this.fireEvent("resize", this, this.size.width, this.size.height);
14521     },
14522
14523     // private
14524     onKeyDown : function(e){
14525         if(this.isVisible()){
14526             this.fireEvent("keydown", this, e);
14527         }
14528     },
14529
14530     /**
14531      * Resizes the dialog.
14532      * @param {Number} width
14533      * @param {Number} height
14534      * @return {Roo.BasicDialog} this
14535      */
14536     resizeTo : function(width, height){
14537         this.el.setSize(width, height);
14538         this.size = {width: width, height: height};
14539         this.syncBodyHeight();
14540         if(this.fixedcenter){
14541             this.center();
14542         }
14543         if(this.isVisible()){
14544             this.constrainXY();
14545             this.adjustAssets();
14546         }
14547         this.fireEvent("resize", this, width, height);
14548         return this;
14549     },
14550
14551
14552     /**
14553      * Resizes the dialog to fit the specified content size.
14554      * @param {Number} width
14555      * @param {Number} height
14556      * @return {Roo.BasicDialog} this
14557      */
14558     setContentSize : function(w, h){
14559         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14560         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14561         //if(!this.el.isBorderBox()){
14562             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14563             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14564         //}
14565         if(this.tabs){
14566             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14567             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14568         }
14569         this.resizeTo(w, h);
14570         return this;
14571     },
14572
14573     /**
14574      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14575      * executed in response to a particular key being pressed while the dialog is active.
14576      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14577      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14578      * @param {Function} fn The function to call
14579      * @param {Object} scope (optional) The scope of the function
14580      * @return {Roo.BasicDialog} this
14581      */
14582     addKeyListener : function(key, fn, scope){
14583         var keyCode, shift, ctrl, alt;
14584         if(typeof key == "object" && !(key instanceof Array)){
14585             keyCode = key["key"];
14586             shift = key["shift"];
14587             ctrl = key["ctrl"];
14588             alt = key["alt"];
14589         }else{
14590             keyCode = key;
14591         }
14592         var handler = function(dlg, e){
14593             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14594                 var k = e.getKey();
14595                 if(keyCode instanceof Array){
14596                     for(var i = 0, len = keyCode.length; i < len; i++){
14597                         if(keyCode[i] == k){
14598                           fn.call(scope || window, dlg, k, e);
14599                           return;
14600                         }
14601                     }
14602                 }else{
14603                     if(k == keyCode){
14604                         fn.call(scope || window, dlg, k, e);
14605                     }
14606                 }
14607             }
14608         };
14609         this.on("keydown", handler);
14610         return this;
14611     },
14612
14613     /**
14614      * Returns the TabPanel component (creates it if it doesn't exist).
14615      * Note: If you wish to simply check for the existence of tabs without creating them,
14616      * check for a null 'tabs' property.
14617      * @return {Roo.TabPanel} The tabs component
14618      */
14619     getTabs : function(){
14620         if(!this.tabs){
14621             this.el.addClass("x-dlg-auto-tabs");
14622             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14623             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14624         }
14625         return this.tabs;
14626     },
14627
14628     /**
14629      * Adds a button to the footer section of the dialog.
14630      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14631      * object or a valid Roo.DomHelper element config
14632      * @param {Function} handler The function called when the button is clicked
14633      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14634      * @return {Roo.Button} The new button
14635      */
14636     addButton : function(config, handler, scope){
14637         var dh = Roo.DomHelper;
14638         if(!this.footer){
14639             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14640         }
14641         if(!this.btnContainer){
14642             var tb = this.footer.createChild({
14643
14644                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14645                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14646             }, null, true);
14647             this.btnContainer = tb.firstChild.firstChild.firstChild;
14648         }
14649         var bconfig = {
14650             handler: handler,
14651             scope: scope,
14652             minWidth: this.minButtonWidth,
14653             hideParent:true
14654         };
14655         if(typeof config == "string"){
14656             bconfig.text = config;
14657         }else{
14658             if(config.tag){
14659                 bconfig.dhconfig = config;
14660             }else{
14661                 Roo.apply(bconfig, config);
14662             }
14663         }
14664         var fc = false;
14665         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14666             bconfig.position = Math.max(0, bconfig.position);
14667             fc = this.btnContainer.childNodes[bconfig.position];
14668         }
14669          
14670         var btn = new Roo.Button(
14671             fc ? 
14672                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14673                 : this.btnContainer.appendChild(document.createElement("td")),
14674             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14675             bconfig
14676         );
14677         this.syncBodyHeight();
14678         if(!this.buttons){
14679             /**
14680              * Array of all the buttons that have been added to this dialog via addButton
14681              * @type Array
14682              */
14683             this.buttons = [];
14684         }
14685         this.buttons.push(btn);
14686         return btn;
14687     },
14688
14689     /**
14690      * Sets the default button to be focused when the dialog is displayed.
14691      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14692      * @return {Roo.BasicDialog} this
14693      */
14694     setDefaultButton : function(btn){
14695         this.defaultButton = btn;
14696         return this;
14697     },
14698
14699     // private
14700     getHeaderFooterHeight : function(safe){
14701         var height = 0;
14702         if(this.header){
14703            height += this.header.getHeight();
14704         }
14705         if(this.footer){
14706            var fm = this.footer.getMargins();
14707             height += (this.footer.getHeight()+fm.top+fm.bottom);
14708         }
14709         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14710         height += this.centerBg.getPadding("tb");
14711         return height;
14712     },
14713
14714     // private
14715     syncBodyHeight : function(){
14716         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14717         var height = this.size.height - this.getHeaderFooterHeight(false);
14718         bd.setHeight(height-bd.getMargins("tb"));
14719         var hh = this.header.getHeight();
14720         var h = this.size.height-hh;
14721         cb.setHeight(h);
14722         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14723         bw.setHeight(h-cb.getPadding("tb"));
14724         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14725         bd.setWidth(bw.getWidth(true));
14726         if(this.tabs){
14727             this.tabs.syncHeight();
14728             if(Roo.isIE){
14729                 this.tabs.el.repaint();
14730             }
14731         }
14732     },
14733
14734     /**
14735      * Restores the previous state of the dialog if Roo.state is configured.
14736      * @return {Roo.BasicDialog} this
14737      */
14738     restoreState : function(){
14739         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14740         if(box && box.width){
14741             this.xy = [box.x, box.y];
14742             this.resizeTo(box.width, box.height);
14743         }
14744         return this;
14745     },
14746
14747     // private
14748     beforeShow : function(){
14749         this.expand();
14750         if(this.fixedcenter){
14751             this.xy = this.el.getCenterXY(true);
14752         }
14753         if(this.modal){
14754             Roo.get(document.body).addClass("x-body-masked");
14755             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14756             this.mask.show();
14757         }
14758         this.constrainXY();
14759     },
14760
14761     // private
14762     animShow : function(){
14763         var b = Roo.get(this.animateTarget).getBox();
14764         this.proxy.setSize(b.width, b.height);
14765         this.proxy.setLocation(b.x, b.y);
14766         this.proxy.show();
14767         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14768                     true, .35, this.showEl.createDelegate(this));
14769     },
14770
14771     /**
14772      * Shows the dialog.
14773      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14774      * @return {Roo.BasicDialog} this
14775      */
14776     show : function(animateTarget){
14777         if (this.fireEvent("beforeshow", this) === false){
14778             return;
14779         }
14780         if(this.syncHeightBeforeShow){
14781             this.syncBodyHeight();
14782         }else if(this.firstShow){
14783             this.firstShow = false;
14784             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14785         }
14786         this.animateTarget = animateTarget || this.animateTarget;
14787         if(!this.el.isVisible()){
14788             this.beforeShow();
14789             if(this.animateTarget && Roo.get(this.animateTarget)){
14790                 this.animShow();
14791             }else{
14792                 this.showEl();
14793             }
14794         }
14795         return this;
14796     },
14797
14798     // private
14799     showEl : function(){
14800         this.proxy.hide();
14801         this.el.setXY(this.xy);
14802         this.el.show();
14803         this.adjustAssets(true);
14804         this.toFront();
14805         this.focus();
14806         // IE peekaboo bug - fix found by Dave Fenwick
14807         if(Roo.isIE){
14808             this.el.repaint();
14809         }
14810         this.fireEvent("show", this);
14811     },
14812
14813     /**
14814      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14815      * dialog itself will receive focus.
14816      */
14817     focus : function(){
14818         if(this.defaultButton){
14819             this.defaultButton.focus();
14820         }else{
14821             this.focusEl.focus();
14822         }
14823     },
14824
14825     // private
14826     constrainXY : function(){
14827         if(this.constraintoviewport !== false){
14828             if(!this.viewSize){
14829                 if(this.container){
14830                     var s = this.container.getSize();
14831                     this.viewSize = [s.width, s.height];
14832                 }else{
14833                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14834                 }
14835             }
14836             var s = Roo.get(this.container||document).getScroll();
14837
14838             var x = this.xy[0], y = this.xy[1];
14839             var w = this.size.width, h = this.size.height;
14840             var vw = this.viewSize[0], vh = this.viewSize[1];
14841             // only move it if it needs it
14842             var moved = false;
14843             // first validate right/bottom
14844             if(x + w > vw+s.left){
14845                 x = vw - w;
14846                 moved = true;
14847             }
14848             if(y + h > vh+s.top){
14849                 y = vh - h;
14850                 moved = true;
14851             }
14852             // then make sure top/left isn't negative
14853             if(x < s.left){
14854                 x = s.left;
14855                 moved = true;
14856             }
14857             if(y < s.top){
14858                 y = s.top;
14859                 moved = true;
14860             }
14861             if(moved){
14862                 // cache xy
14863                 this.xy = [x, y];
14864                 if(this.isVisible()){
14865                     this.el.setLocation(x, y);
14866                     this.adjustAssets();
14867                 }
14868             }
14869         }
14870     },
14871
14872     // private
14873     onDrag : function(){
14874         if(!this.proxyDrag){
14875             this.xy = this.el.getXY();
14876             this.adjustAssets();
14877         }
14878     },
14879
14880     // private
14881     adjustAssets : function(doShow){
14882         var x = this.xy[0], y = this.xy[1];
14883         var w = this.size.width, h = this.size.height;
14884         if(doShow === true){
14885             if(this.shadow){
14886                 this.shadow.show(this.el);
14887             }
14888             if(this.shim){
14889                 this.shim.show();
14890             }
14891         }
14892         if(this.shadow && this.shadow.isVisible()){
14893             this.shadow.show(this.el);
14894         }
14895         if(this.shim && this.shim.isVisible()){
14896             this.shim.setBounds(x, y, w, h);
14897         }
14898     },
14899
14900     // private
14901     adjustViewport : function(w, h){
14902         if(!w || !h){
14903             w = Roo.lib.Dom.getViewWidth();
14904             h = Roo.lib.Dom.getViewHeight();
14905         }
14906         // cache the size
14907         this.viewSize = [w, h];
14908         if(this.modal && this.mask.isVisible()){
14909             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14910             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14911         }
14912         if(this.isVisible()){
14913             this.constrainXY();
14914         }
14915     },
14916
14917     /**
14918      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14919      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14920      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14921      */
14922     destroy : function(removeEl){
14923         if(this.isVisible()){
14924             this.animateTarget = null;
14925             this.hide();
14926         }
14927         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14928         if(this.tabs){
14929             this.tabs.destroy(removeEl);
14930         }
14931         Roo.destroy(
14932              this.shim,
14933              this.proxy,
14934              this.resizer,
14935              this.close,
14936              this.mask
14937         );
14938         if(this.dd){
14939             this.dd.unreg();
14940         }
14941         if(this.buttons){
14942            for(var i = 0, len = this.buttons.length; i < len; i++){
14943                this.buttons[i].destroy();
14944            }
14945         }
14946         this.el.removeAllListeners();
14947         if(removeEl === true){
14948             this.el.update("");
14949             this.el.remove();
14950         }
14951         Roo.DialogManager.unregister(this);
14952     },
14953
14954     // private
14955     startMove : function(){
14956         if(this.proxyDrag){
14957             this.proxy.show();
14958         }
14959         if(this.constraintoviewport !== false){
14960             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14961         }
14962     },
14963
14964     // private
14965     endMove : function(){
14966         if(!this.proxyDrag){
14967             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14968         }else{
14969             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14970             this.proxy.hide();
14971         }
14972         this.refreshSize();
14973         this.adjustAssets();
14974         this.focus();
14975         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14976     },
14977
14978     /**
14979      * Brings this dialog to the front of any other visible dialogs
14980      * @return {Roo.BasicDialog} this
14981      */
14982     toFront : function(){
14983         Roo.DialogManager.bringToFront(this);
14984         return this;
14985     },
14986
14987     /**
14988      * Sends this dialog to the back (under) of any other visible dialogs
14989      * @return {Roo.BasicDialog} this
14990      */
14991     toBack : function(){
14992         Roo.DialogManager.sendToBack(this);
14993         return this;
14994     },
14995
14996     /**
14997      * Centers this dialog in the viewport
14998      * @return {Roo.BasicDialog} this
14999      */
15000     center : function(){
15001         var xy = this.el.getCenterXY(true);
15002         this.moveTo(xy[0], xy[1]);
15003         return this;
15004     },
15005
15006     /**
15007      * Moves the dialog's top-left corner to the specified point
15008      * @param {Number} x
15009      * @param {Number} y
15010      * @return {Roo.BasicDialog} this
15011      */
15012     moveTo : function(x, y){
15013         this.xy = [x,y];
15014         if(this.isVisible()){
15015             this.el.setXY(this.xy);
15016             this.adjustAssets();
15017         }
15018         return this;
15019     },
15020
15021     /**
15022      * Aligns the dialog to the specified element
15023      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15024      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15025      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15026      * @return {Roo.BasicDialog} this
15027      */
15028     alignTo : function(element, position, offsets){
15029         this.xy = this.el.getAlignToXY(element, position, offsets);
15030         if(this.isVisible()){
15031             this.el.setXY(this.xy);
15032             this.adjustAssets();
15033         }
15034         return this;
15035     },
15036
15037     /**
15038      * Anchors an element to another element and realigns it when the window is resized.
15039      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15040      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15041      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15042      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15043      * is a number, it is used as the buffer delay (defaults to 50ms).
15044      * @return {Roo.BasicDialog} this
15045      */
15046     anchorTo : function(el, alignment, offsets, monitorScroll){
15047         var action = function(){
15048             this.alignTo(el, alignment, offsets);
15049         };
15050         Roo.EventManager.onWindowResize(action, this);
15051         var tm = typeof monitorScroll;
15052         if(tm != 'undefined'){
15053             Roo.EventManager.on(window, 'scroll', action, this,
15054                 {buffer: tm == 'number' ? monitorScroll : 50});
15055         }
15056         action.call(this);
15057         return this;
15058     },
15059
15060     /**
15061      * Returns true if the dialog is visible
15062      * @return {Boolean}
15063      */
15064     isVisible : function(){
15065         return this.el.isVisible();
15066     },
15067
15068     // private
15069     animHide : function(callback){
15070         var b = Roo.get(this.animateTarget).getBox();
15071         this.proxy.show();
15072         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15073         this.el.hide();
15074         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15075                     this.hideEl.createDelegate(this, [callback]));
15076     },
15077
15078     /**
15079      * Hides the dialog.
15080      * @param {Function} callback (optional) Function to call when the dialog is hidden
15081      * @return {Roo.BasicDialog} this
15082      */
15083     hide : function(callback){
15084         if (this.fireEvent("beforehide", this) === false){
15085             return;
15086         }
15087         if(this.shadow){
15088             this.shadow.hide();
15089         }
15090         if(this.shim) {
15091           this.shim.hide();
15092         }
15093         // sometimes animateTarget seems to get set.. causing problems...
15094         // this just double checks..
15095         if(this.animateTarget && Roo.get(this.animateTarget)) {
15096            this.animHide(callback);
15097         }else{
15098             this.el.hide();
15099             this.hideEl(callback);
15100         }
15101         return this;
15102     },
15103
15104     // private
15105     hideEl : function(callback){
15106         this.proxy.hide();
15107         if(this.modal){
15108             this.mask.hide();
15109             Roo.get(document.body).removeClass("x-body-masked");
15110         }
15111         this.fireEvent("hide", this);
15112         if(typeof callback == "function"){
15113             callback();
15114         }
15115     },
15116
15117     // private
15118     hideAction : function(){
15119         this.setLeft("-10000px");
15120         this.setTop("-10000px");
15121         this.setStyle("visibility", "hidden");
15122     },
15123
15124     // private
15125     refreshSize : function(){
15126         this.size = this.el.getSize();
15127         this.xy = this.el.getXY();
15128         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15129     },
15130
15131     // private
15132     // z-index is managed by the DialogManager and may be overwritten at any time
15133     setZIndex : function(index){
15134         if(this.modal){
15135             this.mask.setStyle("z-index", index);
15136         }
15137         if(this.shim){
15138             this.shim.setStyle("z-index", ++index);
15139         }
15140         if(this.shadow){
15141             this.shadow.setZIndex(++index);
15142         }
15143         this.el.setStyle("z-index", ++index);
15144         if(this.proxy){
15145             this.proxy.setStyle("z-index", ++index);
15146         }
15147         if(this.resizer){
15148             this.resizer.proxy.setStyle("z-index", ++index);
15149         }
15150
15151         this.lastZIndex = index;
15152     },
15153
15154     /**
15155      * Returns the element for this dialog
15156      * @return {Roo.Element} The underlying dialog Element
15157      */
15158     getEl : function(){
15159         return this.el;
15160     }
15161 });
15162
15163 /**
15164  * @class Roo.DialogManager
15165  * Provides global access to BasicDialogs that have been created and
15166  * support for z-indexing (layering) multiple open dialogs.
15167  */
15168 Roo.DialogManager = function(){
15169     var list = {};
15170     var accessList = [];
15171     var front = null;
15172
15173     // private
15174     var sortDialogs = function(d1, d2){
15175         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15176     };
15177
15178     // private
15179     var orderDialogs = function(){
15180         accessList.sort(sortDialogs);
15181         var seed = Roo.DialogManager.zseed;
15182         for(var i = 0, len = accessList.length; i < len; i++){
15183             var dlg = accessList[i];
15184             if(dlg){
15185                 dlg.setZIndex(seed + (i*10));
15186             }
15187         }
15188     };
15189
15190     return {
15191         /**
15192          * The starting z-index for BasicDialogs (defaults to 9000)
15193          * @type Number The z-index value
15194          */
15195         zseed : 9000,
15196
15197         // private
15198         register : function(dlg){
15199             list[dlg.id] = dlg;
15200             accessList.push(dlg);
15201         },
15202
15203         // private
15204         unregister : function(dlg){
15205             delete list[dlg.id];
15206             var i=0;
15207             var len=0;
15208             if(!accessList.indexOf){
15209                 for(  i = 0, len = accessList.length; i < len; i++){
15210                     if(accessList[i] == dlg){
15211                         accessList.splice(i, 1);
15212                         return;
15213                     }
15214                 }
15215             }else{
15216                  i = accessList.indexOf(dlg);
15217                 if(i != -1){
15218                     accessList.splice(i, 1);
15219                 }
15220             }
15221         },
15222
15223         /**
15224          * Gets a registered dialog by id
15225          * @param {String/Object} id The id of the dialog or a dialog
15226          * @return {Roo.BasicDialog} this
15227          */
15228         get : function(id){
15229             return typeof id == "object" ? id : list[id];
15230         },
15231
15232         /**
15233          * Brings the specified dialog to the front
15234          * @param {String/Object} dlg The id of the dialog or a dialog
15235          * @return {Roo.BasicDialog} this
15236          */
15237         bringToFront : function(dlg){
15238             dlg = this.get(dlg);
15239             if(dlg != front){
15240                 front = dlg;
15241                 dlg._lastAccess = new Date().getTime();
15242                 orderDialogs();
15243             }
15244             return dlg;
15245         },
15246
15247         /**
15248          * Sends the specified dialog to the back
15249          * @param {String/Object} dlg The id of the dialog or a dialog
15250          * @return {Roo.BasicDialog} this
15251          */
15252         sendToBack : function(dlg){
15253             dlg = this.get(dlg);
15254             dlg._lastAccess = -(new Date().getTime());
15255             orderDialogs();
15256             return dlg;
15257         },
15258
15259         /**
15260          * Hides all dialogs
15261          */
15262         hideAll : function(){
15263             for(var id in list){
15264                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15265                     list[id].hide();
15266                 }
15267             }
15268         }
15269     };
15270 }();
15271
15272 /**
15273  * @class Roo.LayoutDialog
15274  * @extends Roo.BasicDialog
15275  * Dialog which provides adjustments for working with a layout in a Dialog.
15276  * Add your necessary layout config options to the dialog's config.<br>
15277  * Example usage (including a nested layout):
15278  * <pre><code>
15279 if(!dialog){
15280     dialog = new Roo.LayoutDialog("download-dlg", {
15281         modal: true,
15282         width:600,
15283         height:450,
15284         shadow:true,
15285         minWidth:500,
15286         minHeight:350,
15287         autoTabs:true,
15288         proxyDrag:true,
15289         // layout config merges with the dialog config
15290         center:{
15291             tabPosition: "top",
15292             alwaysShowTabs: true
15293         }
15294     });
15295     dialog.addKeyListener(27, dialog.hide, dialog);
15296     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15297     dialog.addButton("Build It!", this.getDownload, this);
15298
15299     // we can even add nested layouts
15300     var innerLayout = new Roo.BorderLayout("dl-inner", {
15301         east: {
15302             initialSize: 200,
15303             autoScroll:true,
15304             split:true
15305         },
15306         center: {
15307             autoScroll:true
15308         }
15309     });
15310     innerLayout.beginUpdate();
15311     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15312     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15313     innerLayout.endUpdate(true);
15314
15315     var layout = dialog.getLayout();
15316     layout.beginUpdate();
15317     layout.add("center", new Roo.ContentPanel("standard-panel",
15318                         {title: "Download the Source", fitToFrame:true}));
15319     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15320                {title: "Build your own roo.js"}));
15321     layout.getRegion("center").showPanel(sp);
15322     layout.endUpdate();
15323 }
15324 </code></pre>
15325     * @constructor
15326     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15327     * @param {Object} config configuration options
15328   */
15329 Roo.LayoutDialog = function(el, cfg){
15330     
15331     var config=  cfg;
15332     if (typeof(cfg) == 'undefined') {
15333         config = Roo.apply({}, el);
15334         // not sure why we use documentElement here.. - it should always be body.
15335         // IE7 borks horribly if we use documentElement.
15336         // webkit also does not like documentElement - it creates a body element...
15337         el = Roo.get( document.body || document.documentElement ).createChild();
15338         //config.autoCreate = true;
15339     }
15340     
15341     
15342     config.autoTabs = false;
15343     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15344     this.body.setStyle({overflow:"hidden", position:"relative"});
15345     this.layout = new Roo.BorderLayout(this.body.dom, config);
15346     this.layout.monitorWindowResize = false;
15347     this.el.addClass("x-dlg-auto-layout");
15348     // fix case when center region overwrites center function
15349     this.center = Roo.BasicDialog.prototype.center;
15350     this.on("show", this.layout.layout, this.layout, true);
15351     if (config.items) {
15352         var xitems = config.items;
15353         delete config.items;
15354         Roo.each(xitems, this.addxtype, this);
15355     }
15356     
15357     
15358 };
15359 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15360     /**
15361      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15362      * @deprecated
15363      */
15364     endUpdate : function(){
15365         this.layout.endUpdate();
15366     },
15367
15368     /**
15369      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15370      *  @deprecated
15371      */
15372     beginUpdate : function(){
15373         this.layout.beginUpdate();
15374     },
15375
15376     /**
15377      * Get the BorderLayout for this dialog
15378      * @return {Roo.BorderLayout}
15379      */
15380     getLayout : function(){
15381         return this.layout;
15382     },
15383
15384     showEl : function(){
15385         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15386         if(Roo.isIE7){
15387             this.layout.layout();
15388         }
15389     },
15390
15391     // private
15392     // Use the syncHeightBeforeShow config option to control this automatically
15393     syncBodyHeight : function(){
15394         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15395         if(this.layout){this.layout.layout();}
15396     },
15397     
15398       /**
15399      * Add an xtype element (actually adds to the layout.)
15400      * @return {Object} xdata xtype object data.
15401      */
15402     
15403     addxtype : function(c) {
15404         return this.layout.addxtype(c);
15405     }
15406 });/*
15407  * Based on:
15408  * Ext JS Library 1.1.1
15409  * Copyright(c) 2006-2007, Ext JS, LLC.
15410  *
15411  * Originally Released Under LGPL - original licence link has changed is not relivant.
15412  *
15413  * Fork - LGPL
15414  * <script type="text/javascript">
15415  */
15416  
15417 /**
15418  * @class Roo.MessageBox
15419  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15420  * Example usage:
15421  *<pre><code>
15422 // Basic alert:
15423 Roo.Msg.alert('Status', 'Changes saved successfully.');
15424
15425 // Prompt for user data:
15426 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15427     if (btn == 'ok'){
15428         // process text value...
15429     }
15430 });
15431
15432 // Show a dialog using config options:
15433 Roo.Msg.show({
15434    title:'Save Changes?',
15435    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15436    buttons: Roo.Msg.YESNOCANCEL,
15437    fn: processResult,
15438    animEl: 'elId'
15439 });
15440 </code></pre>
15441  * @singleton
15442  */
15443 Roo.MessageBox = function(){
15444     var dlg, opt, mask, waitTimer;
15445     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15446     var buttons, activeTextEl, bwidth;
15447
15448     // private
15449     var handleButton = function(button){
15450         dlg.hide();
15451         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15452     };
15453
15454     // private
15455     var handleHide = function(){
15456         if(opt && opt.cls){
15457             dlg.el.removeClass(opt.cls);
15458         }
15459         if(waitTimer){
15460             Roo.TaskMgr.stop(waitTimer);
15461             waitTimer = null;
15462         }
15463     };
15464
15465     // private
15466     var updateButtons = function(b){
15467         var width = 0;
15468         if(!b){
15469             buttons["ok"].hide();
15470             buttons["cancel"].hide();
15471             buttons["yes"].hide();
15472             buttons["no"].hide();
15473             dlg.footer.dom.style.display = 'none';
15474             return width;
15475         }
15476         dlg.footer.dom.style.display = '';
15477         for(var k in buttons){
15478             if(typeof buttons[k] != "function"){
15479                 if(b[k]){
15480                     buttons[k].show();
15481                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15482                     width += buttons[k].el.getWidth()+15;
15483                 }else{
15484                     buttons[k].hide();
15485                 }
15486             }
15487         }
15488         return width;
15489     };
15490
15491     // private
15492     var handleEsc = function(d, k, e){
15493         if(opt && opt.closable !== false){
15494             dlg.hide();
15495         }
15496         if(e){
15497             e.stopEvent();
15498         }
15499     };
15500
15501     return {
15502         /**
15503          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15504          * @return {Roo.BasicDialog} The BasicDialog element
15505          */
15506         getDialog : function(){
15507            if(!dlg){
15508                 dlg = new Roo.BasicDialog("x-msg-box", {
15509                     autoCreate : true,
15510                     shadow: true,
15511                     draggable: true,
15512                     resizable:false,
15513                     constraintoviewport:false,
15514                     fixedcenter:true,
15515                     collapsible : false,
15516                     shim:true,
15517                     modal: true,
15518                     width:400, height:100,
15519                     buttonAlign:"center",
15520                     closeClick : function(){
15521                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15522                             handleButton("no");
15523                         }else{
15524                             handleButton("cancel");
15525                         }
15526                     }
15527                 });
15528                 dlg.on("hide", handleHide);
15529                 mask = dlg.mask;
15530                 dlg.addKeyListener(27, handleEsc);
15531                 buttons = {};
15532                 var bt = this.buttonText;
15533                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15534                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15535                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15536                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15537                 bodyEl = dlg.body.createChild({
15538
15539                     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>'
15540                 });
15541                 msgEl = bodyEl.dom.firstChild;
15542                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15543                 textboxEl.enableDisplayMode();
15544                 textboxEl.addKeyListener([10,13], function(){
15545                     if(dlg.isVisible() && opt && opt.buttons){
15546                         if(opt.buttons.ok){
15547                             handleButton("ok");
15548                         }else if(opt.buttons.yes){
15549                             handleButton("yes");
15550                         }
15551                     }
15552                 });
15553                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15554                 textareaEl.enableDisplayMode();
15555                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15556                 progressEl.enableDisplayMode();
15557                 var pf = progressEl.dom.firstChild;
15558                 if (pf) {
15559                     pp = Roo.get(pf.firstChild);
15560                     pp.setHeight(pf.offsetHeight);
15561                 }
15562                 
15563             }
15564             return dlg;
15565         },
15566
15567         /**
15568          * Updates the message box body text
15569          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15570          * the XHTML-compliant non-breaking space character '&amp;#160;')
15571          * @return {Roo.MessageBox} This message box
15572          */
15573         updateText : function(text){
15574             if(!dlg.isVisible() && !opt.width){
15575                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15576             }
15577             msgEl.innerHTML = text || '&#160;';
15578             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15579                         Math.max(opt.minWidth || this.minWidth, bwidth));
15580             if(opt.prompt){
15581                 activeTextEl.setWidth(w);
15582             }
15583             if(dlg.isVisible()){
15584                 dlg.fixedcenter = false;
15585             }
15586             dlg.setContentSize(w, bodyEl.getHeight());
15587             if(dlg.isVisible()){
15588                 dlg.fixedcenter = true;
15589             }
15590             return this;
15591         },
15592
15593         /**
15594          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15595          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15596          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15597          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15598          * @return {Roo.MessageBox} This message box
15599          */
15600         updateProgress : function(value, text){
15601             if(text){
15602                 this.updateText(text);
15603             }
15604             if (pp) { // weird bug on my firefox - for some reason this is not defined
15605                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15606             }
15607             return this;
15608         },        
15609
15610         /**
15611          * Returns true if the message box is currently displayed
15612          * @return {Boolean} True if the message box is visible, else false
15613          */
15614         isVisible : function(){
15615             return dlg && dlg.isVisible();  
15616         },
15617
15618         /**
15619          * Hides the message box if it is displayed
15620          */
15621         hide : function(){
15622             if(this.isVisible()){
15623                 dlg.hide();
15624             }  
15625         },
15626
15627         /**
15628          * Displays a new message box, or reinitializes an existing message box, based on the config options
15629          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15630          * The following config object properties are supported:
15631          * <pre>
15632 Property    Type             Description
15633 ----------  ---------------  ------------------------------------------------------------------------------------
15634 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15635                                    closes (defaults to undefined)
15636 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15637                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15638 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15639                                    progress and wait dialogs will ignore this property and always hide the
15640                                    close button as they can only be closed programmatically.
15641 cls               String           A custom CSS class to apply to the message box element
15642 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15643                                    displayed (defaults to 75)
15644 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15645                                    function will be btn (the name of the button that was clicked, if applicable,
15646                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15647                                    Progress and wait dialogs will ignore this option since they do not respond to
15648                                    user actions and can only be closed programmatically, so any required function
15649                                    should be called by the same code after it closes the dialog.
15650 icon              String           A CSS class that provides a background image to be used as an icon for
15651                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15652 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15653 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15654 modal             Boolean          False to allow user interaction with the page while the message box is
15655                                    displayed (defaults to true)
15656 msg               String           A string that will replace the existing message box body text (defaults
15657                                    to the XHTML-compliant non-breaking space character '&#160;')
15658 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15659 progress          Boolean          True to display a progress bar (defaults to false)
15660 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15661 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15662 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15663 title             String           The title text
15664 value             String           The string value to set into the active textbox element if displayed
15665 wait              Boolean          True to display a progress bar (defaults to false)
15666 width             Number           The width of the dialog in pixels
15667 </pre>
15668          *
15669          * Example usage:
15670          * <pre><code>
15671 Roo.Msg.show({
15672    title: 'Address',
15673    msg: 'Please enter your address:',
15674    width: 300,
15675    buttons: Roo.MessageBox.OKCANCEL,
15676    multiline: true,
15677    fn: saveAddress,
15678    animEl: 'addAddressBtn'
15679 });
15680 </code></pre>
15681          * @param {Object} config Configuration options
15682          * @return {Roo.MessageBox} This message box
15683          */
15684         show : function(options){
15685             if(this.isVisible()){
15686                 this.hide();
15687             }
15688             var d = this.getDialog();
15689             opt = options;
15690             d.setTitle(opt.title || "&#160;");
15691             d.close.setDisplayed(opt.closable !== false);
15692             activeTextEl = textboxEl;
15693             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15694             if(opt.prompt){
15695                 if(opt.multiline){
15696                     textboxEl.hide();
15697                     textareaEl.show();
15698                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15699                         opt.multiline : this.defaultTextHeight);
15700                     activeTextEl = textareaEl;
15701                 }else{
15702                     textboxEl.show();
15703                     textareaEl.hide();
15704                 }
15705             }else{
15706                 textboxEl.hide();
15707                 textareaEl.hide();
15708             }
15709             progressEl.setDisplayed(opt.progress === true);
15710             this.updateProgress(0);
15711             activeTextEl.dom.value = opt.value || "";
15712             if(opt.prompt){
15713                 dlg.setDefaultButton(activeTextEl);
15714             }else{
15715                 var bs = opt.buttons;
15716                 var db = null;
15717                 if(bs && bs.ok){
15718                     db = buttons["ok"];
15719                 }else if(bs && bs.yes){
15720                     db = buttons["yes"];
15721                 }
15722                 dlg.setDefaultButton(db);
15723             }
15724             bwidth = updateButtons(opt.buttons);
15725             this.updateText(opt.msg);
15726             if(opt.cls){
15727                 d.el.addClass(opt.cls);
15728             }
15729             d.proxyDrag = opt.proxyDrag === true;
15730             d.modal = opt.modal !== false;
15731             d.mask = opt.modal !== false ? mask : false;
15732             if(!d.isVisible()){
15733                 // force it to the end of the z-index stack so it gets a cursor in FF
15734                 document.body.appendChild(dlg.el.dom);
15735                 d.animateTarget = null;
15736                 d.show(options.animEl);
15737             }
15738             return this;
15739         },
15740
15741         /**
15742          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15743          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15744          * and closing the message box when the process is complete.
15745          * @param {String} title The title bar text
15746          * @param {String} msg The message box body text
15747          * @return {Roo.MessageBox} This message box
15748          */
15749         progress : function(title, msg){
15750             this.show({
15751                 title : title,
15752                 msg : msg,
15753                 buttons: false,
15754                 progress:true,
15755                 closable:false,
15756                 minWidth: this.minProgressWidth,
15757                 modal : true
15758             });
15759             return this;
15760         },
15761
15762         /**
15763          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15764          * If a callback function is passed it will be called after the user clicks the button, and the
15765          * id of the button that was clicked will be passed as the only parameter to the callback
15766          * (could also be the top-right close button).
15767          * @param {String} title The title bar text
15768          * @param {String} msg The message box body text
15769          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15770          * @param {Object} scope (optional) The scope of the callback function
15771          * @return {Roo.MessageBox} This message box
15772          */
15773         alert : function(title, msg, fn, scope){
15774             this.show({
15775                 title : title,
15776                 msg : msg,
15777                 buttons: this.OK,
15778                 fn: fn,
15779                 scope : scope,
15780                 modal : true
15781             });
15782             return this;
15783         },
15784
15785         /**
15786          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15787          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15788          * You are responsible for closing the message box when the process is complete.
15789          * @param {String} msg The message box body text
15790          * @param {String} title (optional) The title bar text
15791          * @return {Roo.MessageBox} This message box
15792          */
15793         wait : function(msg, title){
15794             this.show({
15795                 title : title,
15796                 msg : msg,
15797                 buttons: false,
15798                 closable:false,
15799                 progress:true,
15800                 modal:true,
15801                 width:300,
15802                 wait:true
15803             });
15804             waitTimer = Roo.TaskMgr.start({
15805                 run: function(i){
15806                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15807                 },
15808                 interval: 1000
15809             });
15810             return this;
15811         },
15812
15813         /**
15814          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15815          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15816          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15817          * @param {String} title The title bar text
15818          * @param {String} msg The message box body text
15819          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15820          * @param {Object} scope (optional) The scope of the callback function
15821          * @return {Roo.MessageBox} This message box
15822          */
15823         confirm : function(title, msg, fn, scope){
15824             this.show({
15825                 title : title,
15826                 msg : msg,
15827                 buttons: this.YESNO,
15828                 fn: fn,
15829                 scope : scope,
15830                 modal : true
15831             });
15832             return this;
15833         },
15834
15835         /**
15836          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15837          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15838          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15839          * (could also be the top-right close button) and the text that was entered will be passed as the two
15840          * parameters to the callback.
15841          * @param {String} title The title bar text
15842          * @param {String} msg The message box body text
15843          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15844          * @param {Object} scope (optional) The scope of the callback function
15845          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15846          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15847          * @return {Roo.MessageBox} This message box
15848          */
15849         prompt : function(title, msg, fn, scope, multiline){
15850             this.show({
15851                 title : title,
15852                 msg : msg,
15853                 buttons: this.OKCANCEL,
15854                 fn: fn,
15855                 minWidth:250,
15856                 scope : scope,
15857                 prompt:true,
15858                 multiline: multiline,
15859                 modal : true
15860             });
15861             return this;
15862         },
15863
15864         /**
15865          * Button config that displays a single OK button
15866          * @type Object
15867          */
15868         OK : {ok:true},
15869         /**
15870          * Button config that displays Yes and No buttons
15871          * @type Object
15872          */
15873         YESNO : {yes:true, no:true},
15874         /**
15875          * Button config that displays OK and Cancel buttons
15876          * @type Object
15877          */
15878         OKCANCEL : {ok:true, cancel:true},
15879         /**
15880          * Button config that displays Yes, No and Cancel buttons
15881          * @type Object
15882          */
15883         YESNOCANCEL : {yes:true, no:true, cancel:true},
15884
15885         /**
15886          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15887          * @type Number
15888          */
15889         defaultTextHeight : 75,
15890         /**
15891          * The maximum width in pixels of the message box (defaults to 600)
15892          * @type Number
15893          */
15894         maxWidth : 600,
15895         /**
15896          * The minimum width in pixels of the message box (defaults to 100)
15897          * @type Number
15898          */
15899         minWidth : 100,
15900         /**
15901          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15902          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15903          * @type Number
15904          */
15905         minProgressWidth : 250,
15906         /**
15907          * An object containing the default button text strings that can be overriden for localized language support.
15908          * Supported properties are: ok, cancel, yes and no.
15909          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15910          * @type Object
15911          */
15912         buttonText : {
15913             ok : "OK",
15914             cancel : "Cancel",
15915             yes : "Yes",
15916             no : "No"
15917         }
15918     };
15919 }();
15920
15921 /**
15922  * Shorthand for {@link Roo.MessageBox}
15923  */
15924 Roo.Msg = Roo.MessageBox;/*
15925  * Based on:
15926  * Ext JS Library 1.1.1
15927  * Copyright(c) 2006-2007, Ext JS, LLC.
15928  *
15929  * Originally Released Under LGPL - original licence link has changed is not relivant.
15930  *
15931  * Fork - LGPL
15932  * <script type="text/javascript">
15933  */
15934 /**
15935  * @class Roo.QuickTips
15936  * Provides attractive and customizable tooltips for any element.
15937  * @singleton
15938  */
15939 Roo.QuickTips = function(){
15940     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15941     var ce, bd, xy, dd;
15942     var visible = false, disabled = true, inited = false;
15943     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15944     
15945     var onOver = function(e){
15946         if(disabled){
15947             return;
15948         }
15949         var t = e.getTarget();
15950         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15951             return;
15952         }
15953         if(ce && t == ce.el){
15954             clearTimeout(hideProc);
15955             return;
15956         }
15957         if(t && tagEls[t.id]){
15958             tagEls[t.id].el = t;
15959             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15960             return;
15961         }
15962         var ttp, et = Roo.fly(t);
15963         var ns = cfg.namespace;
15964         if(tm.interceptTitles && t.title){
15965             ttp = t.title;
15966             t.qtip = ttp;
15967             t.removeAttribute("title");
15968             e.preventDefault();
15969         }else{
15970             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15971         }
15972         if(ttp){
15973             showProc = show.defer(tm.showDelay, tm, [{
15974                 el: t, 
15975                 text: ttp, 
15976                 width: et.getAttributeNS(ns, cfg.width),
15977                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15978                 title: et.getAttributeNS(ns, cfg.title),
15979                     cls: et.getAttributeNS(ns, cfg.cls)
15980             }]);
15981         }
15982     };
15983     
15984     var onOut = function(e){
15985         clearTimeout(showProc);
15986         var t = e.getTarget();
15987         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15988             hideProc = setTimeout(hide, tm.hideDelay);
15989         }
15990     };
15991     
15992     var onMove = function(e){
15993         if(disabled){
15994             return;
15995         }
15996         xy = e.getXY();
15997         xy[1] += 18;
15998         if(tm.trackMouse && ce){
15999             el.setXY(xy);
16000         }
16001     };
16002     
16003     var onDown = function(e){
16004         clearTimeout(showProc);
16005         clearTimeout(hideProc);
16006         if(!e.within(el)){
16007             if(tm.hideOnClick){
16008                 hide();
16009                 tm.disable();
16010                 tm.enable.defer(100, tm);
16011             }
16012         }
16013     };
16014     
16015     var getPad = function(){
16016         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16017     };
16018
16019     var show = function(o){
16020         if(disabled){
16021             return;
16022         }
16023         clearTimeout(dismissProc);
16024         ce = o;
16025         if(removeCls){ // in case manually hidden
16026             el.removeClass(removeCls);
16027             removeCls = null;
16028         }
16029         if(ce.cls){
16030             el.addClass(ce.cls);
16031             removeCls = ce.cls;
16032         }
16033         if(ce.title){
16034             tipTitle.update(ce.title);
16035             tipTitle.show();
16036         }else{
16037             tipTitle.update('');
16038             tipTitle.hide();
16039         }
16040         el.dom.style.width  = tm.maxWidth+'px';
16041         //tipBody.dom.style.width = '';
16042         tipBodyText.update(o.text);
16043         var p = getPad(), w = ce.width;
16044         if(!w){
16045             var td = tipBodyText.dom;
16046             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16047             if(aw > tm.maxWidth){
16048                 w = tm.maxWidth;
16049             }else if(aw < tm.minWidth){
16050                 w = tm.minWidth;
16051             }else{
16052                 w = aw;
16053             }
16054         }
16055         //tipBody.setWidth(w);
16056         el.setWidth(parseInt(w, 10) + p);
16057         if(ce.autoHide === false){
16058             close.setDisplayed(true);
16059             if(dd){
16060                 dd.unlock();
16061             }
16062         }else{
16063             close.setDisplayed(false);
16064             if(dd){
16065                 dd.lock();
16066             }
16067         }
16068         if(xy){
16069             el.avoidY = xy[1]-18;
16070             el.setXY(xy);
16071         }
16072         if(tm.animate){
16073             el.setOpacity(.1);
16074             el.setStyle("visibility", "visible");
16075             el.fadeIn({callback: afterShow});
16076         }else{
16077             afterShow();
16078         }
16079     };
16080     
16081     var afterShow = function(){
16082         if(ce){
16083             el.show();
16084             esc.enable();
16085             if(tm.autoDismiss && ce.autoHide !== false){
16086                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16087             }
16088         }
16089     };
16090     
16091     var hide = function(noanim){
16092         clearTimeout(dismissProc);
16093         clearTimeout(hideProc);
16094         ce = null;
16095         if(el.isVisible()){
16096             esc.disable();
16097             if(noanim !== true && tm.animate){
16098                 el.fadeOut({callback: afterHide});
16099             }else{
16100                 afterHide();
16101             } 
16102         }
16103     };
16104     
16105     var afterHide = function(){
16106         el.hide();
16107         if(removeCls){
16108             el.removeClass(removeCls);
16109             removeCls = null;
16110         }
16111     };
16112     
16113     return {
16114         /**
16115         * @cfg {Number} minWidth
16116         * The minimum width of the quick tip (defaults to 40)
16117         */
16118        minWidth : 40,
16119         /**
16120         * @cfg {Number} maxWidth
16121         * The maximum width of the quick tip (defaults to 300)
16122         */
16123        maxWidth : 300,
16124         /**
16125         * @cfg {Boolean} interceptTitles
16126         * True to automatically use the element's DOM title value if available (defaults to false)
16127         */
16128        interceptTitles : false,
16129         /**
16130         * @cfg {Boolean} trackMouse
16131         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16132         */
16133        trackMouse : false,
16134         /**
16135         * @cfg {Boolean} hideOnClick
16136         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16137         */
16138        hideOnClick : true,
16139         /**
16140         * @cfg {Number} showDelay
16141         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16142         */
16143        showDelay : 500,
16144         /**
16145         * @cfg {Number} hideDelay
16146         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16147         */
16148        hideDelay : 200,
16149         /**
16150         * @cfg {Boolean} autoHide
16151         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16152         * Used in conjunction with hideDelay.
16153         */
16154        autoHide : true,
16155         /**
16156         * @cfg {Boolean}
16157         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16158         * (defaults to true).  Used in conjunction with autoDismissDelay.
16159         */
16160        autoDismiss : true,
16161         /**
16162         * @cfg {Number}
16163         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16164         */
16165        autoDismissDelay : 5000,
16166        /**
16167         * @cfg {Boolean} animate
16168         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16169         */
16170        animate : false,
16171
16172        /**
16173         * @cfg {String} title
16174         * Title text to display (defaults to '').  This can be any valid HTML markup.
16175         */
16176         title: '',
16177        /**
16178         * @cfg {String} text
16179         * Body text to display (defaults to '').  This can be any valid HTML markup.
16180         */
16181         text : '',
16182        /**
16183         * @cfg {String} cls
16184         * A CSS class to apply to the base quick tip element (defaults to '').
16185         */
16186         cls : '',
16187        /**
16188         * @cfg {Number} width
16189         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16190         * minWidth or maxWidth.
16191         */
16192         width : null,
16193
16194     /**
16195      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16196      * or display QuickTips in a page.
16197      */
16198        init : function(){
16199           tm = Roo.QuickTips;
16200           cfg = tm.tagConfig;
16201           if(!inited){
16202               if(!Roo.isReady){ // allow calling of init() before onReady
16203                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16204                   return;
16205               }
16206               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16207               el.fxDefaults = {stopFx: true};
16208               // maximum custom styling
16209               //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>');
16210               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>');              
16211               tipTitle = el.child('h3');
16212               tipTitle.enableDisplayMode("block");
16213               tipBody = el.child('div.x-tip-bd');
16214               tipBodyText = el.child('div.x-tip-bd-inner');
16215               //bdLeft = el.child('div.x-tip-bd-left');
16216               //bdRight = el.child('div.x-tip-bd-right');
16217               close = el.child('div.x-tip-close');
16218               close.enableDisplayMode("block");
16219               close.on("click", hide);
16220               var d = Roo.get(document);
16221               d.on("mousedown", onDown);
16222               d.on("mouseover", onOver);
16223               d.on("mouseout", onOut);
16224               d.on("mousemove", onMove);
16225               esc = d.addKeyListener(27, hide);
16226               esc.disable();
16227               if(Roo.dd.DD){
16228                   dd = el.initDD("default", null, {
16229                       onDrag : function(){
16230                           el.sync();  
16231                       }
16232                   });
16233                   dd.setHandleElId(tipTitle.id);
16234                   dd.lock();
16235               }
16236               inited = true;
16237           }
16238           this.enable(); 
16239        },
16240
16241     /**
16242      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16243      * are supported:
16244      * <pre>
16245 Property    Type                   Description
16246 ----------  ---------------------  ------------------------------------------------------------------------
16247 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16248      * </ul>
16249      * @param {Object} config The config object
16250      */
16251        register : function(config){
16252            var cs = config instanceof Array ? config : arguments;
16253            for(var i = 0, len = cs.length; i < len; i++) {
16254                var c = cs[i];
16255                var target = c.target;
16256                if(target){
16257                    if(target instanceof Array){
16258                        for(var j = 0, jlen = target.length; j < jlen; j++){
16259                            tagEls[target[j]] = c;
16260                        }
16261                    }else{
16262                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16263                    }
16264                }
16265            }
16266        },
16267
16268     /**
16269      * Removes this quick tip from its element and destroys it.
16270      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16271      */
16272        unregister : function(el){
16273            delete tagEls[Roo.id(el)];
16274        },
16275
16276     /**
16277      * Enable this quick tip.
16278      */
16279        enable : function(){
16280            if(inited && disabled){
16281                locks.pop();
16282                if(locks.length < 1){
16283                    disabled = false;
16284                }
16285            }
16286        },
16287
16288     /**
16289      * Disable this quick tip.
16290      */
16291        disable : function(){
16292           disabled = true;
16293           clearTimeout(showProc);
16294           clearTimeout(hideProc);
16295           clearTimeout(dismissProc);
16296           if(ce){
16297               hide(true);
16298           }
16299           locks.push(1);
16300        },
16301
16302     /**
16303      * Returns true if the quick tip is enabled, else false.
16304      */
16305        isEnabled : function(){
16306             return !disabled;
16307        },
16308
16309         // private
16310        tagConfig : {
16311            namespace : "ext",
16312            attribute : "qtip",
16313            width : "width",
16314            target : "target",
16315            title : "qtitle",
16316            hide : "hide",
16317            cls : "qclass"
16318        }
16319    };
16320 }();
16321
16322 // backwards compat
16323 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16324  * Based on:
16325  * Ext JS Library 1.1.1
16326  * Copyright(c) 2006-2007, Ext JS, LLC.
16327  *
16328  * Originally Released Under LGPL - original licence link has changed is not relivant.
16329  *
16330  * Fork - LGPL
16331  * <script type="text/javascript">
16332  */
16333  
16334
16335 /**
16336  * @class Roo.tree.TreePanel
16337  * @extends Roo.data.Tree
16338
16339  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16340  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16341  * @cfg {Boolean} enableDD true to enable drag and drop
16342  * @cfg {Boolean} enableDrag true to enable just drag
16343  * @cfg {Boolean} enableDrop true to enable just drop
16344  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16345  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16346  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16347  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16348  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16349  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16350  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16351  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16352  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16353  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16354  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16355  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16356  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16357  * @cfg {Function} renderer 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>
16358  * @cfg {Function} rendererTip 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>
16359  * 
16360  * @constructor
16361  * @param {String/HTMLElement/Element} el The container element
16362  * @param {Object} config
16363  */
16364 Roo.tree.TreePanel = function(el, config){
16365     var root = false;
16366     var loader = false;
16367     if (config.root) {
16368         root = config.root;
16369         delete config.root;
16370     }
16371     if (config.loader) {
16372         loader = config.loader;
16373         delete config.loader;
16374     }
16375     
16376     Roo.apply(this, config);
16377     Roo.tree.TreePanel.superclass.constructor.call(this);
16378     this.el = Roo.get(el);
16379     this.el.addClass('x-tree');
16380     //console.log(root);
16381     if (root) {
16382         this.setRootNode( Roo.factory(root, Roo.tree));
16383     }
16384     if (loader) {
16385         this.loader = Roo.factory(loader, Roo.tree);
16386     }
16387    /**
16388     * Read-only. The id of the container element becomes this TreePanel's id.
16389     */
16390    this.id = this.el.id;
16391    this.addEvents({
16392         /**
16393         * @event beforeload
16394         * Fires before a node is loaded, return false to cancel
16395         * @param {Node} node The node being loaded
16396         */
16397         "beforeload" : true,
16398         /**
16399         * @event load
16400         * Fires when a node is loaded
16401         * @param {Node} node The node that was loaded
16402         */
16403         "load" : true,
16404         /**
16405         * @event textchange
16406         * Fires when the text for a node is changed
16407         * @param {Node} node The node
16408         * @param {String} text The new text
16409         * @param {String} oldText The old text
16410         */
16411         "textchange" : true,
16412         /**
16413         * @event beforeexpand
16414         * Fires before a node is expanded, return false to cancel.
16415         * @param {Node} node The node
16416         * @param {Boolean} deep
16417         * @param {Boolean} anim
16418         */
16419         "beforeexpand" : true,
16420         /**
16421         * @event beforecollapse
16422         * Fires before a node is collapsed, return false to cancel.
16423         * @param {Node} node The node
16424         * @param {Boolean} deep
16425         * @param {Boolean} anim
16426         */
16427         "beforecollapse" : true,
16428         /**
16429         * @event expand
16430         * Fires when a node is expanded
16431         * @param {Node} node The node
16432         */
16433         "expand" : true,
16434         /**
16435         * @event disabledchange
16436         * Fires when the disabled status of a node changes
16437         * @param {Node} node The node
16438         * @param {Boolean} disabled
16439         */
16440         "disabledchange" : true,
16441         /**
16442         * @event collapse
16443         * Fires when a node is collapsed
16444         * @param {Node} node The node
16445         */
16446         "collapse" : true,
16447         /**
16448         * @event beforeclick
16449         * Fires before click processing on a node. Return false to cancel the default action.
16450         * @param {Node} node The node
16451         * @param {Roo.EventObject} e The event object
16452         */
16453         "beforeclick":true,
16454         /**
16455         * @event checkchange
16456         * Fires when a node with a checkbox's checked property changes
16457         * @param {Node} this This node
16458         * @param {Boolean} checked
16459         */
16460         "checkchange":true,
16461         /**
16462         * @event click
16463         * Fires when a node is clicked
16464         * @param {Node} node The node
16465         * @param {Roo.EventObject} e The event object
16466         */
16467         "click":true,
16468         /**
16469         * @event dblclick
16470         * Fires when a node is double clicked
16471         * @param {Node} node The node
16472         * @param {Roo.EventObject} e The event object
16473         */
16474         "dblclick":true,
16475         /**
16476         * @event contextmenu
16477         * Fires when a node is right clicked
16478         * @param {Node} node The node
16479         * @param {Roo.EventObject} e The event object
16480         */
16481         "contextmenu":true,
16482         /**
16483         * @event beforechildrenrendered
16484         * Fires right before the child nodes for a node are rendered
16485         * @param {Node} node The node
16486         */
16487         "beforechildrenrendered":true,
16488        /**
16489              * @event startdrag
16490              * Fires when a node starts being dragged
16491              * @param {Roo.tree.TreePanel} this
16492              * @param {Roo.tree.TreeNode} node
16493              * @param {event} e The raw browser event
16494              */ 
16495             "startdrag" : true,
16496             /**
16497              * @event enddrag
16498              * Fires when a drag operation is complete
16499              * @param {Roo.tree.TreePanel} this
16500              * @param {Roo.tree.TreeNode} node
16501              * @param {event} e The raw browser event
16502              */
16503             "enddrag" : true,
16504             /**
16505              * @event dragdrop
16506              * Fires when a dragged node is dropped on a valid DD target
16507              * @param {Roo.tree.TreePanel} this
16508              * @param {Roo.tree.TreeNode} node
16509              * @param {DD} dd The dd it was dropped on
16510              * @param {event} e The raw browser event
16511              */
16512             "dragdrop" : true,
16513             /**
16514              * @event beforenodedrop
16515              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16516              * passed to handlers has the following properties:<br />
16517              * <ul style="padding:5px;padding-left:16px;">
16518              * <li>tree - The TreePanel</li>
16519              * <li>target - The node being targeted for the drop</li>
16520              * <li>data - The drag data from the drag source</li>
16521              * <li>point - The point of the drop - append, above or below</li>
16522              * <li>source - The drag source</li>
16523              * <li>rawEvent - Raw mouse event</li>
16524              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16525              * to be inserted by setting them on this object.</li>
16526              * <li>cancel - Set this to true to cancel the drop.</li>
16527              * </ul>
16528              * @param {Object} dropEvent
16529              */
16530             "beforenodedrop" : true,
16531             /**
16532              * @event nodedrop
16533              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16534              * passed to handlers has the following properties:<br />
16535              * <ul style="padding:5px;padding-left:16px;">
16536              * <li>tree - The TreePanel</li>
16537              * <li>target - The node being targeted for the drop</li>
16538              * <li>data - The drag data from the drag source</li>
16539              * <li>point - The point of the drop - append, above or below</li>
16540              * <li>source - The drag source</li>
16541              * <li>rawEvent - Raw mouse event</li>
16542              * <li>dropNode - Dropped node(s).</li>
16543              * </ul>
16544              * @param {Object} dropEvent
16545              */
16546             "nodedrop" : true,
16547              /**
16548              * @event nodedragover
16549              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16550              * passed to handlers has the following properties:<br />
16551              * <ul style="padding:5px;padding-left:16px;">
16552              * <li>tree - The TreePanel</li>
16553              * <li>target - The node being targeted for the drop</li>
16554              * <li>data - The drag data from the drag source</li>
16555              * <li>point - The point of the drop - append, above or below</li>
16556              * <li>source - The drag source</li>
16557              * <li>rawEvent - Raw mouse event</li>
16558              * <li>dropNode - Drop node(s) provided by the source.</li>
16559              * <li>cancel - Set this to true to signal drop not allowed.</li>
16560              * </ul>
16561              * @param {Object} dragOverEvent
16562              */
16563             "nodedragover" : true
16564         
16565    });
16566    if(this.singleExpand){
16567        this.on("beforeexpand", this.restrictExpand, this);
16568    }
16569 };
16570 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16571     rootVisible : true,
16572     animate: Roo.enableFx,
16573     lines : true,
16574     enableDD : false,
16575     hlDrop : Roo.enableFx,
16576   
16577     renderer: false,
16578     
16579     rendererTip: false,
16580     // private
16581     restrictExpand : function(node){
16582         var p = node.parentNode;
16583         if(p){
16584             if(p.expandedChild && p.expandedChild.parentNode == p){
16585                 p.expandedChild.collapse();
16586             }
16587             p.expandedChild = node;
16588         }
16589     },
16590
16591     // private override
16592     setRootNode : function(node){
16593         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16594         if(!this.rootVisible){
16595             node.ui = new Roo.tree.RootTreeNodeUI(node);
16596         }
16597         return node;
16598     },
16599
16600     /**
16601      * Returns the container element for this TreePanel
16602      */
16603     getEl : function(){
16604         return this.el;
16605     },
16606
16607     /**
16608      * Returns the default TreeLoader for this TreePanel
16609      */
16610     getLoader : function(){
16611         return this.loader;
16612     },
16613
16614     /**
16615      * Expand all nodes
16616      */
16617     expandAll : function(){
16618         this.root.expand(true);
16619     },
16620
16621     /**
16622      * Collapse all nodes
16623      */
16624     collapseAll : function(){
16625         this.root.collapse(true);
16626     },
16627
16628     /**
16629      * Returns the selection model used by this TreePanel
16630      */
16631     getSelectionModel : function(){
16632         if(!this.selModel){
16633             this.selModel = new Roo.tree.DefaultSelectionModel();
16634         }
16635         return this.selModel;
16636     },
16637
16638     /**
16639      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16640      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16641      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16642      * @return {Array}
16643      */
16644     getChecked : function(a, startNode){
16645         startNode = startNode || this.root;
16646         var r = [];
16647         var f = function(){
16648             if(this.attributes.checked){
16649                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16650             }
16651         }
16652         startNode.cascade(f);
16653         return r;
16654     },
16655
16656     /**
16657      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16658      * @param {String} path
16659      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16660      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16661      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16662      */
16663     expandPath : function(path, attr, callback){
16664         attr = attr || "id";
16665         var keys = path.split(this.pathSeparator);
16666         var curNode = this.root;
16667         if(curNode.attributes[attr] != keys[1]){ // invalid root
16668             if(callback){
16669                 callback(false, null);
16670             }
16671             return;
16672         }
16673         var index = 1;
16674         var f = function(){
16675             if(++index == keys.length){
16676                 if(callback){
16677                     callback(true, curNode);
16678                 }
16679                 return;
16680             }
16681             var c = curNode.findChild(attr, keys[index]);
16682             if(!c){
16683                 if(callback){
16684                     callback(false, curNode);
16685                 }
16686                 return;
16687             }
16688             curNode = c;
16689             c.expand(false, false, f);
16690         };
16691         curNode.expand(false, false, f);
16692     },
16693
16694     /**
16695      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16696      * @param {String} path
16697      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16698      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16699      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16700      */
16701     selectPath : function(path, attr, callback){
16702         attr = attr || "id";
16703         var keys = path.split(this.pathSeparator);
16704         var v = keys.pop();
16705         if(keys.length > 0){
16706             var f = function(success, node){
16707                 if(success && node){
16708                     var n = node.findChild(attr, v);
16709                     if(n){
16710                         n.select();
16711                         if(callback){
16712                             callback(true, n);
16713                         }
16714                     }else if(callback){
16715                         callback(false, n);
16716                     }
16717                 }else{
16718                     if(callback){
16719                         callback(false, n);
16720                     }
16721                 }
16722             };
16723             this.expandPath(keys.join(this.pathSeparator), attr, f);
16724         }else{
16725             this.root.select();
16726             if(callback){
16727                 callback(true, this.root);
16728             }
16729         }
16730     },
16731
16732     getTreeEl : function(){
16733         return this.el;
16734     },
16735
16736     /**
16737      * Trigger rendering of this TreePanel
16738      */
16739     render : function(){
16740         if (this.innerCt) {
16741             return this; // stop it rendering more than once!!
16742         }
16743         
16744         this.innerCt = this.el.createChild({tag:"ul",
16745                cls:"x-tree-root-ct " +
16746                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16747
16748         if(this.containerScroll){
16749             Roo.dd.ScrollManager.register(this.el);
16750         }
16751         if((this.enableDD || this.enableDrop) && !this.dropZone){
16752            /**
16753             * The dropZone used by this tree if drop is enabled
16754             * @type Roo.tree.TreeDropZone
16755             */
16756              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16757                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16758            });
16759         }
16760         if((this.enableDD || this.enableDrag) && !this.dragZone){
16761            /**
16762             * The dragZone used by this tree if drag is enabled
16763             * @type Roo.tree.TreeDragZone
16764             */
16765             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16766                ddGroup: this.ddGroup || "TreeDD",
16767                scroll: this.ddScroll
16768            });
16769         }
16770         this.getSelectionModel().init(this);
16771         if (!this.root) {
16772             console.log("ROOT not set in tree");
16773             return;
16774         }
16775         this.root.render();
16776         if(!this.rootVisible){
16777             this.root.renderChildren();
16778         }
16779         return this;
16780     }
16781 });/*
16782  * Based on:
16783  * Ext JS Library 1.1.1
16784  * Copyright(c) 2006-2007, Ext JS, LLC.
16785  *
16786  * Originally Released Under LGPL - original licence link has changed is not relivant.
16787  *
16788  * Fork - LGPL
16789  * <script type="text/javascript">
16790  */
16791  
16792
16793 /**
16794  * @class Roo.tree.DefaultSelectionModel
16795  * @extends Roo.util.Observable
16796  * The default single selection for a TreePanel.
16797  */
16798 Roo.tree.DefaultSelectionModel = function(){
16799    this.selNode = null;
16800    
16801    this.addEvents({
16802        /**
16803         * @event selectionchange
16804         * Fires when the selected node changes
16805         * @param {DefaultSelectionModel} this
16806         * @param {TreeNode} node the new selection
16807         */
16808        "selectionchange" : true,
16809
16810        /**
16811         * @event beforeselect
16812         * Fires before the selected node changes, return false to cancel the change
16813         * @param {DefaultSelectionModel} this
16814         * @param {TreeNode} node the new selection
16815         * @param {TreeNode} node the old selection
16816         */
16817        "beforeselect" : true
16818    });
16819 };
16820
16821 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16822     init : function(tree){
16823         this.tree = tree;
16824         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16825         tree.on("click", this.onNodeClick, this);
16826     },
16827     
16828     onNodeClick : function(node, e){
16829         if (e.ctrlKey && this.selNode == node)  {
16830             this.unselect(node);
16831             return;
16832         }
16833         this.select(node);
16834     },
16835     
16836     /**
16837      * Select a node.
16838      * @param {TreeNode} node The node to select
16839      * @return {TreeNode} The selected node
16840      */
16841     select : function(node){
16842         var last = this.selNode;
16843         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16844             if(last){
16845                 last.ui.onSelectedChange(false);
16846             }
16847             this.selNode = node;
16848             node.ui.onSelectedChange(true);
16849             this.fireEvent("selectionchange", this, node, last);
16850         }
16851         return node;
16852     },
16853     
16854     /**
16855      * Deselect a node.
16856      * @param {TreeNode} node The node to unselect
16857      */
16858     unselect : function(node){
16859         if(this.selNode == node){
16860             this.clearSelections();
16861         }    
16862     },
16863     
16864     /**
16865      * Clear all selections
16866      */
16867     clearSelections : function(){
16868         var n = this.selNode;
16869         if(n){
16870             n.ui.onSelectedChange(false);
16871             this.selNode = null;
16872             this.fireEvent("selectionchange", this, null);
16873         }
16874         return n;
16875     },
16876     
16877     /**
16878      * Get the selected node
16879      * @return {TreeNode} The selected node
16880      */
16881     getSelectedNode : function(){
16882         return this.selNode;    
16883     },
16884     
16885     /**
16886      * Returns true if the node is selected
16887      * @param {TreeNode} node The node to check
16888      * @return {Boolean}
16889      */
16890     isSelected : function(node){
16891         return this.selNode == node;  
16892     },
16893
16894     /**
16895      * Selects the node above the selected node in the tree, intelligently walking the nodes
16896      * @return TreeNode The new selection
16897      */
16898     selectPrevious : function(){
16899         var s = this.selNode || this.lastSelNode;
16900         if(!s){
16901             return null;
16902         }
16903         var ps = s.previousSibling;
16904         if(ps){
16905             if(!ps.isExpanded() || ps.childNodes.length < 1){
16906                 return this.select(ps);
16907             } else{
16908                 var lc = ps.lastChild;
16909                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16910                     lc = lc.lastChild;
16911                 }
16912                 return this.select(lc);
16913             }
16914         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16915             return this.select(s.parentNode);
16916         }
16917         return null;
16918     },
16919
16920     /**
16921      * Selects the node above the selected node in the tree, intelligently walking the nodes
16922      * @return TreeNode The new selection
16923      */
16924     selectNext : function(){
16925         var s = this.selNode || this.lastSelNode;
16926         if(!s){
16927             return null;
16928         }
16929         if(s.firstChild && s.isExpanded()){
16930              return this.select(s.firstChild);
16931          }else if(s.nextSibling){
16932              return this.select(s.nextSibling);
16933          }else if(s.parentNode){
16934             var newS = null;
16935             s.parentNode.bubble(function(){
16936                 if(this.nextSibling){
16937                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16938                     return false;
16939                 }
16940             });
16941             return newS;
16942          }
16943         return null;
16944     },
16945
16946     onKeyDown : function(e){
16947         var s = this.selNode || this.lastSelNode;
16948         // undesirable, but required
16949         var sm = this;
16950         if(!s){
16951             return;
16952         }
16953         var k = e.getKey();
16954         switch(k){
16955              case e.DOWN:
16956                  e.stopEvent();
16957                  this.selectNext();
16958              break;
16959              case e.UP:
16960                  e.stopEvent();
16961                  this.selectPrevious();
16962              break;
16963              case e.RIGHT:
16964                  e.preventDefault();
16965                  if(s.hasChildNodes()){
16966                      if(!s.isExpanded()){
16967                          s.expand();
16968                      }else if(s.firstChild){
16969                          this.select(s.firstChild, e);
16970                      }
16971                  }
16972              break;
16973              case e.LEFT:
16974                  e.preventDefault();
16975                  if(s.hasChildNodes() && s.isExpanded()){
16976                      s.collapse();
16977                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16978                      this.select(s.parentNode, e);
16979                  }
16980              break;
16981         };
16982     }
16983 });
16984
16985 /**
16986  * @class Roo.tree.MultiSelectionModel
16987  * @extends Roo.util.Observable
16988  * Multi selection for a TreePanel.
16989  */
16990 Roo.tree.MultiSelectionModel = function(){
16991    this.selNodes = [];
16992    this.selMap = {};
16993    this.addEvents({
16994        /**
16995         * @event selectionchange
16996         * Fires when the selected nodes change
16997         * @param {MultiSelectionModel} this
16998         * @param {Array} nodes Array of the selected nodes
16999         */
17000        "selectionchange" : true
17001    });
17002 };
17003
17004 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17005     init : function(tree){
17006         this.tree = tree;
17007         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17008         tree.on("click", this.onNodeClick, this);
17009     },
17010     
17011     onNodeClick : function(node, e){
17012         this.select(node, e, e.ctrlKey);
17013     },
17014     
17015     /**
17016      * Select a node.
17017      * @param {TreeNode} node The node to select
17018      * @param {EventObject} e (optional) An event associated with the selection
17019      * @param {Boolean} keepExisting True to retain existing selections
17020      * @return {TreeNode} The selected node
17021      */
17022     select : function(node, e, keepExisting){
17023         if(keepExisting !== true){
17024             this.clearSelections(true);
17025         }
17026         if(this.isSelected(node)){
17027             this.lastSelNode = node;
17028             return node;
17029         }
17030         this.selNodes.push(node);
17031         this.selMap[node.id] = node;
17032         this.lastSelNode = node;
17033         node.ui.onSelectedChange(true);
17034         this.fireEvent("selectionchange", this, this.selNodes);
17035         return node;
17036     },
17037     
17038     /**
17039      * Deselect a node.
17040      * @param {TreeNode} node The node to unselect
17041      */
17042     unselect : function(node){
17043         if(this.selMap[node.id]){
17044             node.ui.onSelectedChange(false);
17045             var sn = this.selNodes;
17046             var index = -1;
17047             if(sn.indexOf){
17048                 index = sn.indexOf(node);
17049             }else{
17050                 for(var i = 0, len = sn.length; i < len; i++){
17051                     if(sn[i] == node){
17052                         index = i;
17053                         break;
17054                     }
17055                 }
17056             }
17057             if(index != -1){
17058                 this.selNodes.splice(index, 1);
17059             }
17060             delete this.selMap[node.id];
17061             this.fireEvent("selectionchange", this, this.selNodes);
17062         }
17063     },
17064     
17065     /**
17066      * Clear all selections
17067      */
17068     clearSelections : function(suppressEvent){
17069         var sn = this.selNodes;
17070         if(sn.length > 0){
17071             for(var i = 0, len = sn.length; i < len; i++){
17072                 sn[i].ui.onSelectedChange(false);
17073             }
17074             this.selNodes = [];
17075             this.selMap = {};
17076             if(suppressEvent !== true){
17077                 this.fireEvent("selectionchange", this, this.selNodes);
17078             }
17079         }
17080     },
17081     
17082     /**
17083      * Returns true if the node is selected
17084      * @param {TreeNode} node The node to check
17085      * @return {Boolean}
17086      */
17087     isSelected : function(node){
17088         return this.selMap[node.id] ? true : false;  
17089     },
17090     
17091     /**
17092      * Returns an array of the selected nodes
17093      * @return {Array}
17094      */
17095     getSelectedNodes : function(){
17096         return this.selNodes;    
17097     },
17098
17099     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17100
17101     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17102
17103     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17104 });/*
17105  * Based on:
17106  * Ext JS Library 1.1.1
17107  * Copyright(c) 2006-2007, Ext JS, LLC.
17108  *
17109  * Originally Released Under LGPL - original licence link has changed is not relivant.
17110  *
17111  * Fork - LGPL
17112  * <script type="text/javascript">
17113  */
17114  
17115 /**
17116  * @class Roo.tree.TreeNode
17117  * @extends Roo.data.Node
17118  * @cfg {String} text The text for this node
17119  * @cfg {Boolean} expanded true to start the node expanded
17120  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17121  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17122  * @cfg {Boolean} disabled true to start the node disabled
17123  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17124  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17125  * @cfg {String} cls A css class to be added to the node
17126  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17127  * @cfg {String} href URL of the link used for the node (defaults to #)
17128  * @cfg {String} hrefTarget target frame for the link
17129  * @cfg {String} qtip An Ext QuickTip for the node
17130  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17131  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17132  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17133  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17134  * (defaults to undefined with no checkbox rendered)
17135  * @constructor
17136  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17137  */
17138 Roo.tree.TreeNode = function(attributes){
17139     attributes = attributes || {};
17140     if(typeof attributes == "string"){
17141         attributes = {text: attributes};
17142     }
17143     this.childrenRendered = false;
17144     this.rendered = false;
17145     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17146     this.expanded = attributes.expanded === true;
17147     this.isTarget = attributes.isTarget !== false;
17148     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17149     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17150
17151     /**
17152      * Read-only. The text for this node. To change it use setText().
17153      * @type String
17154      */
17155     this.text = attributes.text;
17156     /**
17157      * True if this node is disabled.
17158      * @type Boolean
17159      */
17160     this.disabled = attributes.disabled === true;
17161
17162     this.addEvents({
17163         /**
17164         * @event textchange
17165         * Fires when the text for this node is changed
17166         * @param {Node} this This node
17167         * @param {String} text The new text
17168         * @param {String} oldText The old text
17169         */
17170         "textchange" : true,
17171         /**
17172         * @event beforeexpand
17173         * Fires before this node is expanded, return false to cancel.
17174         * @param {Node} this This node
17175         * @param {Boolean} deep
17176         * @param {Boolean} anim
17177         */
17178         "beforeexpand" : true,
17179         /**
17180         * @event beforecollapse
17181         * Fires before this node is collapsed, return false to cancel.
17182         * @param {Node} this This node
17183         * @param {Boolean} deep
17184         * @param {Boolean} anim
17185         */
17186         "beforecollapse" : true,
17187         /**
17188         * @event expand
17189         * Fires when this node is expanded
17190         * @param {Node} this This node
17191         */
17192         "expand" : true,
17193         /**
17194         * @event disabledchange
17195         * Fires when the disabled status of this node changes
17196         * @param {Node} this This node
17197         * @param {Boolean} disabled
17198         */
17199         "disabledchange" : true,
17200         /**
17201         * @event collapse
17202         * Fires when this node is collapsed
17203         * @param {Node} this This node
17204         */
17205         "collapse" : true,
17206         /**
17207         * @event beforeclick
17208         * Fires before click processing. Return false to cancel the default action.
17209         * @param {Node} this This node
17210         * @param {Roo.EventObject} e The event object
17211         */
17212         "beforeclick":true,
17213         /**
17214         * @event checkchange
17215         * Fires when a node with a checkbox's checked property changes
17216         * @param {Node} this This node
17217         * @param {Boolean} checked
17218         */
17219         "checkchange":true,
17220         /**
17221         * @event click
17222         * Fires when this node is clicked
17223         * @param {Node} this This node
17224         * @param {Roo.EventObject} e The event object
17225         */
17226         "click":true,
17227         /**
17228         * @event dblclick
17229         * Fires when this node is double clicked
17230         * @param {Node} this This node
17231         * @param {Roo.EventObject} e The event object
17232         */
17233         "dblclick":true,
17234         /**
17235         * @event contextmenu
17236         * Fires when this node is right clicked
17237         * @param {Node} this This node
17238         * @param {Roo.EventObject} e The event object
17239         */
17240         "contextmenu":true,
17241         /**
17242         * @event beforechildrenrendered
17243         * Fires right before the child nodes for this node are rendered
17244         * @param {Node} this This node
17245         */
17246         "beforechildrenrendered":true
17247     });
17248
17249     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17250
17251     /**
17252      * Read-only. The UI for this node
17253      * @type TreeNodeUI
17254      */
17255     this.ui = new uiClass(this);
17256 };
17257 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17258     preventHScroll: true,
17259     /**
17260      * Returns true if this node is expanded
17261      * @return {Boolean}
17262      */
17263     isExpanded : function(){
17264         return this.expanded;
17265     },
17266
17267     /**
17268      * Returns the UI object for this node
17269      * @return {TreeNodeUI}
17270      */
17271     getUI : function(){
17272         return this.ui;
17273     },
17274
17275     // private override
17276     setFirstChild : function(node){
17277         var of = this.firstChild;
17278         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17279         if(this.childrenRendered && of && node != of){
17280             of.renderIndent(true, true);
17281         }
17282         if(this.rendered){
17283             this.renderIndent(true, true);
17284         }
17285     },
17286
17287     // private override
17288     setLastChild : function(node){
17289         var ol = this.lastChild;
17290         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17291         if(this.childrenRendered && ol && node != ol){
17292             ol.renderIndent(true, true);
17293         }
17294         if(this.rendered){
17295             this.renderIndent(true, true);
17296         }
17297     },
17298
17299     // these methods are overridden to provide lazy rendering support
17300     // private override
17301     appendChild : function(){
17302         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17303         if(node && this.childrenRendered){
17304             node.render();
17305         }
17306         this.ui.updateExpandIcon();
17307         return node;
17308     },
17309
17310     // private override
17311     removeChild : function(node){
17312         this.ownerTree.getSelectionModel().unselect(node);
17313         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17314         // if it's been rendered remove dom node
17315         if(this.childrenRendered){
17316             node.ui.remove();
17317         }
17318         if(this.childNodes.length < 1){
17319             this.collapse(false, false);
17320         }else{
17321             this.ui.updateExpandIcon();
17322         }
17323         if(!this.firstChild) {
17324             this.childrenRendered = false;
17325         }
17326         return node;
17327     },
17328
17329     // private override
17330     insertBefore : function(node, refNode){
17331         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17332         if(newNode && refNode && this.childrenRendered){
17333             node.render();
17334         }
17335         this.ui.updateExpandIcon();
17336         return newNode;
17337     },
17338
17339     /**
17340      * Sets the text for this node
17341      * @param {String} text
17342      */
17343     setText : function(text){
17344         var oldText = this.text;
17345         this.text = text;
17346         this.attributes.text = text;
17347         if(this.rendered){ // event without subscribing
17348             this.ui.onTextChange(this, text, oldText);
17349         }
17350         this.fireEvent("textchange", this, text, oldText);
17351     },
17352
17353     /**
17354      * Triggers selection of this node
17355      */
17356     select : function(){
17357         this.getOwnerTree().getSelectionModel().select(this);
17358     },
17359
17360     /**
17361      * Triggers deselection of this node
17362      */
17363     unselect : function(){
17364         this.getOwnerTree().getSelectionModel().unselect(this);
17365     },
17366
17367     /**
17368      * Returns true if this node is selected
17369      * @return {Boolean}
17370      */
17371     isSelected : function(){
17372         return this.getOwnerTree().getSelectionModel().isSelected(this);
17373     },
17374
17375     /**
17376      * Expand this node.
17377      * @param {Boolean} deep (optional) True to expand all children as well
17378      * @param {Boolean} anim (optional) false to cancel the default animation
17379      * @param {Function} callback (optional) A callback to be called when
17380      * expanding this node completes (does not wait for deep expand to complete).
17381      * Called with 1 parameter, this node.
17382      */
17383     expand : function(deep, anim, callback){
17384         if(!this.expanded){
17385             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17386                 return;
17387             }
17388             if(!this.childrenRendered){
17389                 this.renderChildren();
17390             }
17391             this.expanded = true;
17392             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17393                 this.ui.animExpand(function(){
17394                     this.fireEvent("expand", this);
17395                     if(typeof callback == "function"){
17396                         callback(this);
17397                     }
17398                     if(deep === true){
17399                         this.expandChildNodes(true);
17400                     }
17401                 }.createDelegate(this));
17402                 return;
17403             }else{
17404                 this.ui.expand();
17405                 this.fireEvent("expand", this);
17406                 if(typeof callback == "function"){
17407                     callback(this);
17408                 }
17409             }
17410         }else{
17411            if(typeof callback == "function"){
17412                callback(this);
17413            }
17414         }
17415         if(deep === true){
17416             this.expandChildNodes(true);
17417         }
17418     },
17419
17420     isHiddenRoot : function(){
17421         return this.isRoot && !this.getOwnerTree().rootVisible;
17422     },
17423
17424     /**
17425      * Collapse this node.
17426      * @param {Boolean} deep (optional) True to collapse all children as well
17427      * @param {Boolean} anim (optional) false to cancel the default animation
17428      */
17429     collapse : function(deep, anim){
17430         if(this.expanded && !this.isHiddenRoot()){
17431             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17432                 return;
17433             }
17434             this.expanded = false;
17435             if((this.getOwnerTree().animate && anim !== false) || anim){
17436                 this.ui.animCollapse(function(){
17437                     this.fireEvent("collapse", this);
17438                     if(deep === true){
17439                         this.collapseChildNodes(true);
17440                     }
17441                 }.createDelegate(this));
17442                 return;
17443             }else{
17444                 this.ui.collapse();
17445                 this.fireEvent("collapse", this);
17446             }
17447         }
17448         if(deep === true){
17449             var cs = this.childNodes;
17450             for(var i = 0, len = cs.length; i < len; i++) {
17451                 cs[i].collapse(true, false);
17452             }
17453         }
17454     },
17455
17456     // private
17457     delayedExpand : function(delay){
17458         if(!this.expandProcId){
17459             this.expandProcId = this.expand.defer(delay, this);
17460         }
17461     },
17462
17463     // private
17464     cancelExpand : function(){
17465         if(this.expandProcId){
17466             clearTimeout(this.expandProcId);
17467         }
17468         this.expandProcId = false;
17469     },
17470
17471     /**
17472      * Toggles expanded/collapsed state of the node
17473      */
17474     toggle : function(){
17475         if(this.expanded){
17476             this.collapse();
17477         }else{
17478             this.expand();
17479         }
17480     },
17481
17482     /**
17483      * Ensures all parent nodes are expanded
17484      */
17485     ensureVisible : function(callback){
17486         var tree = this.getOwnerTree();
17487         tree.expandPath(this.parentNode.getPath(), false, function(){
17488             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17489             Roo.callback(callback);
17490         }.createDelegate(this));
17491     },
17492
17493     /**
17494      * Expand all child nodes
17495      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17496      */
17497     expandChildNodes : function(deep){
17498         var cs = this.childNodes;
17499         for(var i = 0, len = cs.length; i < len; i++) {
17500                 cs[i].expand(deep);
17501         }
17502     },
17503
17504     /**
17505      * Collapse all child nodes
17506      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17507      */
17508     collapseChildNodes : function(deep){
17509         var cs = this.childNodes;
17510         for(var i = 0, len = cs.length; i < len; i++) {
17511                 cs[i].collapse(deep);
17512         }
17513     },
17514
17515     /**
17516      * Disables this node
17517      */
17518     disable : function(){
17519         this.disabled = true;
17520         this.unselect();
17521         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17522             this.ui.onDisableChange(this, true);
17523         }
17524         this.fireEvent("disabledchange", this, true);
17525     },
17526
17527     /**
17528      * Enables this node
17529      */
17530     enable : function(){
17531         this.disabled = false;
17532         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17533             this.ui.onDisableChange(this, false);
17534         }
17535         this.fireEvent("disabledchange", this, false);
17536     },
17537
17538     // private
17539     renderChildren : function(suppressEvent){
17540         if(suppressEvent !== false){
17541             this.fireEvent("beforechildrenrendered", this);
17542         }
17543         var cs = this.childNodes;
17544         for(var i = 0, len = cs.length; i < len; i++){
17545             cs[i].render(true);
17546         }
17547         this.childrenRendered = true;
17548     },
17549
17550     // private
17551     sort : function(fn, scope){
17552         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17553         if(this.childrenRendered){
17554             var cs = this.childNodes;
17555             for(var i = 0, len = cs.length; i < len; i++){
17556                 cs[i].render(true);
17557             }
17558         }
17559     },
17560
17561     // private
17562     render : function(bulkRender){
17563         this.ui.render(bulkRender);
17564         if(!this.rendered){
17565             this.rendered = true;
17566             if(this.expanded){
17567                 this.expanded = false;
17568                 this.expand(false, false);
17569             }
17570         }
17571     },
17572
17573     // private
17574     renderIndent : function(deep, refresh){
17575         if(refresh){
17576             this.ui.childIndent = null;
17577         }
17578         this.ui.renderIndent();
17579         if(deep === true && this.childrenRendered){
17580             var cs = this.childNodes;
17581             for(var i = 0, len = cs.length; i < len; i++){
17582                 cs[i].renderIndent(true, refresh);
17583             }
17584         }
17585     }
17586 });/*
17587  * Based on:
17588  * Ext JS Library 1.1.1
17589  * Copyright(c) 2006-2007, Ext JS, LLC.
17590  *
17591  * Originally Released Under LGPL - original licence link has changed is not relivant.
17592  *
17593  * Fork - LGPL
17594  * <script type="text/javascript">
17595  */
17596  
17597 /**
17598  * @class Roo.tree.AsyncTreeNode
17599  * @extends Roo.tree.TreeNode
17600  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17601  * @constructor
17602  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17603  */
17604  Roo.tree.AsyncTreeNode = function(config){
17605     this.loaded = false;
17606     this.loading = false;
17607     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17608     /**
17609     * @event beforeload
17610     * Fires before this node is loaded, return false to cancel
17611     * @param {Node} this This node
17612     */
17613     this.addEvents({'beforeload':true, 'load': true});
17614     /**
17615     * @event load
17616     * Fires when this node is loaded
17617     * @param {Node} this This node
17618     */
17619     /**
17620      * The loader used by this node (defaults to using the tree's defined loader)
17621      * @type TreeLoader
17622      * @property loader
17623      */
17624 };
17625 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17626     expand : function(deep, anim, callback){
17627         if(this.loading){ // if an async load is already running, waiting til it's done
17628             var timer;
17629             var f = function(){
17630                 if(!this.loading){ // done loading
17631                     clearInterval(timer);
17632                     this.expand(deep, anim, callback);
17633                 }
17634             }.createDelegate(this);
17635             timer = setInterval(f, 200);
17636             return;
17637         }
17638         if(!this.loaded){
17639             if(this.fireEvent("beforeload", this) === false){
17640                 return;
17641             }
17642             this.loading = true;
17643             this.ui.beforeLoad(this);
17644             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17645             if(loader){
17646                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17647                 return;
17648             }
17649         }
17650         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17651     },
17652     
17653     /**
17654      * Returns true if this node is currently loading
17655      * @return {Boolean}
17656      */
17657     isLoading : function(){
17658         return this.loading;  
17659     },
17660     
17661     loadComplete : function(deep, anim, callback){
17662         this.loading = false;
17663         this.loaded = true;
17664         this.ui.afterLoad(this);
17665         this.fireEvent("load", this);
17666         this.expand(deep, anim, callback);
17667     },
17668     
17669     /**
17670      * Returns true if this node has been loaded
17671      * @return {Boolean}
17672      */
17673     isLoaded : function(){
17674         return this.loaded;
17675     },
17676     
17677     hasChildNodes : function(){
17678         if(!this.isLeaf() && !this.loaded){
17679             return true;
17680         }else{
17681             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17682         }
17683     },
17684
17685     /**
17686      * Trigger a reload for this node
17687      * @param {Function} callback
17688      */
17689     reload : function(callback){
17690         this.collapse(false, false);
17691         while(this.firstChild){
17692             this.removeChild(this.firstChild);
17693         }
17694         this.childrenRendered = false;
17695         this.loaded = false;
17696         if(this.isHiddenRoot()){
17697             this.expanded = false;
17698         }
17699         this.expand(false, false, callback);
17700     }
17701 });/*
17702  * Based on:
17703  * Ext JS Library 1.1.1
17704  * Copyright(c) 2006-2007, Ext JS, LLC.
17705  *
17706  * Originally Released Under LGPL - original licence link has changed is not relivant.
17707  *
17708  * Fork - LGPL
17709  * <script type="text/javascript">
17710  */
17711  
17712 /**
17713  * @class Roo.tree.TreeNodeUI
17714  * @constructor
17715  * @param {Object} node The node to render
17716  * The TreeNode UI implementation is separate from the
17717  * tree implementation. Unless you are customizing the tree UI,
17718  * you should never have to use this directly.
17719  */
17720 Roo.tree.TreeNodeUI = function(node){
17721     this.node = node;
17722     this.rendered = false;
17723     this.animating = false;
17724     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17725 };
17726
17727 Roo.tree.TreeNodeUI.prototype = {
17728     removeChild : function(node){
17729         if(this.rendered){
17730             this.ctNode.removeChild(node.ui.getEl());
17731         }
17732     },
17733
17734     beforeLoad : function(){
17735          this.addClass("x-tree-node-loading");
17736     },
17737
17738     afterLoad : function(){
17739          this.removeClass("x-tree-node-loading");
17740     },
17741
17742     onTextChange : function(node, text, oldText){
17743         if(this.rendered){
17744             this.textNode.innerHTML = text;
17745         }
17746     },
17747
17748     onDisableChange : function(node, state){
17749         this.disabled = state;
17750         if(state){
17751             this.addClass("x-tree-node-disabled");
17752         }else{
17753             this.removeClass("x-tree-node-disabled");
17754         }
17755     },
17756
17757     onSelectedChange : function(state){
17758         if(state){
17759             this.focus();
17760             this.addClass("x-tree-selected");
17761         }else{
17762             //this.blur();
17763             this.removeClass("x-tree-selected");
17764         }
17765     },
17766
17767     onMove : function(tree, node, oldParent, newParent, index, refNode){
17768         this.childIndent = null;
17769         if(this.rendered){
17770             var targetNode = newParent.ui.getContainer();
17771             if(!targetNode){//target not rendered
17772                 this.holder = document.createElement("div");
17773                 this.holder.appendChild(this.wrap);
17774                 return;
17775             }
17776             var insertBefore = refNode ? refNode.ui.getEl() : null;
17777             if(insertBefore){
17778                 targetNode.insertBefore(this.wrap, insertBefore);
17779             }else{
17780                 targetNode.appendChild(this.wrap);
17781             }
17782             this.node.renderIndent(true);
17783         }
17784     },
17785
17786     addClass : function(cls){
17787         if(this.elNode){
17788             Roo.fly(this.elNode).addClass(cls);
17789         }
17790     },
17791
17792     removeClass : function(cls){
17793         if(this.elNode){
17794             Roo.fly(this.elNode).removeClass(cls);
17795         }
17796     },
17797
17798     remove : function(){
17799         if(this.rendered){
17800             this.holder = document.createElement("div");
17801             this.holder.appendChild(this.wrap);
17802         }
17803     },
17804
17805     fireEvent : function(){
17806         return this.node.fireEvent.apply(this.node, arguments);
17807     },
17808
17809     initEvents : function(){
17810         this.node.on("move", this.onMove, this);
17811         var E = Roo.EventManager;
17812         var a = this.anchor;
17813
17814         var el = Roo.fly(a, '_treeui');
17815
17816         if(Roo.isOpera){ // opera render bug ignores the CSS
17817             el.setStyle("text-decoration", "none");
17818         }
17819
17820         el.on("click", this.onClick, this);
17821         el.on("dblclick", this.onDblClick, this);
17822
17823         if(this.checkbox){
17824             Roo.EventManager.on(this.checkbox,
17825                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17826         }
17827
17828         el.on("contextmenu", this.onContextMenu, this);
17829
17830         var icon = Roo.fly(this.iconNode);
17831         icon.on("click", this.onClick, this);
17832         icon.on("dblclick", this.onDblClick, this);
17833         icon.on("contextmenu", this.onContextMenu, this);
17834         E.on(this.ecNode, "click", this.ecClick, this, true);
17835
17836         if(this.node.disabled){
17837             this.addClass("x-tree-node-disabled");
17838         }
17839         if(this.node.hidden){
17840             this.addClass("x-tree-node-disabled");
17841         }
17842         var ot = this.node.getOwnerTree();
17843         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17844         if(dd && (!this.node.isRoot || ot.rootVisible)){
17845             Roo.dd.Registry.register(this.elNode, {
17846                 node: this.node,
17847                 handles: this.getDDHandles(),
17848                 isHandle: false
17849             });
17850         }
17851     },
17852
17853     getDDHandles : function(){
17854         return [this.iconNode, this.textNode];
17855     },
17856
17857     hide : function(){
17858         if(this.rendered){
17859             this.wrap.style.display = "none";
17860         }
17861     },
17862
17863     show : function(){
17864         if(this.rendered){
17865             this.wrap.style.display = "";
17866         }
17867     },
17868
17869     onContextMenu : function(e){
17870         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17871             e.preventDefault();
17872             this.focus();
17873             this.fireEvent("contextmenu", this.node, e);
17874         }
17875     },
17876
17877     onClick : function(e){
17878         if(this.dropping){
17879             e.stopEvent();
17880             return;
17881         }
17882         if(this.fireEvent("beforeclick", this.node, e) !== false){
17883             if(!this.disabled && this.node.attributes.href){
17884                 this.fireEvent("click", this.node, e);
17885                 return;
17886             }
17887             e.preventDefault();
17888             if(this.disabled){
17889                 return;
17890             }
17891
17892             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17893                 this.node.toggle();
17894             }
17895
17896             this.fireEvent("click", this.node, e);
17897         }else{
17898             e.stopEvent();
17899         }
17900     },
17901
17902     onDblClick : function(e){
17903         e.preventDefault();
17904         if(this.disabled){
17905             return;
17906         }
17907         if(this.checkbox){
17908             this.toggleCheck();
17909         }
17910         if(!this.animating && this.node.hasChildNodes()){
17911             this.node.toggle();
17912         }
17913         this.fireEvent("dblclick", this.node, e);
17914     },
17915
17916     onCheckChange : function(){
17917         var checked = this.checkbox.checked;
17918         this.node.attributes.checked = checked;
17919         this.fireEvent('checkchange', this.node, checked);
17920     },
17921
17922     ecClick : function(e){
17923         if(!this.animating && this.node.hasChildNodes()){
17924             this.node.toggle();
17925         }
17926     },
17927
17928     startDrop : function(){
17929         this.dropping = true;
17930     },
17931
17932     // delayed drop so the click event doesn't get fired on a drop
17933     endDrop : function(){
17934        setTimeout(function(){
17935            this.dropping = false;
17936        }.createDelegate(this), 50);
17937     },
17938
17939     expand : function(){
17940         this.updateExpandIcon();
17941         this.ctNode.style.display = "";
17942     },
17943
17944     focus : function(){
17945         if(!this.node.preventHScroll){
17946             try{this.anchor.focus();
17947             }catch(e){}
17948         }else if(!Roo.isIE){
17949             try{
17950                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17951                 var l = noscroll.scrollLeft;
17952                 this.anchor.focus();
17953                 noscroll.scrollLeft = l;
17954             }catch(e){}
17955         }
17956     },
17957
17958     toggleCheck : function(value){
17959         var cb = this.checkbox;
17960         if(cb){
17961             cb.checked = (value === undefined ? !cb.checked : value);
17962         }
17963     },
17964
17965     blur : function(){
17966         try{
17967             this.anchor.blur();
17968         }catch(e){}
17969     },
17970
17971     animExpand : function(callback){
17972         var ct = Roo.get(this.ctNode);
17973         ct.stopFx();
17974         if(!this.node.hasChildNodes()){
17975             this.updateExpandIcon();
17976             this.ctNode.style.display = "";
17977             Roo.callback(callback);
17978             return;
17979         }
17980         this.animating = true;
17981         this.updateExpandIcon();
17982
17983         ct.slideIn('t', {
17984            callback : function(){
17985                this.animating = false;
17986                Roo.callback(callback);
17987             },
17988             scope: this,
17989             duration: this.node.ownerTree.duration || .25
17990         });
17991     },
17992
17993     highlight : function(){
17994         var tree = this.node.getOwnerTree();
17995         Roo.fly(this.wrap).highlight(
17996             tree.hlColor || "C3DAF9",
17997             {endColor: tree.hlBaseColor}
17998         );
17999     },
18000
18001     collapse : function(){
18002         this.updateExpandIcon();
18003         this.ctNode.style.display = "none";
18004     },
18005
18006     animCollapse : function(callback){
18007         var ct = Roo.get(this.ctNode);
18008         ct.enableDisplayMode('block');
18009         ct.stopFx();
18010
18011         this.animating = true;
18012         this.updateExpandIcon();
18013
18014         ct.slideOut('t', {
18015             callback : function(){
18016                this.animating = false;
18017                Roo.callback(callback);
18018             },
18019             scope: this,
18020             duration: this.node.ownerTree.duration || .25
18021         });
18022     },
18023
18024     getContainer : function(){
18025         return this.ctNode;
18026     },
18027
18028     getEl : function(){
18029         return this.wrap;
18030     },
18031
18032     appendDDGhost : function(ghostNode){
18033         ghostNode.appendChild(this.elNode.cloneNode(true));
18034     },
18035
18036     getDDRepairXY : function(){
18037         return Roo.lib.Dom.getXY(this.iconNode);
18038     },
18039
18040     onRender : function(){
18041         this.render();
18042     },
18043
18044     render : function(bulkRender){
18045         var n = this.node, a = n.attributes;
18046         var targetNode = n.parentNode ?
18047               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18048
18049         if(!this.rendered){
18050             this.rendered = true;
18051
18052             this.renderElements(n, a, targetNode, bulkRender);
18053
18054             if(a.qtip){
18055                if(this.textNode.setAttributeNS){
18056                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18057                    if(a.qtipTitle){
18058                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18059                    }
18060                }else{
18061                    this.textNode.setAttribute("ext:qtip", a.qtip);
18062                    if(a.qtipTitle){
18063                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18064                    }
18065                }
18066             }else if(a.qtipCfg){
18067                 a.qtipCfg.target = Roo.id(this.textNode);
18068                 Roo.QuickTips.register(a.qtipCfg);
18069             }
18070             this.initEvents();
18071             if(!this.node.expanded){
18072                 this.updateExpandIcon();
18073             }
18074         }else{
18075             if(bulkRender === true) {
18076                 targetNode.appendChild(this.wrap);
18077             }
18078         }
18079     },
18080
18081     renderElements : function(n, a, targetNode, bulkRender){
18082         // add some indent caching, this helps performance when rendering a large tree
18083         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18084         var t = n.getOwnerTree();
18085         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18086         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18087         var cb = typeof a.checked == 'boolean';
18088         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18089         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18090             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18091             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18092             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18093             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18094             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18095              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18096                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18097             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18098             "</li>"];
18099
18100         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18101             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18102                                 n.nextSibling.ui.getEl(), buf.join(""));
18103         }else{
18104             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18105         }
18106
18107         this.elNode = this.wrap.childNodes[0];
18108         this.ctNode = this.wrap.childNodes[1];
18109         var cs = this.elNode.childNodes;
18110         this.indentNode = cs[0];
18111         this.ecNode = cs[1];
18112         this.iconNode = cs[2];
18113         var index = 3;
18114         if(cb){
18115             this.checkbox = cs[3];
18116             index++;
18117         }
18118         this.anchor = cs[index];
18119         this.textNode = cs[index].firstChild;
18120     },
18121
18122     getAnchor : function(){
18123         return this.anchor;
18124     },
18125
18126     getTextEl : function(){
18127         return this.textNode;
18128     },
18129
18130     getIconEl : function(){
18131         return this.iconNode;
18132     },
18133
18134     isChecked : function(){
18135         return this.checkbox ? this.checkbox.checked : false;
18136     },
18137
18138     updateExpandIcon : function(){
18139         if(this.rendered){
18140             var n = this.node, c1, c2;
18141             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18142             var hasChild = n.hasChildNodes();
18143             if(hasChild){
18144                 if(n.expanded){
18145                     cls += "-minus";
18146                     c1 = "x-tree-node-collapsed";
18147                     c2 = "x-tree-node-expanded";
18148                 }else{
18149                     cls += "-plus";
18150                     c1 = "x-tree-node-expanded";
18151                     c2 = "x-tree-node-collapsed";
18152                 }
18153                 if(this.wasLeaf){
18154                     this.removeClass("x-tree-node-leaf");
18155                     this.wasLeaf = false;
18156                 }
18157                 if(this.c1 != c1 || this.c2 != c2){
18158                     Roo.fly(this.elNode).replaceClass(c1, c2);
18159                     this.c1 = c1; this.c2 = c2;
18160                 }
18161             }else{
18162                 if(!this.wasLeaf){
18163                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18164                     delete this.c1;
18165                     delete this.c2;
18166                     this.wasLeaf = true;
18167                 }
18168             }
18169             var ecc = "x-tree-ec-icon "+cls;
18170             if(this.ecc != ecc){
18171                 this.ecNode.className = ecc;
18172                 this.ecc = ecc;
18173             }
18174         }
18175     },
18176
18177     getChildIndent : function(){
18178         if(!this.childIndent){
18179             var buf = [];
18180             var p = this.node;
18181             while(p){
18182                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18183                     if(!p.isLast()) {
18184                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18185                     } else {
18186                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18187                     }
18188                 }
18189                 p = p.parentNode;
18190             }
18191             this.childIndent = buf.join("");
18192         }
18193         return this.childIndent;
18194     },
18195
18196     renderIndent : function(){
18197         if(this.rendered){
18198             var indent = "";
18199             var p = this.node.parentNode;
18200             if(p){
18201                 indent = p.ui.getChildIndent();
18202             }
18203             if(this.indentMarkup != indent){ // don't rerender if not required
18204                 this.indentNode.innerHTML = indent;
18205                 this.indentMarkup = indent;
18206             }
18207             this.updateExpandIcon();
18208         }
18209     }
18210 };
18211
18212 Roo.tree.RootTreeNodeUI = function(){
18213     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18214 };
18215 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18216     render : function(){
18217         if(!this.rendered){
18218             var targetNode = this.node.ownerTree.innerCt.dom;
18219             this.node.expanded = true;
18220             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18221             this.wrap = this.ctNode = targetNode.firstChild;
18222         }
18223     },
18224     collapse : function(){
18225     },
18226     expand : function(){
18227     }
18228 });/*
18229  * Based on:
18230  * Ext JS Library 1.1.1
18231  * Copyright(c) 2006-2007, Ext JS, LLC.
18232  *
18233  * Originally Released Under LGPL - original licence link has changed is not relivant.
18234  *
18235  * Fork - LGPL
18236  * <script type="text/javascript">
18237  */
18238 /**
18239  * @class Roo.tree.TreeLoader
18240  * @extends Roo.util.Observable
18241  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18242  * nodes from a specified URL. The response must be a javascript Array definition
18243  * who's elements are node definition objects. eg:
18244  * <pre><code>
18245    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18246     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18247 </code></pre>
18248  * <br><br>
18249  * A server request is sent, and child nodes are loaded only when a node is expanded.
18250  * The loading node's id is passed to the server under the parameter name "node" to
18251  * enable the server to produce the correct child nodes.
18252  * <br><br>
18253  * To pass extra parameters, an event handler may be attached to the "beforeload"
18254  * event, and the parameters specified in the TreeLoader's baseParams property:
18255  * <pre><code>
18256     myTreeLoader.on("beforeload", function(treeLoader, node) {
18257         this.baseParams.category = node.attributes.category;
18258     }, this);
18259 </code></pre><
18260  * This would pass an HTTP parameter called "category" to the server containing
18261  * the value of the Node's "category" attribute.
18262  * @constructor
18263  * Creates a new Treeloader.
18264  * @param {Object} config A config object containing config properties.
18265  */
18266 Roo.tree.TreeLoader = function(config){
18267     this.baseParams = {};
18268     this.requestMethod = "POST";
18269     Roo.apply(this, config);
18270
18271     this.addEvents({
18272     
18273         /**
18274          * @event beforeload
18275          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18276          * @param {Object} This TreeLoader object.
18277          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18278          * @param {Object} callback The callback function specified in the {@link #load} call.
18279          */
18280         beforeload : true,
18281         /**
18282          * @event load
18283          * Fires when the node has been successfuly loaded.
18284          * @param {Object} This TreeLoader object.
18285          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18286          * @param {Object} response The response object containing the data from the server.
18287          */
18288         load : true,
18289         /**
18290          * @event loadexception
18291          * Fires if the network request failed.
18292          * @param {Object} This TreeLoader object.
18293          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18294          * @param {Object} response The response object containing the data from the server.
18295          */
18296         loadexception : true,
18297         /**
18298          * @event create
18299          * Fires before a node is created, enabling you to return custom Node types 
18300          * @param {Object} This TreeLoader object.
18301          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18302          */
18303         create : true
18304     });
18305
18306     Roo.tree.TreeLoader.superclass.constructor.call(this);
18307 };
18308
18309 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18310     /**
18311     * @cfg {String} dataUrl The URL from which to request a Json string which
18312     * specifies an array of node definition object representing the child nodes
18313     * to be loaded.
18314     */
18315     /**
18316     * @cfg {Object} baseParams (optional) An object containing properties which
18317     * specify HTTP parameters to be passed to each request for child nodes.
18318     */
18319     /**
18320     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18321     * created by this loader. If the attributes sent by the server have an attribute in this object,
18322     * they take priority.
18323     */
18324     /**
18325     * @cfg {Object} uiProviders (optional) An object containing properties which
18326     * 
18327     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18328     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18329     * <i>uiProvider</i> attribute of a returned child node is a string rather
18330     * than a reference to a TreeNodeUI implementation, this that string value
18331     * is used as a property name in the uiProviders object. You can define the provider named
18332     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18333     */
18334     uiProviders : {},
18335
18336     /**
18337     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18338     * child nodes before loading.
18339     */
18340     clearOnLoad : true,
18341
18342     /**
18343     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18344     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18345     * Grid query { data : [ .....] }
18346     */
18347     
18348     root : false,
18349      /**
18350     * @cfg {String} queryParam (optional) 
18351     * Name of the query as it will be passed on the querystring (defaults to 'node')
18352     * eg. the request will be ?node=[id]
18353     */
18354     
18355     
18356     queryParam: false,
18357     
18358     /**
18359      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18360      * This is called automatically when a node is expanded, but may be used to reload
18361      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18362      * @param {Roo.tree.TreeNode} node
18363      * @param {Function} callback
18364      */
18365     load : function(node, callback){
18366         if(this.clearOnLoad){
18367             while(node.firstChild){
18368                 node.removeChild(node.firstChild);
18369             }
18370         }
18371         if(node.attributes.children){ // preloaded json children
18372             var cs = node.attributes.children;
18373             for(var i = 0, len = cs.length; i < len; i++){
18374                 node.appendChild(this.createNode(cs[i]));
18375             }
18376             if(typeof callback == "function"){
18377                 callback();
18378             }
18379         }else if(this.dataUrl){
18380             this.requestData(node, callback);
18381         }
18382     },
18383
18384     getParams: function(node){
18385         var buf = [], bp = this.baseParams;
18386         for(var key in bp){
18387             if(typeof bp[key] != "function"){
18388                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18389             }
18390         }
18391         var n = this.queryParam === false ? 'node' : this.queryParam;
18392         buf.push(n + "=", encodeURIComponent(node.id));
18393         return buf.join("");
18394     },
18395
18396     requestData : function(node, callback){
18397         if(this.fireEvent("beforeload", this, node, callback) !== false){
18398             this.transId = Roo.Ajax.request({
18399                 method:this.requestMethod,
18400                 url: this.dataUrl||this.url,
18401                 success: this.handleResponse,
18402                 failure: this.handleFailure,
18403                 scope: this,
18404                 argument: {callback: callback, node: node},
18405                 params: this.getParams(node)
18406             });
18407         }else{
18408             // if the load is cancelled, make sure we notify
18409             // the node that we are done
18410             if(typeof callback == "function"){
18411                 callback();
18412             }
18413         }
18414     },
18415
18416     isLoading : function(){
18417         return this.transId ? true : false;
18418     },
18419
18420     abort : function(){
18421         if(this.isLoading()){
18422             Roo.Ajax.abort(this.transId);
18423         }
18424     },
18425
18426     // private
18427     createNode : function(attr){
18428         // apply baseAttrs, nice idea Corey!
18429         if(this.baseAttrs){
18430             Roo.applyIf(attr, this.baseAttrs);
18431         }
18432         if(this.applyLoader !== false){
18433             attr.loader = this;
18434         }
18435         // uiProvider = depreciated..
18436         
18437         if(typeof(attr.uiProvider) == 'string'){
18438            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18439                 /**  eval:var:attr */ eval(attr.uiProvider);
18440         }
18441         if(typeof(this.uiProviders['default']) != 'undefined') {
18442             attr.uiProvider = this.uiProviders['default'];
18443         }
18444         
18445         this.fireEvent('create', this, attr);
18446         
18447         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18448         return(attr.leaf ?
18449                         new Roo.tree.TreeNode(attr) :
18450                         new Roo.tree.AsyncTreeNode(attr));
18451     },
18452
18453     processResponse : function(response, node, callback){
18454         var json = response.responseText;
18455         try {
18456             
18457             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18458             if (this.root !== false) {
18459                 o = o[this.root];
18460             }
18461             
18462             for(var i = 0, len = o.length; i < len; i++){
18463                 var n = this.createNode(o[i]);
18464                 if(n){
18465                     node.appendChild(n);
18466                 }
18467             }
18468             if(typeof callback == "function"){
18469                 callback(this, node);
18470             }
18471         }catch(e){
18472             this.handleFailure(response);
18473         }
18474     },
18475
18476     handleResponse : function(response){
18477         this.transId = false;
18478         var a = response.argument;
18479         this.processResponse(response, a.node, a.callback);
18480         this.fireEvent("load", this, a.node, response);
18481     },
18482
18483     handleFailure : function(response){
18484         this.transId = false;
18485         var a = response.argument;
18486         this.fireEvent("loadexception", this, a.node, response);
18487         if(typeof a.callback == "function"){
18488             a.callback(this, a.node);
18489         }
18490     }
18491 });/*
18492  * Based on:
18493  * Ext JS Library 1.1.1
18494  * Copyright(c) 2006-2007, Ext JS, LLC.
18495  *
18496  * Originally Released Under LGPL - original licence link has changed is not relivant.
18497  *
18498  * Fork - LGPL
18499  * <script type="text/javascript">
18500  */
18501
18502 /**
18503 * @class Roo.tree.TreeFilter
18504 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18505 * @param {TreePanel} tree
18506 * @param {Object} config (optional)
18507  */
18508 Roo.tree.TreeFilter = function(tree, config){
18509     this.tree = tree;
18510     this.filtered = {};
18511     Roo.apply(this, config);
18512 };
18513
18514 Roo.tree.TreeFilter.prototype = {
18515     clearBlank:false,
18516     reverse:false,
18517     autoClear:false,
18518     remove:false,
18519
18520      /**
18521      * Filter the data by a specific attribute.
18522      * @param {String/RegExp} value Either string that the attribute value
18523      * should start with or a RegExp to test against the attribute
18524      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18525      * @param {TreeNode} startNode (optional) The node to start the filter at.
18526      */
18527     filter : function(value, attr, startNode){
18528         attr = attr || "text";
18529         var f;
18530         if(typeof value == "string"){
18531             var vlen = value.length;
18532             // auto clear empty filter
18533             if(vlen == 0 && this.clearBlank){
18534                 this.clear();
18535                 return;
18536             }
18537             value = value.toLowerCase();
18538             f = function(n){
18539                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18540             };
18541         }else if(value.exec){ // regex?
18542             f = function(n){
18543                 return value.test(n.attributes[attr]);
18544             };
18545         }else{
18546             throw 'Illegal filter type, must be string or regex';
18547         }
18548         this.filterBy(f, null, startNode);
18549         },
18550
18551     /**
18552      * Filter by a function. The passed function will be called with each
18553      * node in the tree (or from the startNode). If the function returns true, the node is kept
18554      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18555      * @param {Function} fn The filter function
18556      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18557      */
18558     filterBy : function(fn, scope, startNode){
18559         startNode = startNode || this.tree.root;
18560         if(this.autoClear){
18561             this.clear();
18562         }
18563         var af = this.filtered, rv = this.reverse;
18564         var f = function(n){
18565             if(n == startNode){
18566                 return true;
18567             }
18568             if(af[n.id]){
18569                 return false;
18570             }
18571             var m = fn.call(scope || n, n);
18572             if(!m || rv){
18573                 af[n.id] = n;
18574                 n.ui.hide();
18575                 return false;
18576             }
18577             return true;
18578         };
18579         startNode.cascade(f);
18580         if(this.remove){
18581            for(var id in af){
18582                if(typeof id != "function"){
18583                    var n = af[id];
18584                    if(n && n.parentNode){
18585                        n.parentNode.removeChild(n);
18586                    }
18587                }
18588            }
18589         }
18590     },
18591
18592     /**
18593      * Clears the current filter. Note: with the "remove" option
18594      * set a filter cannot be cleared.
18595      */
18596     clear : function(){
18597         var t = this.tree;
18598         var af = this.filtered;
18599         for(var id in af){
18600             if(typeof id != "function"){
18601                 var n = af[id];
18602                 if(n){
18603                     n.ui.show();
18604                 }
18605             }
18606         }
18607         this.filtered = {};
18608     }
18609 };
18610 /*
18611  * Based on:
18612  * Ext JS Library 1.1.1
18613  * Copyright(c) 2006-2007, Ext JS, LLC.
18614  *
18615  * Originally Released Under LGPL - original licence link has changed is not relivant.
18616  *
18617  * Fork - LGPL
18618  * <script type="text/javascript">
18619  */
18620  
18621
18622 /**
18623  * @class Roo.tree.TreeSorter
18624  * Provides sorting of nodes in a TreePanel
18625  * 
18626  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18627  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18628  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18629  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18630  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18631  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18632  * @constructor
18633  * @param {TreePanel} tree
18634  * @param {Object} config
18635  */
18636 Roo.tree.TreeSorter = function(tree, config){
18637     Roo.apply(this, config);
18638     tree.on("beforechildrenrendered", this.doSort, this);
18639     tree.on("append", this.updateSort, this);
18640     tree.on("insert", this.updateSort, this);
18641     
18642     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18643     var p = this.property || "text";
18644     var sortType = this.sortType;
18645     var fs = this.folderSort;
18646     var cs = this.caseSensitive === true;
18647     var leafAttr = this.leafAttr || 'leaf';
18648
18649     this.sortFn = function(n1, n2){
18650         if(fs){
18651             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18652                 return 1;
18653             }
18654             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18655                 return -1;
18656             }
18657         }
18658         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18659         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18660         if(v1 < v2){
18661                         return dsc ? +1 : -1;
18662                 }else if(v1 > v2){
18663                         return dsc ? -1 : +1;
18664         }else{
18665                 return 0;
18666         }
18667     };
18668 };
18669
18670 Roo.tree.TreeSorter.prototype = {
18671     doSort : function(node){
18672         node.sort(this.sortFn);
18673     },
18674     
18675     compareNodes : function(n1, n2){
18676         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18677     },
18678     
18679     updateSort : function(tree, node){
18680         if(node.childrenRendered){
18681             this.doSort.defer(1, this, [node]);
18682         }
18683     }
18684 };/*
18685  * Based on:
18686  * Ext JS Library 1.1.1
18687  * Copyright(c) 2006-2007, Ext JS, LLC.
18688  *
18689  * Originally Released Under LGPL - original licence link has changed is not relivant.
18690  *
18691  * Fork - LGPL
18692  * <script type="text/javascript">
18693  */
18694
18695 if(Roo.dd.DropZone){
18696     
18697 Roo.tree.TreeDropZone = function(tree, config){
18698     this.allowParentInsert = false;
18699     this.allowContainerDrop = false;
18700     this.appendOnly = false;
18701     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18702     this.tree = tree;
18703     this.lastInsertClass = "x-tree-no-status";
18704     this.dragOverData = {};
18705 };
18706
18707 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18708     ddGroup : "TreeDD",
18709     
18710     expandDelay : 1000,
18711     
18712     expandNode : function(node){
18713         if(node.hasChildNodes() && !node.isExpanded()){
18714             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18715         }
18716     },
18717     
18718     queueExpand : function(node){
18719         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18720     },
18721     
18722     cancelExpand : function(){
18723         if(this.expandProcId){
18724             clearTimeout(this.expandProcId);
18725             this.expandProcId = false;
18726         }
18727     },
18728     
18729     isValidDropPoint : function(n, pt, dd, e, data){
18730         if(!n || !data){ return false; }
18731         var targetNode = n.node;
18732         var dropNode = data.node;
18733         // default drop rules
18734         if(!(targetNode && targetNode.isTarget && pt)){
18735             return false;
18736         }
18737         if(pt == "append" && targetNode.allowChildren === false){
18738             return false;
18739         }
18740         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18741             return false;
18742         }
18743         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18744             return false;
18745         }
18746         // reuse the object
18747         var overEvent = this.dragOverData;
18748         overEvent.tree = this.tree;
18749         overEvent.target = targetNode;
18750         overEvent.data = data;
18751         overEvent.point = pt;
18752         overEvent.source = dd;
18753         overEvent.rawEvent = e;
18754         overEvent.dropNode = dropNode;
18755         overEvent.cancel = false;  
18756         var result = this.tree.fireEvent("nodedragover", overEvent);
18757         return overEvent.cancel === false && result !== false;
18758     },
18759     
18760     getDropPoint : function(e, n, dd){
18761         var tn = n.node;
18762         if(tn.isRoot){
18763             return tn.allowChildren !== false ? "append" : false; // always append for root
18764         }
18765         var dragEl = n.ddel;
18766         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18767         var y = Roo.lib.Event.getPageY(e);
18768         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18769         
18770         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18771         var noAppend = tn.allowChildren === false;
18772         if(this.appendOnly || tn.parentNode.allowChildren === false){
18773             return noAppend ? false : "append";
18774         }
18775         var noBelow = false;
18776         if(!this.allowParentInsert){
18777             noBelow = tn.hasChildNodes() && tn.isExpanded();
18778         }
18779         var q = (b - t) / (noAppend ? 2 : 3);
18780         if(y >= t && y < (t + q)){
18781             return "above";
18782         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18783             return "below";
18784         }else{
18785             return "append";
18786         }
18787     },
18788     
18789     onNodeEnter : function(n, dd, e, data){
18790         this.cancelExpand();
18791     },
18792     
18793     onNodeOver : function(n, dd, e, data){
18794         var pt = this.getDropPoint(e, n, dd);
18795         var node = n.node;
18796         
18797         // auto node expand check
18798         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18799             this.queueExpand(node);
18800         }else if(pt != "append"){
18801             this.cancelExpand();
18802         }
18803         
18804         // set the insert point style on the target node
18805         var returnCls = this.dropNotAllowed;
18806         if(this.isValidDropPoint(n, pt, dd, e, data)){
18807            if(pt){
18808                var el = n.ddel;
18809                var cls;
18810                if(pt == "above"){
18811                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18812                    cls = "x-tree-drag-insert-above";
18813                }else if(pt == "below"){
18814                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18815                    cls = "x-tree-drag-insert-below";
18816                }else{
18817                    returnCls = "x-tree-drop-ok-append";
18818                    cls = "x-tree-drag-append";
18819                }
18820                if(this.lastInsertClass != cls){
18821                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18822                    this.lastInsertClass = cls;
18823                }
18824            }
18825        }
18826        return returnCls;
18827     },
18828     
18829     onNodeOut : function(n, dd, e, data){
18830         this.cancelExpand();
18831         this.removeDropIndicators(n);
18832     },
18833     
18834     onNodeDrop : function(n, dd, e, data){
18835         var point = this.getDropPoint(e, n, dd);
18836         var targetNode = n.node;
18837         targetNode.ui.startDrop();
18838         if(!this.isValidDropPoint(n, point, dd, e, data)){
18839             targetNode.ui.endDrop();
18840             return false;
18841         }
18842         // first try to find the drop node
18843         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18844         var dropEvent = {
18845             tree : this.tree,
18846             target: targetNode,
18847             data: data,
18848             point: point,
18849             source: dd,
18850             rawEvent: e,
18851             dropNode: dropNode,
18852             cancel: !dropNode   
18853         };
18854         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18855         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18856             targetNode.ui.endDrop();
18857             return false;
18858         }
18859         // allow target changing
18860         targetNode = dropEvent.target;
18861         if(point == "append" && !targetNode.isExpanded()){
18862             targetNode.expand(false, null, function(){
18863                 this.completeDrop(dropEvent);
18864             }.createDelegate(this));
18865         }else{
18866             this.completeDrop(dropEvent);
18867         }
18868         return true;
18869     },
18870     
18871     completeDrop : function(de){
18872         var ns = de.dropNode, p = de.point, t = de.target;
18873         if(!(ns instanceof Array)){
18874             ns = [ns];
18875         }
18876         var n;
18877         for(var i = 0, len = ns.length; i < len; i++){
18878             n = ns[i];
18879             if(p == "above"){
18880                 t.parentNode.insertBefore(n, t);
18881             }else if(p == "below"){
18882                 t.parentNode.insertBefore(n, t.nextSibling);
18883             }else{
18884                 t.appendChild(n);
18885             }
18886         }
18887         n.ui.focus();
18888         if(this.tree.hlDrop){
18889             n.ui.highlight();
18890         }
18891         t.ui.endDrop();
18892         this.tree.fireEvent("nodedrop", de);
18893     },
18894     
18895     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18896         if(this.tree.hlDrop){
18897             dropNode.ui.focus();
18898             dropNode.ui.highlight();
18899         }
18900         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18901     },
18902     
18903     getTree : function(){
18904         return this.tree;
18905     },
18906     
18907     removeDropIndicators : function(n){
18908         if(n && n.ddel){
18909             var el = n.ddel;
18910             Roo.fly(el).removeClass([
18911                     "x-tree-drag-insert-above",
18912                     "x-tree-drag-insert-below",
18913                     "x-tree-drag-append"]);
18914             this.lastInsertClass = "_noclass";
18915         }
18916     },
18917     
18918     beforeDragDrop : function(target, e, id){
18919         this.cancelExpand();
18920         return true;
18921     },
18922     
18923     afterRepair : function(data){
18924         if(data && Roo.enableFx){
18925             data.node.ui.highlight();
18926         }
18927         this.hideProxy();
18928     }    
18929 });
18930
18931 }
18932 /*
18933  * Based on:
18934  * Ext JS Library 1.1.1
18935  * Copyright(c) 2006-2007, Ext JS, LLC.
18936  *
18937  * Originally Released Under LGPL - original licence link has changed is not relivant.
18938  *
18939  * Fork - LGPL
18940  * <script type="text/javascript">
18941  */
18942  
18943
18944 if(Roo.dd.DragZone){
18945 Roo.tree.TreeDragZone = function(tree, config){
18946     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18947     this.tree = tree;
18948 };
18949
18950 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18951     ddGroup : "TreeDD",
18952     
18953     onBeforeDrag : function(data, e){
18954         var n = data.node;
18955         return n && n.draggable && !n.disabled;
18956     },
18957     
18958     onInitDrag : function(e){
18959         var data = this.dragData;
18960         this.tree.getSelectionModel().select(data.node);
18961         this.proxy.update("");
18962         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18963         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18964     },
18965     
18966     getRepairXY : function(e, data){
18967         return data.node.ui.getDDRepairXY();
18968     },
18969     
18970     onEndDrag : function(data, e){
18971         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18972     },
18973     
18974     onValidDrop : function(dd, e, id){
18975         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18976         this.hideProxy();
18977     },
18978     
18979     beforeInvalidDrop : function(e, id){
18980         // this scrolls the original position back into view
18981         var sm = this.tree.getSelectionModel();
18982         sm.clearSelections();
18983         sm.select(this.dragData.node);
18984     }
18985 });
18986 }/*
18987  * Based on:
18988  * Ext JS Library 1.1.1
18989  * Copyright(c) 2006-2007, Ext JS, LLC.
18990  *
18991  * Originally Released Under LGPL - original licence link has changed is not relivant.
18992  *
18993  * Fork - LGPL
18994  * <script type="text/javascript">
18995  */
18996 /**
18997  * @class Roo.tree.TreeEditor
18998  * @extends Roo.Editor
18999  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19000  * as the editor field.
19001  * @constructor
19002  * @param {TreePanel} tree
19003  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19004  */
19005 Roo.tree.TreeEditor = function(tree, config){
19006     config = config || {};
19007     var field = config.events ? config : new Roo.form.TextField(config);
19008     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
19009
19010     this.tree = tree;
19011
19012     tree.on('beforeclick', this.beforeNodeClick, this);
19013     tree.getTreeEl().on('mousedown', this.hide, this);
19014     this.on('complete', this.updateNode, this);
19015     this.on('beforestartedit', this.fitToTree, this);
19016     this.on('startedit', this.bindScroll, this, {delay:10});
19017     this.on('specialkey', this.onSpecialKey, this);
19018 };
19019
19020 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19021     /**
19022      * @cfg {String} alignment
19023      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19024      */
19025     alignment: "l-l",
19026     // inherit
19027     autoSize: false,
19028     /**
19029      * @cfg {Boolean} hideEl
19030      * True to hide the bound element while the editor is displayed (defaults to false)
19031      */
19032     hideEl : false,
19033     /**
19034      * @cfg {String} cls
19035      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19036      */
19037     cls: "x-small-editor x-tree-editor",
19038     /**
19039      * @cfg {Boolean} shim
19040      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19041      */
19042     shim:false,
19043     // inherit
19044     shadow:"frame",
19045     /**
19046      * @cfg {Number} maxWidth
19047      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19048      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19049      * scroll and client offsets into account prior to each edit.
19050      */
19051     maxWidth: 250,
19052
19053     editDelay : 350,
19054
19055     // private
19056     fitToTree : function(ed, el){
19057         var td = this.tree.getTreeEl().dom, nd = el.dom;
19058         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19059             td.scrollLeft = nd.offsetLeft;
19060         }
19061         var w = Math.min(
19062                 this.maxWidth,
19063                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19064         this.setSize(w, '');
19065     },
19066
19067     // private
19068     triggerEdit : function(node){
19069         this.completeEdit();
19070         this.editNode = node;
19071         this.startEdit(node.ui.textNode, node.text);
19072     },
19073
19074     // private
19075     bindScroll : function(){
19076         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19077     },
19078
19079     // private
19080     beforeNodeClick : function(node, e){
19081         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19082         this.lastClick = new Date();
19083         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19084             e.stopEvent();
19085             this.triggerEdit(node);
19086             return false;
19087         }
19088     },
19089
19090     // private
19091     updateNode : function(ed, value){
19092         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19093         this.editNode.setText(value);
19094     },
19095
19096     // private
19097     onHide : function(){
19098         Roo.tree.TreeEditor.superclass.onHide.call(this);
19099         if(this.editNode){
19100             this.editNode.ui.focus();
19101         }
19102     },
19103
19104     // private
19105     onSpecialKey : function(field, e){
19106         var k = e.getKey();
19107         if(k == e.ESC){
19108             e.stopEvent();
19109             this.cancelEdit();
19110         }else if(k == e.ENTER && !e.hasModifier()){
19111             e.stopEvent();
19112             this.completeEdit();
19113         }
19114     }
19115 });//<Script type="text/javascript">
19116 /*
19117  * Based on:
19118  * Ext JS Library 1.1.1
19119  * Copyright(c) 2006-2007, Ext JS, LLC.
19120  *
19121  * Originally Released Under LGPL - original licence link has changed is not relivant.
19122  *
19123  * Fork - LGPL
19124  * <script type="text/javascript">
19125  */
19126  
19127 /**
19128  * Not documented??? - probably should be...
19129  */
19130
19131 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19132     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19133     
19134     renderElements : function(n, a, targetNode, bulkRender){
19135         //consel.log("renderElements?");
19136         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19137
19138         var t = n.getOwnerTree();
19139         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19140         
19141         var cols = t.columns;
19142         var bw = t.borderWidth;
19143         var c = cols[0];
19144         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19145          var cb = typeof a.checked == "boolean";
19146         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19147         var colcls = 'x-t-' + tid + '-c0';
19148         var buf = [
19149             '<li class="x-tree-node">',
19150             
19151                 
19152                 '<div class="x-tree-node-el ', a.cls,'">',
19153                     // extran...
19154                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19155                 
19156                 
19157                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19158                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19159                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19160                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19161                            (a.iconCls ? ' '+a.iconCls : ''),
19162                            '" unselectable="on" />',
19163                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19164                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19165                              
19166                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19167                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19168                             '<span unselectable="on" qtip="' + tx + '">',
19169                              tx,
19170                              '</span></a>' ,
19171                     '</div>',
19172                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19173                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19174                  ];
19175         for(var i = 1, len = cols.length; i < len; i++){
19176             c = cols[i];
19177             colcls = 'x-t-' + tid + '-c' +i;
19178             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19179             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19180                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19181                       "</div>");
19182          }
19183          
19184          buf.push(
19185             '</a>',
19186             '<div class="x-clear"></div></div>',
19187             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19188             "</li>");
19189         
19190         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19191             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19192                                 n.nextSibling.ui.getEl(), buf.join(""));
19193         }else{
19194             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19195         }
19196         var el = this.wrap.firstChild;
19197         this.elRow = el;
19198         this.elNode = el.firstChild;
19199         this.ranchor = el.childNodes[1];
19200         this.ctNode = this.wrap.childNodes[1];
19201         var cs = el.firstChild.childNodes;
19202         this.indentNode = cs[0];
19203         this.ecNode = cs[1];
19204         this.iconNode = cs[2];
19205         var index = 3;
19206         if(cb){
19207             this.checkbox = cs[3];
19208             index++;
19209         }
19210         this.anchor = cs[index];
19211         
19212         this.textNode = cs[index].firstChild;
19213         
19214         //el.on("click", this.onClick, this);
19215         //el.on("dblclick", this.onDblClick, this);
19216         
19217         
19218        // console.log(this);
19219     },
19220     initEvents : function(){
19221         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19222         
19223             
19224         var a = this.ranchor;
19225
19226         var el = Roo.get(a);
19227
19228         if(Roo.isOpera){ // opera render bug ignores the CSS
19229             el.setStyle("text-decoration", "none");
19230         }
19231
19232         el.on("click", this.onClick, this);
19233         el.on("dblclick", this.onDblClick, this);
19234         el.on("contextmenu", this.onContextMenu, this);
19235         
19236     },
19237     
19238     /*onSelectedChange : function(state){
19239         if(state){
19240             this.focus();
19241             this.addClass("x-tree-selected");
19242         }else{
19243             //this.blur();
19244             this.removeClass("x-tree-selected");
19245         }
19246     },*/
19247     addClass : function(cls){
19248         if(this.elRow){
19249             Roo.fly(this.elRow).addClass(cls);
19250         }
19251         
19252     },
19253     
19254     
19255     removeClass : function(cls){
19256         if(this.elRow){
19257             Roo.fly(this.elRow).removeClass(cls);
19258         }
19259     }
19260
19261     
19262     
19263 });//<Script type="text/javascript">
19264
19265 /*
19266  * Based on:
19267  * Ext JS Library 1.1.1
19268  * Copyright(c) 2006-2007, Ext JS, LLC.
19269  *
19270  * Originally Released Under LGPL - original licence link has changed is not relivant.
19271  *
19272  * Fork - LGPL
19273  * <script type="text/javascript">
19274  */
19275  
19276
19277 /**
19278  * @class Roo.tree.ColumnTree
19279  * @extends Roo.data.TreePanel
19280  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19281  * @cfg {int} borderWidth  compined right/left border allowance
19282  * @constructor
19283  * @param {String/HTMLElement/Element} el The container element
19284  * @param {Object} config
19285  */
19286 Roo.tree.ColumnTree =  function(el, config)
19287 {
19288    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19289    this.addEvents({
19290         /**
19291         * @event resize
19292         * Fire this event on a container when it resizes
19293         * @param {int} w Width
19294         * @param {int} h Height
19295         */
19296        "resize" : true
19297     });
19298     this.on('resize', this.onResize, this);
19299 };
19300
19301 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19302     //lines:false,
19303     
19304     
19305     borderWidth: Roo.isBorderBox ? 0 : 2, 
19306     headEls : false,
19307     
19308     render : function(){
19309         // add the header.....
19310        
19311         Roo.tree.ColumnTree.superclass.render.apply(this);
19312         
19313         this.el.addClass('x-column-tree');
19314         
19315         this.headers = this.el.createChild(
19316             {cls:'x-tree-headers'},this.innerCt.dom);
19317    
19318         var cols = this.columns, c;
19319         var totalWidth = 0;
19320         this.headEls = [];
19321         var  len = cols.length;
19322         for(var i = 0; i < len; i++){
19323              c = cols[i];
19324              totalWidth += c.width;
19325             this.headEls.push(this.headers.createChild({
19326                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19327                  cn: {
19328                      cls:'x-tree-hd-text',
19329                      html: c.header
19330                  },
19331                  style:'width:'+(c.width-this.borderWidth)+'px;'
19332              }));
19333         }
19334         this.headers.createChild({cls:'x-clear'});
19335         // prevent floats from wrapping when clipped
19336         this.headers.setWidth(totalWidth);
19337         //this.innerCt.setWidth(totalWidth);
19338         this.innerCt.setStyle({ overflow: 'auto' });
19339         this.onResize(this.width, this.height);
19340              
19341         
19342     },
19343     onResize : function(w,h)
19344     {
19345         this.height = h;
19346         this.width = w;
19347         // resize cols..
19348         this.innerCt.setWidth(this.width);
19349         this.innerCt.setHeight(this.height-20);
19350         
19351         // headers...
19352         var cols = this.columns, c;
19353         var totalWidth = 0;
19354         var expEl = false;
19355         var len = cols.length;
19356         for(var i = 0; i < len; i++){
19357             c = cols[i];
19358             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19359                 // it's the expander..
19360                 expEl  = this.headEls[i];
19361                 continue;
19362             }
19363             totalWidth += c.width;
19364             
19365         }
19366         if (expEl) {
19367             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19368         }
19369         this.headers.setWidth(w-20);
19370
19371         
19372         
19373         
19374     }
19375 });
19376 /*
19377  * Based on:
19378  * Ext JS Library 1.1.1
19379  * Copyright(c) 2006-2007, Ext JS, LLC.
19380  *
19381  * Originally Released Under LGPL - original licence link has changed is not relivant.
19382  *
19383  * Fork - LGPL
19384  * <script type="text/javascript">
19385  */
19386  
19387 /**
19388  * @class Roo.menu.Menu
19389  * @extends Roo.util.Observable
19390  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19391  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19392  * @constructor
19393  * Creates a new Menu
19394  * @param {Object} config Configuration options
19395  */
19396 Roo.menu.Menu = function(config){
19397     Roo.apply(this, config);
19398     this.id = this.id || Roo.id();
19399     this.addEvents({
19400         /**
19401          * @event beforeshow
19402          * Fires before this menu is displayed
19403          * @param {Roo.menu.Menu} this
19404          */
19405         beforeshow : true,
19406         /**
19407          * @event beforehide
19408          * Fires before this menu is hidden
19409          * @param {Roo.menu.Menu} this
19410          */
19411         beforehide : true,
19412         /**
19413          * @event show
19414          * Fires after this menu is displayed
19415          * @param {Roo.menu.Menu} this
19416          */
19417         show : true,
19418         /**
19419          * @event hide
19420          * Fires after this menu is hidden
19421          * @param {Roo.menu.Menu} this
19422          */
19423         hide : true,
19424         /**
19425          * @event click
19426          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19427          * @param {Roo.menu.Menu} this
19428          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19429          * @param {Roo.EventObject} e
19430          */
19431         click : true,
19432         /**
19433          * @event mouseover
19434          * Fires when the mouse is hovering over this menu
19435          * @param {Roo.menu.Menu} this
19436          * @param {Roo.EventObject} e
19437          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19438          */
19439         mouseover : true,
19440         /**
19441          * @event mouseout
19442          * Fires when the mouse exits this menu
19443          * @param {Roo.menu.Menu} this
19444          * @param {Roo.EventObject} e
19445          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19446          */
19447         mouseout : true,
19448         /**
19449          * @event itemclick
19450          * Fires when a menu item contained in this menu is clicked
19451          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19452          * @param {Roo.EventObject} e
19453          */
19454         itemclick: true
19455     });
19456     if (this.registerMenu) {
19457         Roo.menu.MenuMgr.register(this);
19458     }
19459     
19460     var mis = this.items;
19461     this.items = new Roo.util.MixedCollection();
19462     if(mis){
19463         this.add.apply(this, mis);
19464     }
19465 };
19466
19467 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19468     /**
19469      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19470      */
19471     minWidth : 120,
19472     /**
19473      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19474      * for bottom-right shadow (defaults to "sides")
19475      */
19476     shadow : "sides",
19477     /**
19478      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19479      * this menu (defaults to "tl-tr?")
19480      */
19481     subMenuAlign : "tl-tr?",
19482     /**
19483      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19484      * relative to its element of origin (defaults to "tl-bl?")
19485      */
19486     defaultAlign : "tl-bl?",
19487     /**
19488      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19489      */
19490     allowOtherMenus : false,
19491     /**
19492      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19493      */
19494     registerMenu : true,
19495
19496     hidden:true,
19497
19498     // private
19499     render : function(){
19500         if(this.el){
19501             return;
19502         }
19503         var el = this.el = new Roo.Layer({
19504             cls: "x-menu",
19505             shadow:this.shadow,
19506             constrain: false,
19507             parentEl: this.parentEl || document.body,
19508             zindex:15000
19509         });
19510
19511         this.keyNav = new Roo.menu.MenuNav(this);
19512
19513         if(this.plain){
19514             el.addClass("x-menu-plain");
19515         }
19516         if(this.cls){
19517             el.addClass(this.cls);
19518         }
19519         // generic focus element
19520         this.focusEl = el.createChild({
19521             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19522         });
19523         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19524         ul.on("click", this.onClick, this);
19525         ul.on("mouseover", this.onMouseOver, this);
19526         ul.on("mouseout", this.onMouseOut, this);
19527         this.items.each(function(item){
19528             var li = document.createElement("li");
19529             li.className = "x-menu-list-item";
19530             ul.dom.appendChild(li);
19531             item.render(li, this);
19532         }, this);
19533         this.ul = ul;
19534         this.autoWidth();
19535     },
19536
19537     // private
19538     autoWidth : function(){
19539         var el = this.el, ul = this.ul;
19540         if(!el){
19541             return;
19542         }
19543         var w = this.width;
19544         if(w){
19545             el.setWidth(w);
19546         }else if(Roo.isIE){
19547             el.setWidth(this.minWidth);
19548             var t = el.dom.offsetWidth; // force recalc
19549             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19550         }
19551     },
19552
19553     // private
19554     delayAutoWidth : function(){
19555         if(this.rendered){
19556             if(!this.awTask){
19557                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19558             }
19559             this.awTask.delay(20);
19560         }
19561     },
19562
19563     // private
19564     findTargetItem : function(e){
19565         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19566         if(t && t.menuItemId){
19567             return this.items.get(t.menuItemId);
19568         }
19569     },
19570
19571     // private
19572     onClick : function(e){
19573         var t;
19574         if(t = this.findTargetItem(e)){
19575             t.onClick(e);
19576             this.fireEvent("click", this, t, e);
19577         }
19578     },
19579
19580     // private
19581     setActiveItem : function(item, autoExpand){
19582         if(item != this.activeItem){
19583             if(this.activeItem){
19584                 this.activeItem.deactivate();
19585             }
19586             this.activeItem = item;
19587             item.activate(autoExpand);
19588         }else if(autoExpand){
19589             item.expandMenu();
19590         }
19591     },
19592
19593     // private
19594     tryActivate : function(start, step){
19595         var items = this.items;
19596         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19597             var item = items.get(i);
19598             if(!item.disabled && item.canActivate){
19599                 this.setActiveItem(item, false);
19600                 return item;
19601             }
19602         }
19603         return false;
19604     },
19605
19606     // private
19607     onMouseOver : function(e){
19608         var t;
19609         if(t = this.findTargetItem(e)){
19610             if(t.canActivate && !t.disabled){
19611                 this.setActiveItem(t, true);
19612             }
19613         }
19614         this.fireEvent("mouseover", this, e, t);
19615     },
19616
19617     // private
19618     onMouseOut : function(e){
19619         var t;
19620         if(t = this.findTargetItem(e)){
19621             if(t == this.activeItem && t.shouldDeactivate(e)){
19622                 this.activeItem.deactivate();
19623                 delete this.activeItem;
19624             }
19625         }
19626         this.fireEvent("mouseout", this, e, t);
19627     },
19628
19629     /**
19630      * Read-only.  Returns true if the menu is currently displayed, else false.
19631      * @type Boolean
19632      */
19633     isVisible : function(){
19634         return this.el && !this.hidden;
19635     },
19636
19637     /**
19638      * Displays this menu relative to another element
19639      * @param {String/HTMLElement/Roo.Element} element The element to align to
19640      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19641      * the element (defaults to this.defaultAlign)
19642      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19643      */
19644     show : function(el, pos, parentMenu){
19645         this.parentMenu = parentMenu;
19646         if(!this.el){
19647             this.render();
19648         }
19649         this.fireEvent("beforeshow", this);
19650         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19651     },
19652
19653     /**
19654      * Displays this menu at a specific xy position
19655      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19656      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19657      */
19658     showAt : function(xy, parentMenu, /* private: */_e){
19659         this.parentMenu = parentMenu;
19660         if(!this.el){
19661             this.render();
19662         }
19663         if(_e !== false){
19664             this.fireEvent("beforeshow", this);
19665             xy = this.el.adjustForConstraints(xy);
19666         }
19667         this.el.setXY(xy);
19668         this.el.show();
19669         this.hidden = false;
19670         this.focus();
19671         this.fireEvent("show", this);
19672     },
19673
19674     focus : function(){
19675         if(!this.hidden){
19676             this.doFocus.defer(50, this);
19677         }
19678     },
19679
19680     doFocus : function(){
19681         if(!this.hidden){
19682             this.focusEl.focus();
19683         }
19684     },
19685
19686     /**
19687      * Hides this menu and optionally all parent menus
19688      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19689      */
19690     hide : function(deep){
19691         if(this.el && this.isVisible()){
19692             this.fireEvent("beforehide", this);
19693             if(this.activeItem){
19694                 this.activeItem.deactivate();
19695                 this.activeItem = null;
19696             }
19697             this.el.hide();
19698             this.hidden = true;
19699             this.fireEvent("hide", this);
19700         }
19701         if(deep === true && this.parentMenu){
19702             this.parentMenu.hide(true);
19703         }
19704     },
19705
19706     /**
19707      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19708      * Any of the following are valid:
19709      * <ul>
19710      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19711      * <li>An HTMLElement object which will be converted to a menu item</li>
19712      * <li>A menu item config object that will be created as a new menu item</li>
19713      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19714      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19715      * </ul>
19716      * Usage:
19717      * <pre><code>
19718 // Create the menu
19719 var menu = new Roo.menu.Menu();
19720
19721 // Create a menu item to add by reference
19722 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19723
19724 // Add a bunch of items at once using different methods.
19725 // Only the last item added will be returned.
19726 var item = menu.add(
19727     menuItem,                // add existing item by ref
19728     'Dynamic Item',          // new TextItem
19729     '-',                     // new separator
19730     { text: 'Config Item' }  // new item by config
19731 );
19732 </code></pre>
19733      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19734      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19735      */
19736     add : function(){
19737         var a = arguments, l = a.length, item;
19738         for(var i = 0; i < l; i++){
19739             var el = a[i];
19740             if ((typeof(el) == "object") && el.xtype && el.xns) {
19741                 el = Roo.factory(el, Roo.menu);
19742             }
19743             
19744             if(el.render){ // some kind of Item
19745                 item = this.addItem(el);
19746             }else if(typeof el == "string"){ // string
19747                 if(el == "separator" || el == "-"){
19748                     item = this.addSeparator();
19749                 }else{
19750                     item = this.addText(el);
19751                 }
19752             }else if(el.tagName || el.el){ // element
19753                 item = this.addElement(el);
19754             }else if(typeof el == "object"){ // must be menu item config?
19755                 item = this.addMenuItem(el);
19756             }
19757         }
19758         return item;
19759     },
19760
19761     /**
19762      * Returns this menu's underlying {@link Roo.Element} object
19763      * @return {Roo.Element} The element
19764      */
19765     getEl : function(){
19766         if(!this.el){
19767             this.render();
19768         }
19769         return this.el;
19770     },
19771
19772     /**
19773      * Adds a separator bar to the menu
19774      * @return {Roo.menu.Item} The menu item that was added
19775      */
19776     addSeparator : function(){
19777         return this.addItem(new Roo.menu.Separator());
19778     },
19779
19780     /**
19781      * Adds an {@link Roo.Element} object to the menu
19782      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19783      * @return {Roo.menu.Item} The menu item that was added
19784      */
19785     addElement : function(el){
19786         return this.addItem(new Roo.menu.BaseItem(el));
19787     },
19788
19789     /**
19790      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19791      * @param {Roo.menu.Item} item The menu item to add
19792      * @return {Roo.menu.Item} The menu item that was added
19793      */
19794     addItem : function(item){
19795         this.items.add(item);
19796         if(this.ul){
19797             var li = document.createElement("li");
19798             li.className = "x-menu-list-item";
19799             this.ul.dom.appendChild(li);
19800             item.render(li, this);
19801             this.delayAutoWidth();
19802         }
19803         return item;
19804     },
19805
19806     /**
19807      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19808      * @param {Object} config A MenuItem config object
19809      * @return {Roo.menu.Item} The menu item that was added
19810      */
19811     addMenuItem : function(config){
19812         if(!(config instanceof Roo.menu.Item)){
19813             if(typeof config.checked == "boolean"){ // must be check menu item config?
19814                 config = new Roo.menu.CheckItem(config);
19815             }else{
19816                 config = new Roo.menu.Item(config);
19817             }
19818         }
19819         return this.addItem(config);
19820     },
19821
19822     /**
19823      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19824      * @param {String} text The text to display in the menu item
19825      * @return {Roo.menu.Item} The menu item that was added
19826      */
19827     addText : function(text){
19828         return this.addItem(new Roo.menu.TextItem({ text : text }));
19829     },
19830
19831     /**
19832      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19833      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19834      * @param {Roo.menu.Item} item The menu item to add
19835      * @return {Roo.menu.Item} The menu item that was added
19836      */
19837     insert : function(index, item){
19838         this.items.insert(index, item);
19839         if(this.ul){
19840             var li = document.createElement("li");
19841             li.className = "x-menu-list-item";
19842             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19843             item.render(li, this);
19844             this.delayAutoWidth();
19845         }
19846         return item;
19847     },
19848
19849     /**
19850      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19851      * @param {Roo.menu.Item} item The menu item to remove
19852      */
19853     remove : function(item){
19854         this.items.removeKey(item.id);
19855         item.destroy();
19856     },
19857
19858     /**
19859      * Removes and destroys all items in the menu
19860      */
19861     removeAll : function(){
19862         var f;
19863         while(f = this.items.first()){
19864             this.remove(f);
19865         }
19866     }
19867 });
19868
19869 // MenuNav is a private utility class used internally by the Menu
19870 Roo.menu.MenuNav = function(menu){
19871     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19872     this.scope = this.menu = menu;
19873 };
19874
19875 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19876     doRelay : function(e, h){
19877         var k = e.getKey();
19878         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19879             this.menu.tryActivate(0, 1);
19880             return false;
19881         }
19882         return h.call(this.scope || this, e, this.menu);
19883     },
19884
19885     up : function(e, m){
19886         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19887             m.tryActivate(m.items.length-1, -1);
19888         }
19889     },
19890
19891     down : function(e, m){
19892         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19893             m.tryActivate(0, 1);
19894         }
19895     },
19896
19897     right : function(e, m){
19898         if(m.activeItem){
19899             m.activeItem.expandMenu(true);
19900         }
19901     },
19902
19903     left : function(e, m){
19904         m.hide();
19905         if(m.parentMenu && m.parentMenu.activeItem){
19906             m.parentMenu.activeItem.activate();
19907         }
19908     },
19909
19910     enter : function(e, m){
19911         if(m.activeItem){
19912             e.stopPropagation();
19913             m.activeItem.onClick(e);
19914             m.fireEvent("click", this, m.activeItem);
19915             return true;
19916         }
19917     }
19918 });/*
19919  * Based on:
19920  * Ext JS Library 1.1.1
19921  * Copyright(c) 2006-2007, Ext JS, LLC.
19922  *
19923  * Originally Released Under LGPL - original licence link has changed is not relivant.
19924  *
19925  * Fork - LGPL
19926  * <script type="text/javascript">
19927  */
19928  
19929 /**
19930  * @class Roo.menu.MenuMgr
19931  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19932  * @singleton
19933  */
19934 Roo.menu.MenuMgr = function(){
19935    var menus, active, groups = {}, attached = false, lastShow = new Date();
19936
19937    // private - called when first menu is created
19938    function init(){
19939        menus = {};
19940        active = new Roo.util.MixedCollection();
19941        Roo.get(document).addKeyListener(27, function(){
19942            if(active.length > 0){
19943                hideAll();
19944            }
19945        });
19946    }
19947
19948    // private
19949    function hideAll(){
19950        if(active && active.length > 0){
19951            var c = active.clone();
19952            c.each(function(m){
19953                m.hide();
19954            });
19955        }
19956    }
19957
19958    // private
19959    function onHide(m){
19960        active.remove(m);
19961        if(active.length < 1){
19962            Roo.get(document).un("mousedown", onMouseDown);
19963            attached = false;
19964        }
19965    }
19966
19967    // private
19968    function onShow(m){
19969        var last = active.last();
19970        lastShow = new Date();
19971        active.add(m);
19972        if(!attached){
19973            Roo.get(document).on("mousedown", onMouseDown);
19974            attached = true;
19975        }
19976        if(m.parentMenu){
19977           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19978           m.parentMenu.activeChild = m;
19979        }else if(last && last.isVisible()){
19980           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19981        }
19982    }
19983
19984    // private
19985    function onBeforeHide(m){
19986        if(m.activeChild){
19987            m.activeChild.hide();
19988        }
19989        if(m.autoHideTimer){
19990            clearTimeout(m.autoHideTimer);
19991            delete m.autoHideTimer;
19992        }
19993    }
19994
19995    // private
19996    function onBeforeShow(m){
19997        var pm = m.parentMenu;
19998        if(!pm && !m.allowOtherMenus){
19999            hideAll();
20000        }else if(pm && pm.activeChild && active != m){
20001            pm.activeChild.hide();
20002        }
20003    }
20004
20005    // private
20006    function onMouseDown(e){
20007        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20008            hideAll();
20009        }
20010    }
20011
20012    // private
20013    function onBeforeCheck(mi, state){
20014        if(state){
20015            var g = groups[mi.group];
20016            for(var i = 0, l = g.length; i < l; i++){
20017                if(g[i] != mi){
20018                    g[i].setChecked(false);
20019                }
20020            }
20021        }
20022    }
20023
20024    return {
20025
20026        /**
20027         * Hides all menus that are currently visible
20028         */
20029        hideAll : function(){
20030             hideAll();  
20031        },
20032
20033        // private
20034        register : function(menu){
20035            if(!menus){
20036                init();
20037            }
20038            menus[menu.id] = menu;
20039            menu.on("beforehide", onBeforeHide);
20040            menu.on("hide", onHide);
20041            menu.on("beforeshow", onBeforeShow);
20042            menu.on("show", onShow);
20043            var g = menu.group;
20044            if(g && menu.events["checkchange"]){
20045                if(!groups[g]){
20046                    groups[g] = [];
20047                }
20048                groups[g].push(menu);
20049                menu.on("checkchange", onCheck);
20050            }
20051        },
20052
20053         /**
20054          * Returns a {@link Roo.menu.Menu} object
20055          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20056          * be used to generate and return a new Menu instance.
20057          */
20058        get : function(menu){
20059            if(typeof menu == "string"){ // menu id
20060                return menus[menu];
20061            }else if(menu.events){  // menu instance
20062                return menu;
20063            }else if(typeof menu.length == 'number'){ // array of menu items?
20064                return new Roo.menu.Menu({items:menu});
20065            }else{ // otherwise, must be a config
20066                return new Roo.menu.Menu(menu);
20067            }
20068        },
20069
20070        // private
20071        unregister : function(menu){
20072            delete menus[menu.id];
20073            menu.un("beforehide", onBeforeHide);
20074            menu.un("hide", onHide);
20075            menu.un("beforeshow", onBeforeShow);
20076            menu.un("show", onShow);
20077            var g = menu.group;
20078            if(g && menu.events["checkchange"]){
20079                groups[g].remove(menu);
20080                menu.un("checkchange", onCheck);
20081            }
20082        },
20083
20084        // private
20085        registerCheckable : function(menuItem){
20086            var g = menuItem.group;
20087            if(g){
20088                if(!groups[g]){
20089                    groups[g] = [];
20090                }
20091                groups[g].push(menuItem);
20092                menuItem.on("beforecheckchange", onBeforeCheck);
20093            }
20094        },
20095
20096        // private
20097        unregisterCheckable : function(menuItem){
20098            var g = menuItem.group;
20099            if(g){
20100                groups[g].remove(menuItem);
20101                menuItem.un("beforecheckchange", onBeforeCheck);
20102            }
20103        }
20104    };
20105 }();/*
20106  * Based on:
20107  * Ext JS Library 1.1.1
20108  * Copyright(c) 2006-2007, Ext JS, LLC.
20109  *
20110  * Originally Released Under LGPL - original licence link has changed is not relivant.
20111  *
20112  * Fork - LGPL
20113  * <script type="text/javascript">
20114  */
20115  
20116
20117 /**
20118  * @class Roo.menu.BaseItem
20119  * @extends Roo.Component
20120  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20121  * management and base configuration options shared by all menu components.
20122  * @constructor
20123  * Creates a new BaseItem
20124  * @param {Object} config Configuration options
20125  */
20126 Roo.menu.BaseItem = function(config){
20127     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20128
20129     this.addEvents({
20130         /**
20131          * @event click
20132          * Fires when this item is clicked
20133          * @param {Roo.menu.BaseItem} this
20134          * @param {Roo.EventObject} e
20135          */
20136         click: true,
20137         /**
20138          * @event activate
20139          * Fires when this item is activated
20140          * @param {Roo.menu.BaseItem} this
20141          */
20142         activate : true,
20143         /**
20144          * @event deactivate
20145          * Fires when this item is deactivated
20146          * @param {Roo.menu.BaseItem} this
20147          */
20148         deactivate : true
20149     });
20150
20151     if(this.handler){
20152         this.on("click", this.handler, this.scope, true);
20153     }
20154 };
20155
20156 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20157     /**
20158      * @cfg {Function} handler
20159      * A function that will handle the click event of this menu item (defaults to undefined)
20160      */
20161     /**
20162      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20163      */
20164     canActivate : false,
20165     /**
20166      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20167      */
20168     activeClass : "x-menu-item-active",
20169     /**
20170      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20171      */
20172     hideOnClick : true,
20173     /**
20174      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20175      */
20176     hideDelay : 100,
20177
20178     // private
20179     ctype: "Roo.menu.BaseItem",
20180
20181     // private
20182     actionMode : "container",
20183
20184     // private
20185     render : function(container, parentMenu){
20186         this.parentMenu = parentMenu;
20187         Roo.menu.BaseItem.superclass.render.call(this, container);
20188         this.container.menuItemId = this.id;
20189     },
20190
20191     // private
20192     onRender : function(container, position){
20193         this.el = Roo.get(this.el);
20194         container.dom.appendChild(this.el.dom);
20195     },
20196
20197     // private
20198     onClick : function(e){
20199         if(!this.disabled && this.fireEvent("click", this, e) !== false
20200                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20201             this.handleClick(e);
20202         }else{
20203             e.stopEvent();
20204         }
20205     },
20206
20207     // private
20208     activate : function(){
20209         if(this.disabled){
20210             return false;
20211         }
20212         var li = this.container;
20213         li.addClass(this.activeClass);
20214         this.region = li.getRegion().adjust(2, 2, -2, -2);
20215         this.fireEvent("activate", this);
20216         return true;
20217     },
20218
20219     // private
20220     deactivate : function(){
20221         this.container.removeClass(this.activeClass);
20222         this.fireEvent("deactivate", this);
20223     },
20224
20225     // private
20226     shouldDeactivate : function(e){
20227         return !this.region || !this.region.contains(e.getPoint());
20228     },
20229
20230     // private
20231     handleClick : function(e){
20232         if(this.hideOnClick){
20233             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20234         }
20235     },
20236
20237     // private
20238     expandMenu : function(autoActivate){
20239         // do nothing
20240     },
20241
20242     // private
20243     hideMenu : function(){
20244         // do nothing
20245     }
20246 });/*
20247  * Based on:
20248  * Ext JS Library 1.1.1
20249  * Copyright(c) 2006-2007, Ext JS, LLC.
20250  *
20251  * Originally Released Under LGPL - original licence link has changed is not relivant.
20252  *
20253  * Fork - LGPL
20254  * <script type="text/javascript">
20255  */
20256  
20257 /**
20258  * @class Roo.menu.Adapter
20259  * @extends Roo.menu.BaseItem
20260  * 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.
20261  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20262  * @constructor
20263  * Creates a new Adapter
20264  * @param {Object} config Configuration options
20265  */
20266 Roo.menu.Adapter = function(component, config){
20267     Roo.menu.Adapter.superclass.constructor.call(this, config);
20268     this.component = component;
20269 };
20270 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20271     // private
20272     canActivate : true,
20273
20274     // private
20275     onRender : function(container, position){
20276         this.component.render(container);
20277         this.el = this.component.getEl();
20278     },
20279
20280     // private
20281     activate : function(){
20282         if(this.disabled){
20283             return false;
20284         }
20285         this.component.focus();
20286         this.fireEvent("activate", this);
20287         return true;
20288     },
20289
20290     // private
20291     deactivate : function(){
20292         this.fireEvent("deactivate", this);
20293     },
20294
20295     // private
20296     disable : function(){
20297         this.component.disable();
20298         Roo.menu.Adapter.superclass.disable.call(this);
20299     },
20300
20301     // private
20302     enable : function(){
20303         this.component.enable();
20304         Roo.menu.Adapter.superclass.enable.call(this);
20305     }
20306 });/*
20307  * Based on:
20308  * Ext JS Library 1.1.1
20309  * Copyright(c) 2006-2007, Ext JS, LLC.
20310  *
20311  * Originally Released Under LGPL - original licence link has changed is not relivant.
20312  *
20313  * Fork - LGPL
20314  * <script type="text/javascript">
20315  */
20316
20317 /**
20318  * @class Roo.menu.TextItem
20319  * @extends Roo.menu.BaseItem
20320  * Adds a static text string to a menu, usually used as either a heading or group separator.
20321  * Note: old style constructor with text is still supported.
20322  * 
20323  * @constructor
20324  * Creates a new TextItem
20325  * @param {Object} cfg Configuration
20326  */
20327 Roo.menu.TextItem = function(cfg){
20328     if (typeof(cfg) == 'string') {
20329         this.text = cfg;
20330     } else {
20331         Roo.apply(this,cfg);
20332     }
20333     
20334     Roo.menu.TextItem.superclass.constructor.call(this);
20335 };
20336
20337 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20338     /**
20339      * @cfg {Boolean} text Text to show on item.
20340      */
20341     text : '',
20342     
20343     /**
20344      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20345      */
20346     hideOnClick : false,
20347     /**
20348      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20349      */
20350     itemCls : "x-menu-text",
20351
20352     // private
20353     onRender : function(){
20354         var s = document.createElement("span");
20355         s.className = this.itemCls;
20356         s.innerHTML = this.text;
20357         this.el = s;
20358         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20359     }
20360 });/*
20361  * Based on:
20362  * Ext JS Library 1.1.1
20363  * Copyright(c) 2006-2007, Ext JS, LLC.
20364  *
20365  * Originally Released Under LGPL - original licence link has changed is not relivant.
20366  *
20367  * Fork - LGPL
20368  * <script type="text/javascript">
20369  */
20370
20371 /**
20372  * @class Roo.menu.Separator
20373  * @extends Roo.menu.BaseItem
20374  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20375  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20376  * @constructor
20377  * @param {Object} config Configuration options
20378  */
20379 Roo.menu.Separator = function(config){
20380     Roo.menu.Separator.superclass.constructor.call(this, config);
20381 };
20382
20383 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20384     /**
20385      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20386      */
20387     itemCls : "x-menu-sep",
20388     /**
20389      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20390      */
20391     hideOnClick : false,
20392
20393     // private
20394     onRender : function(li){
20395         var s = document.createElement("span");
20396         s.className = this.itemCls;
20397         s.innerHTML = "&#160;";
20398         this.el = s;
20399         li.addClass("x-menu-sep-li");
20400         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20401     }
20402 });/*
20403  * Based on:
20404  * Ext JS Library 1.1.1
20405  * Copyright(c) 2006-2007, Ext JS, LLC.
20406  *
20407  * Originally Released Under LGPL - original licence link has changed is not relivant.
20408  *
20409  * Fork - LGPL
20410  * <script type="text/javascript">
20411  */
20412 /**
20413  * @class Roo.menu.Item
20414  * @extends Roo.menu.BaseItem
20415  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20416  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20417  * activation and click handling.
20418  * @constructor
20419  * Creates a new Item
20420  * @param {Object} config Configuration options
20421  */
20422 Roo.menu.Item = function(config){
20423     Roo.menu.Item.superclass.constructor.call(this, config);
20424     if(this.menu){
20425         this.menu = Roo.menu.MenuMgr.get(this.menu);
20426     }
20427 };
20428 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20429     
20430     /**
20431      * @cfg {String} text
20432      * The text to show on the menu item.
20433      */
20434     text: '',
20435      /**
20436      * @cfg {String} HTML to render in menu
20437      * The text to show on the menu item (HTML version).
20438      */
20439     html: '',
20440     /**
20441      * @cfg {String} icon
20442      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20443      */
20444     icon: undefined,
20445     /**
20446      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20447      */
20448     itemCls : "x-menu-item",
20449     /**
20450      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20451      */
20452     canActivate : true,
20453     /**
20454      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20455      */
20456     showDelay: 200,
20457     // doc'd in BaseItem
20458     hideDelay: 200,
20459
20460     // private
20461     ctype: "Roo.menu.Item",
20462     
20463     // private
20464     onRender : function(container, position){
20465         var el = document.createElement("a");
20466         el.hideFocus = true;
20467         el.unselectable = "on";
20468         el.href = this.href || "#";
20469         if(this.hrefTarget){
20470             el.target = this.hrefTarget;
20471         }
20472         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20473         
20474         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20475         
20476         el.innerHTML = String.format(
20477                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20478                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20479         this.el = el;
20480         Roo.menu.Item.superclass.onRender.call(this, container, position);
20481     },
20482
20483     /**
20484      * Sets the text to display in this menu item
20485      * @param {String} text The text to display
20486      * @param {Boolean} isHTML true to indicate text is pure html.
20487      */
20488     setText : function(text, isHTML){
20489         if (isHTML) {
20490             this.html = text;
20491         } else {
20492             this.text = text;
20493             this.html = '';
20494         }
20495         if(this.rendered){
20496             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20497      
20498             this.el.update(String.format(
20499                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20500                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20501             this.parentMenu.autoWidth();
20502         }
20503     },
20504
20505     // private
20506     handleClick : function(e){
20507         if(!this.href){ // if no link defined, stop the event automatically
20508             e.stopEvent();
20509         }
20510         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20511     },
20512
20513     // private
20514     activate : function(autoExpand){
20515         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20516             this.focus();
20517             if(autoExpand){
20518                 this.expandMenu();
20519             }
20520         }
20521         return true;
20522     },
20523
20524     // private
20525     shouldDeactivate : function(e){
20526         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20527             if(this.menu && this.menu.isVisible()){
20528                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20529             }
20530             return true;
20531         }
20532         return false;
20533     },
20534
20535     // private
20536     deactivate : function(){
20537         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20538         this.hideMenu();
20539     },
20540
20541     // private
20542     expandMenu : function(autoActivate){
20543         if(!this.disabled && this.menu){
20544             clearTimeout(this.hideTimer);
20545             delete this.hideTimer;
20546             if(!this.menu.isVisible() && !this.showTimer){
20547                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20548             }else if (this.menu.isVisible() && autoActivate){
20549                 this.menu.tryActivate(0, 1);
20550             }
20551         }
20552     },
20553
20554     // private
20555     deferExpand : function(autoActivate){
20556         delete this.showTimer;
20557         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20558         if(autoActivate){
20559             this.menu.tryActivate(0, 1);
20560         }
20561     },
20562
20563     // private
20564     hideMenu : function(){
20565         clearTimeout(this.showTimer);
20566         delete this.showTimer;
20567         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20568             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20569         }
20570     },
20571
20572     // private
20573     deferHide : function(){
20574         delete this.hideTimer;
20575         this.menu.hide();
20576     }
20577 });/*
20578  * Based on:
20579  * Ext JS Library 1.1.1
20580  * Copyright(c) 2006-2007, Ext JS, LLC.
20581  *
20582  * Originally Released Under LGPL - original licence link has changed is not relivant.
20583  *
20584  * Fork - LGPL
20585  * <script type="text/javascript">
20586  */
20587  
20588 /**
20589  * @class Roo.menu.CheckItem
20590  * @extends Roo.menu.Item
20591  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20592  * @constructor
20593  * Creates a new CheckItem
20594  * @param {Object} config Configuration options
20595  */
20596 Roo.menu.CheckItem = function(config){
20597     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20598     this.addEvents({
20599         /**
20600          * @event beforecheckchange
20601          * Fires before the checked value is set, providing an opportunity to cancel if needed
20602          * @param {Roo.menu.CheckItem} this
20603          * @param {Boolean} checked The new checked value that will be set
20604          */
20605         "beforecheckchange" : true,
20606         /**
20607          * @event checkchange
20608          * Fires after the checked value has been set
20609          * @param {Roo.menu.CheckItem} this
20610          * @param {Boolean} checked The checked value that was set
20611          */
20612         "checkchange" : true
20613     });
20614     if(this.checkHandler){
20615         this.on('checkchange', this.checkHandler, this.scope);
20616     }
20617 };
20618 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20619     /**
20620      * @cfg {String} group
20621      * All check items with the same group name will automatically be grouped into a single-select
20622      * radio button group (defaults to '')
20623      */
20624     /**
20625      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20626      */
20627     itemCls : "x-menu-item x-menu-check-item",
20628     /**
20629      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20630      */
20631     groupClass : "x-menu-group-item",
20632
20633     /**
20634      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20635      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20636      * initialized with checked = true will be rendered as checked.
20637      */
20638     checked: false,
20639
20640     // private
20641     ctype: "Roo.menu.CheckItem",
20642
20643     // private
20644     onRender : function(c){
20645         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20646         if(this.group){
20647             this.el.addClass(this.groupClass);
20648         }
20649         Roo.menu.MenuMgr.registerCheckable(this);
20650         if(this.checked){
20651             this.checked = false;
20652             this.setChecked(true, true);
20653         }
20654     },
20655
20656     // private
20657     destroy : function(){
20658         if(this.rendered){
20659             Roo.menu.MenuMgr.unregisterCheckable(this);
20660         }
20661         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20662     },
20663
20664     /**
20665      * Set the checked state of this item
20666      * @param {Boolean} checked The new checked value
20667      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20668      */
20669     setChecked : function(state, suppressEvent){
20670         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20671             if(this.container){
20672                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20673             }
20674             this.checked = state;
20675             if(suppressEvent !== true){
20676                 this.fireEvent("checkchange", this, state);
20677             }
20678         }
20679     },
20680
20681     // private
20682     handleClick : function(e){
20683        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20684            this.setChecked(!this.checked);
20685        }
20686        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20687     }
20688 });/*
20689  * Based on:
20690  * Ext JS Library 1.1.1
20691  * Copyright(c) 2006-2007, Ext JS, LLC.
20692  *
20693  * Originally Released Under LGPL - original licence link has changed is not relivant.
20694  *
20695  * Fork - LGPL
20696  * <script type="text/javascript">
20697  */
20698  
20699 /**
20700  * @class Roo.menu.DateItem
20701  * @extends Roo.menu.Adapter
20702  * A menu item that wraps the {@link Roo.DatPicker} component.
20703  * @constructor
20704  * Creates a new DateItem
20705  * @param {Object} config Configuration options
20706  */
20707 Roo.menu.DateItem = function(config){
20708     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20709     /** The Roo.DatePicker object @type Roo.DatePicker */
20710     this.picker = this.component;
20711     this.addEvents({select: true});
20712     
20713     this.picker.on("render", function(picker){
20714         picker.getEl().swallowEvent("click");
20715         picker.container.addClass("x-menu-date-item");
20716     });
20717
20718     this.picker.on("select", this.onSelect, this);
20719 };
20720
20721 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20722     // private
20723     onSelect : function(picker, date){
20724         this.fireEvent("select", this, date, picker);
20725         Roo.menu.DateItem.superclass.handleClick.call(this);
20726     }
20727 });/*
20728  * Based on:
20729  * Ext JS Library 1.1.1
20730  * Copyright(c) 2006-2007, Ext JS, LLC.
20731  *
20732  * Originally Released Under LGPL - original licence link has changed is not relivant.
20733  *
20734  * Fork - LGPL
20735  * <script type="text/javascript">
20736  */
20737  
20738 /**
20739  * @class Roo.menu.ColorItem
20740  * @extends Roo.menu.Adapter
20741  * A menu item that wraps the {@link Roo.ColorPalette} component.
20742  * @constructor
20743  * Creates a new ColorItem
20744  * @param {Object} config Configuration options
20745  */
20746 Roo.menu.ColorItem = function(config){
20747     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20748     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20749     this.palette = this.component;
20750     this.relayEvents(this.palette, ["select"]);
20751     if(this.selectHandler){
20752         this.on('select', this.selectHandler, this.scope);
20753     }
20754 };
20755 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20756  * Based on:
20757  * Ext JS Library 1.1.1
20758  * Copyright(c) 2006-2007, Ext JS, LLC.
20759  *
20760  * Originally Released Under LGPL - original licence link has changed is not relivant.
20761  *
20762  * Fork - LGPL
20763  * <script type="text/javascript">
20764  */
20765  
20766
20767 /**
20768  * @class Roo.menu.DateMenu
20769  * @extends Roo.menu.Menu
20770  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20771  * @constructor
20772  * Creates a new DateMenu
20773  * @param {Object} config Configuration options
20774  */
20775 Roo.menu.DateMenu = function(config){
20776     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20777     this.plain = true;
20778     var di = new Roo.menu.DateItem(config);
20779     this.add(di);
20780     /**
20781      * The {@link Roo.DatePicker} instance for this DateMenu
20782      * @type DatePicker
20783      */
20784     this.picker = di.picker;
20785     /**
20786      * @event select
20787      * @param {DatePicker} picker
20788      * @param {Date} date
20789      */
20790     this.relayEvents(di, ["select"]);
20791
20792     this.on('beforeshow', function(){
20793         if(this.picker){
20794             this.picker.hideMonthPicker(true);
20795         }
20796     }, this);
20797 };
20798 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20799     cls:'x-date-menu'
20800 });/*
20801  * Based on:
20802  * Ext JS Library 1.1.1
20803  * Copyright(c) 2006-2007, Ext JS, LLC.
20804  *
20805  * Originally Released Under LGPL - original licence link has changed is not relivant.
20806  *
20807  * Fork - LGPL
20808  * <script type="text/javascript">
20809  */
20810  
20811
20812 /**
20813  * @class Roo.menu.ColorMenu
20814  * @extends Roo.menu.Menu
20815  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20816  * @constructor
20817  * Creates a new ColorMenu
20818  * @param {Object} config Configuration options
20819  */
20820 Roo.menu.ColorMenu = function(config){
20821     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20822     this.plain = true;
20823     var ci = new Roo.menu.ColorItem(config);
20824     this.add(ci);
20825     /**
20826      * The {@link Roo.ColorPalette} instance for this ColorMenu
20827      * @type ColorPalette
20828      */
20829     this.palette = ci.palette;
20830     /**
20831      * @event select
20832      * @param {ColorPalette} palette
20833      * @param {String} color
20834      */
20835     this.relayEvents(ci, ["select"]);
20836 };
20837 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20838  * Based on:
20839  * Ext JS Library 1.1.1
20840  * Copyright(c) 2006-2007, Ext JS, LLC.
20841  *
20842  * Originally Released Under LGPL - original licence link has changed is not relivant.
20843  *
20844  * Fork - LGPL
20845  * <script type="text/javascript">
20846  */
20847  
20848 /**
20849  * @class Roo.form.Field
20850  * @extends Roo.BoxComponent
20851  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20852  * @constructor
20853  * Creates a new Field
20854  * @param {Object} config Configuration options
20855  */
20856 Roo.form.Field = function(config){
20857     Roo.form.Field.superclass.constructor.call(this, config);
20858 };
20859
20860 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20861     /**
20862      * @cfg {String} fieldLabel Label to use when rendering a form.
20863      */
20864        /**
20865      * @cfg {String} qtip Mouse over tip
20866      */
20867      
20868     /**
20869      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20870      */
20871     invalidClass : "x-form-invalid",
20872     /**
20873      * @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")
20874      */
20875     invalidText : "The value in this field is invalid",
20876     /**
20877      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20878      */
20879     focusClass : "x-form-focus",
20880     /**
20881      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20882       automatic validation (defaults to "keyup").
20883      */
20884     validationEvent : "keyup",
20885     /**
20886      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20887      */
20888     validateOnBlur : true,
20889     /**
20890      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20891      */
20892     validationDelay : 250,
20893     /**
20894      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20895      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20896      */
20897     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20898     /**
20899      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20900      */
20901     fieldClass : "x-form-field",
20902     /**
20903      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20904      *<pre>
20905 Value         Description
20906 -----------   ----------------------------------------------------------------------
20907 qtip          Display a quick tip when the user hovers over the field
20908 title         Display a default browser title attribute popup
20909 under         Add a block div beneath the field containing the error text
20910 side          Add an error icon to the right of the field with a popup on hover
20911 [element id]  Add the error text directly to the innerHTML of the specified element
20912 </pre>
20913      */
20914     msgTarget : 'qtip',
20915     /**
20916      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20917      */
20918     msgFx : 'normal',
20919
20920     /**
20921      * @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.
20922      */
20923     readOnly : false,
20924
20925     /**
20926      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20927      */
20928     disabled : false,
20929
20930     /**
20931      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20932      */
20933     inputType : undefined,
20934     
20935     /**
20936      * @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).
20937          */
20938         tabIndex : undefined,
20939         
20940     // private
20941     isFormField : true,
20942
20943     // private
20944     hasFocus : false,
20945     /**
20946      * @property {Roo.Element} fieldEl
20947      * Element Containing the rendered Field (with label etc.)
20948      */
20949     /**
20950      * @cfg {Mixed} value A value to initialize this field with.
20951      */
20952     value : undefined,
20953
20954     /**
20955      * @cfg {String} name The field's HTML name attribute.
20956      */
20957     /**
20958      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20959      */
20960
20961         // private ??
20962         initComponent : function(){
20963         Roo.form.Field.superclass.initComponent.call(this);
20964         this.addEvents({
20965             /**
20966              * @event focus
20967              * Fires when this field receives input focus.
20968              * @param {Roo.form.Field} this
20969              */
20970             focus : true,
20971             /**
20972              * @event blur
20973              * Fires when this field loses input focus.
20974              * @param {Roo.form.Field} this
20975              */
20976             blur : true,
20977             /**
20978              * @event specialkey
20979              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20980              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20981              * @param {Roo.form.Field} this
20982              * @param {Roo.EventObject} e The event object
20983              */
20984             specialkey : true,
20985             /**
20986              * @event change
20987              * Fires just before the field blurs if the field value has changed.
20988              * @param {Roo.form.Field} this
20989              * @param {Mixed} newValue The new value
20990              * @param {Mixed} oldValue The original value
20991              */
20992             change : true,
20993             /**
20994              * @event invalid
20995              * Fires after the field has been marked as invalid.
20996              * @param {Roo.form.Field} this
20997              * @param {String} msg The validation message
20998              */
20999             invalid : true,
21000             /**
21001              * @event valid
21002              * Fires after the field has been validated with no errors.
21003              * @param {Roo.form.Field} this
21004              */
21005             valid : true,
21006              /**
21007              * @event keyup
21008              * Fires after the key up
21009              * @param {Roo.form.Field} this
21010              * @param {Roo.EventObject}  e The event Object
21011              */
21012             keyup : true
21013         });
21014     },
21015
21016     /**
21017      * Returns the name attribute of the field if available
21018      * @return {String} name The field name
21019      */
21020     getName: function(){
21021          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21022     },
21023
21024     // private
21025     onRender : function(ct, position){
21026         Roo.form.Field.superclass.onRender.call(this, ct, position);
21027         if(!this.el){
21028             var cfg = this.getAutoCreate();
21029             if(!cfg.name){
21030                 cfg.name = this.name || this.id;
21031             }
21032             if(this.inputType){
21033                 cfg.type = this.inputType;
21034             }
21035             this.el = ct.createChild(cfg, position);
21036         }
21037         var type = this.el.dom.type;
21038         if(type){
21039             if(type == 'password'){
21040                 type = 'text';
21041             }
21042             this.el.addClass('x-form-'+type);
21043         }
21044         if(this.readOnly){
21045             this.el.dom.readOnly = true;
21046         }
21047         if(this.tabIndex !== undefined){
21048             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21049         }
21050
21051         this.el.addClass([this.fieldClass, this.cls]);
21052         this.initValue();
21053     },
21054
21055     /**
21056      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21057      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21058      * @return {Roo.form.Field} this
21059      */
21060     applyTo : function(target){
21061         this.allowDomMove = false;
21062         this.el = Roo.get(target);
21063         this.render(this.el.dom.parentNode);
21064         return this;
21065     },
21066
21067     // private
21068     initValue : function(){
21069         if(this.value !== undefined){
21070             this.setValue(this.value);
21071         }else if(this.el.dom.value.length > 0){
21072             this.setValue(this.el.dom.value);
21073         }
21074     },
21075
21076     /**
21077      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21078      */
21079     isDirty : function() {
21080         if(this.disabled) {
21081             return false;
21082         }
21083         return String(this.getValue()) !== String(this.originalValue);
21084     },
21085
21086     // private
21087     afterRender : function(){
21088         Roo.form.Field.superclass.afterRender.call(this);
21089         this.initEvents();
21090     },
21091
21092     // private
21093     fireKey : function(e){
21094         //Roo.log('field ' + e.getKey());
21095         if(e.isNavKeyPress()){
21096             this.fireEvent("specialkey", this, e);
21097         }
21098     },
21099
21100     /**
21101      * Resets the current field value to the originally loaded value and clears any validation messages
21102      */
21103     reset : function(){
21104         this.setValue(this.originalValue);
21105         this.clearInvalid();
21106     },
21107
21108     // private
21109     initEvents : function(){
21110         // safari killled keypress - so keydown is now used..
21111         this.el.on("keydown" , this.fireKey,  this);
21112         this.el.on("focus", this.onFocus,  this);
21113         this.el.on("blur", this.onBlur,  this);
21114         this.el.relayEvent('keyup', this);
21115
21116         // reference to original value for reset
21117         this.originalValue = this.getValue();
21118     },
21119
21120     // private
21121     onFocus : function(){
21122         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21123             this.el.addClass(this.focusClass);
21124         }
21125         if(!this.hasFocus){
21126             this.hasFocus = true;
21127             this.startValue = this.getValue();
21128             this.fireEvent("focus", this);
21129         }
21130     },
21131
21132     beforeBlur : Roo.emptyFn,
21133
21134     // private
21135     onBlur : function(){
21136         this.beforeBlur();
21137         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21138             this.el.removeClass(this.focusClass);
21139         }
21140         this.hasFocus = false;
21141         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21142             this.validate();
21143         }
21144         var v = this.getValue();
21145         if(String(v) !== String(this.startValue)){
21146             this.fireEvent('change', this, v, this.startValue);
21147         }
21148         this.fireEvent("blur", this);
21149     },
21150
21151     /**
21152      * Returns whether or not the field value is currently valid
21153      * @param {Boolean} preventMark True to disable marking the field invalid
21154      * @return {Boolean} True if the value is valid, else false
21155      */
21156     isValid : function(preventMark){
21157         if(this.disabled){
21158             return true;
21159         }
21160         var restore = this.preventMark;
21161         this.preventMark = preventMark === true;
21162         var v = this.validateValue(this.processValue(this.getRawValue()));
21163         this.preventMark = restore;
21164         return v;
21165     },
21166
21167     /**
21168      * Validates the field value
21169      * @return {Boolean} True if the value is valid, else false
21170      */
21171     validate : function(){
21172         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21173             this.clearInvalid();
21174             return true;
21175         }
21176         return false;
21177     },
21178
21179     processValue : function(value){
21180         return value;
21181     },
21182
21183     // private
21184     // Subclasses should provide the validation implementation by overriding this
21185     validateValue : function(value){
21186         return true;
21187     },
21188
21189     /**
21190      * Mark this field as invalid
21191      * @param {String} msg The validation message
21192      */
21193     markInvalid : function(msg){
21194         if(!this.rendered || this.preventMark){ // not rendered
21195             return;
21196         }
21197         this.el.addClass(this.invalidClass);
21198         msg = msg || this.invalidText;
21199         switch(this.msgTarget){
21200             case 'qtip':
21201                 this.el.dom.qtip = msg;
21202                 this.el.dom.qclass = 'x-form-invalid-tip';
21203                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21204                     Roo.QuickTips.enable();
21205                 }
21206                 break;
21207             case 'title':
21208                 this.el.dom.title = msg;
21209                 break;
21210             case 'under':
21211                 if(!this.errorEl){
21212                     var elp = this.el.findParent('.x-form-element', 5, true);
21213                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21214                     this.errorEl.setWidth(elp.getWidth(true)-20);
21215                 }
21216                 this.errorEl.update(msg);
21217                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21218                 break;
21219             case 'side':
21220                 if(!this.errorIcon){
21221                     var elp = this.el.findParent('.x-form-element', 5, true);
21222                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21223                 }
21224                 this.alignErrorIcon();
21225                 this.errorIcon.dom.qtip = msg;
21226                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21227                 this.errorIcon.show();
21228                 this.on('resize', this.alignErrorIcon, this);
21229                 break;
21230             default:
21231                 var t = Roo.getDom(this.msgTarget);
21232                 t.innerHTML = msg;
21233                 t.style.display = this.msgDisplay;
21234                 break;
21235         }
21236         this.fireEvent('invalid', this, msg);
21237     },
21238
21239     // private
21240     alignErrorIcon : function(){
21241         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21242     },
21243
21244     /**
21245      * Clear any invalid styles/messages for this field
21246      */
21247     clearInvalid : function(){
21248         if(!this.rendered || this.preventMark){ // not rendered
21249             return;
21250         }
21251         this.el.removeClass(this.invalidClass);
21252         switch(this.msgTarget){
21253             case 'qtip':
21254                 this.el.dom.qtip = '';
21255                 break;
21256             case 'title':
21257                 this.el.dom.title = '';
21258                 break;
21259             case 'under':
21260                 if(this.errorEl){
21261                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21262                 }
21263                 break;
21264             case 'side':
21265                 if(this.errorIcon){
21266                     this.errorIcon.dom.qtip = '';
21267                     this.errorIcon.hide();
21268                     this.un('resize', this.alignErrorIcon, this);
21269                 }
21270                 break;
21271             default:
21272                 var t = Roo.getDom(this.msgTarget);
21273                 t.innerHTML = '';
21274                 t.style.display = 'none';
21275                 break;
21276         }
21277         this.fireEvent('valid', this);
21278     },
21279
21280     /**
21281      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21282      * @return {Mixed} value The field value
21283      */
21284     getRawValue : function(){
21285         var v = this.el.getValue();
21286         if(v === this.emptyText){
21287             v = '';
21288         }
21289         return v;
21290     },
21291
21292     /**
21293      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21294      * @return {Mixed} value The field value
21295      */
21296     getValue : function(){
21297         var v = this.el.getValue();
21298         if(v === this.emptyText || v === undefined){
21299             v = '';
21300         }
21301         return v;
21302     },
21303
21304     /**
21305      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21306      * @param {Mixed} value The value to set
21307      */
21308     setRawValue : function(v){
21309         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21310     },
21311
21312     /**
21313      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21314      * @param {Mixed} value The value to set
21315      */
21316     setValue : function(v){
21317         this.value = v;
21318         if(this.rendered){
21319             this.el.dom.value = (v === null || v === undefined ? '' : v);
21320             this.validate();
21321         }
21322     },
21323
21324     adjustSize : function(w, h){
21325         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21326         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21327         return s;
21328     },
21329
21330     adjustWidth : function(tag, w){
21331         tag = tag.toLowerCase();
21332         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21333             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21334                 if(tag == 'input'){
21335                     return w + 2;
21336                 }
21337                 if(tag = 'textarea'){
21338                     return w-2;
21339                 }
21340             }else if(Roo.isOpera){
21341                 if(tag == 'input'){
21342                     return w + 2;
21343                 }
21344                 if(tag = 'textarea'){
21345                     return w-2;
21346                 }
21347             }
21348         }
21349         return w;
21350     }
21351 });
21352
21353
21354 // anything other than normal should be considered experimental
21355 Roo.form.Field.msgFx = {
21356     normal : {
21357         show: function(msgEl, f){
21358             msgEl.setDisplayed('block');
21359         },
21360
21361         hide : function(msgEl, f){
21362             msgEl.setDisplayed(false).update('');
21363         }
21364     },
21365
21366     slide : {
21367         show: function(msgEl, f){
21368             msgEl.slideIn('t', {stopFx:true});
21369         },
21370
21371         hide : function(msgEl, f){
21372             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21373         }
21374     },
21375
21376     slideRight : {
21377         show: function(msgEl, f){
21378             msgEl.fixDisplay();
21379             msgEl.alignTo(f.el, 'tl-tr');
21380             msgEl.slideIn('l', {stopFx:true});
21381         },
21382
21383         hide : function(msgEl, f){
21384             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21385         }
21386     }
21387 };/*
21388  * Based on:
21389  * Ext JS Library 1.1.1
21390  * Copyright(c) 2006-2007, Ext JS, LLC.
21391  *
21392  * Originally Released Under LGPL - original licence link has changed is not relivant.
21393  *
21394  * Fork - LGPL
21395  * <script type="text/javascript">
21396  */
21397  
21398
21399 /**
21400  * @class Roo.form.TextField
21401  * @extends Roo.form.Field
21402  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21403  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21404  * @constructor
21405  * Creates a new TextField
21406  * @param {Object} config Configuration options
21407  */
21408 Roo.form.TextField = function(config){
21409     Roo.form.TextField.superclass.constructor.call(this, config);
21410     this.addEvents({
21411         /**
21412          * @event autosize
21413          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21414          * according to the default logic, but this event provides a hook for the developer to apply additional
21415          * logic at runtime to resize the field if needed.
21416              * @param {Roo.form.Field} this This text field
21417              * @param {Number} width The new field width
21418              */
21419         autosize : true
21420     });
21421 };
21422
21423 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21424     /**
21425      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21426      */
21427     grow : false,
21428     /**
21429      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21430      */
21431     growMin : 30,
21432     /**
21433      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21434      */
21435     growMax : 800,
21436     /**
21437      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21438      */
21439     vtype : null,
21440     /**
21441      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21442      */
21443     maskRe : null,
21444     /**
21445      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21446      */
21447     disableKeyFilter : false,
21448     /**
21449      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21450      */
21451     allowBlank : true,
21452     /**
21453      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21454      */
21455     minLength : 0,
21456     /**
21457      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21458      */
21459     maxLength : Number.MAX_VALUE,
21460     /**
21461      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21462      */
21463     minLengthText : "The minimum length for this field is {0}",
21464     /**
21465      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21466      */
21467     maxLengthText : "The maximum length for this field is {0}",
21468     /**
21469      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21470      */
21471     selectOnFocus : false,
21472     /**
21473      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21474      */
21475     blankText : "This field is required",
21476     /**
21477      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21478      * If available, this function will be called only after the basic validators all return true, and will be passed the
21479      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21480      */
21481     validator : null,
21482     /**
21483      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21484      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21485      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21486      */
21487     regex : null,
21488     /**
21489      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21490      */
21491     regexText : "",
21492     /**
21493      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21494      */
21495     emptyText : null,
21496     /**
21497      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21498      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21499      */
21500     emptyClass : 'x-form-empty-field',
21501
21502     // private
21503     initEvents : function(){
21504         Roo.form.TextField.superclass.initEvents.call(this);
21505         if(this.validationEvent == 'keyup'){
21506             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21507             this.el.on('keyup', this.filterValidation, this);
21508         }
21509         else if(this.validationEvent !== false){
21510             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21511         }
21512         if(this.selectOnFocus || this.emptyText){
21513             this.on("focus", this.preFocus, this);
21514             if(this.emptyText){
21515                 this.on('blur', this.postBlur, this);
21516                 this.applyEmptyText();
21517             }
21518         }
21519         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21520             this.el.on("keypress", this.filterKeys, this);
21521         }
21522         if(this.grow){
21523             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21524             this.el.on("click", this.autoSize,  this);
21525         }
21526     },
21527
21528     processValue : function(value){
21529         if(this.stripCharsRe){
21530             var newValue = value.replace(this.stripCharsRe, '');
21531             if(newValue !== value){
21532                 this.setRawValue(newValue);
21533                 return newValue;
21534             }
21535         }
21536         return value;
21537     },
21538
21539     filterValidation : function(e){
21540         if(!e.isNavKeyPress()){
21541             this.validationTask.delay(this.validationDelay);
21542         }
21543     },
21544
21545     // private
21546     onKeyUp : function(e){
21547         if(!e.isNavKeyPress()){
21548             this.autoSize();
21549         }
21550     },
21551
21552     /**
21553      * Resets the current field value to the originally-loaded value and clears any validation messages.
21554      * Also adds emptyText and emptyClass if the original value was blank.
21555      */
21556     reset : function(){
21557         Roo.form.TextField.superclass.reset.call(this);
21558         this.applyEmptyText();
21559     },
21560
21561     applyEmptyText : function(){
21562         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21563             this.setRawValue(this.emptyText);
21564             this.el.addClass(this.emptyClass);
21565         }
21566     },
21567
21568     // private
21569     preFocus : function(){
21570         if(this.emptyText){
21571             if(this.el.dom.value == this.emptyText){
21572                 this.setRawValue('');
21573             }
21574             this.el.removeClass(this.emptyClass);
21575         }
21576         if(this.selectOnFocus){
21577             this.el.dom.select();
21578         }
21579     },
21580
21581     // private
21582     postBlur : function(){
21583         this.applyEmptyText();
21584     },
21585
21586     // private
21587     filterKeys : function(e){
21588         var k = e.getKey();
21589         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21590             return;
21591         }
21592         var c = e.getCharCode(), cc = String.fromCharCode(c);
21593         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21594             return;
21595         }
21596         if(!this.maskRe.test(cc)){
21597             e.stopEvent();
21598         }
21599     },
21600
21601     setValue : function(v){
21602         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21603             this.el.removeClass(this.emptyClass);
21604         }
21605         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21606         this.applyEmptyText();
21607         this.autoSize();
21608     },
21609
21610     /**
21611      * Validates a value according to the field's validation rules and marks the field as invalid
21612      * if the validation fails
21613      * @param {Mixed} value The value to validate
21614      * @return {Boolean} True if the value is valid, else false
21615      */
21616     validateValue : function(value){
21617         if(value.length < 1 || value === this.emptyText){ // if it's blank
21618              if(this.allowBlank){
21619                 this.clearInvalid();
21620                 return true;
21621              }else{
21622                 this.markInvalid(this.blankText);
21623                 return false;
21624              }
21625         }
21626         if(value.length < this.minLength){
21627             this.markInvalid(String.format(this.minLengthText, this.minLength));
21628             return false;
21629         }
21630         if(value.length > this.maxLength){
21631             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21632             return false;
21633         }
21634         if(this.vtype){
21635             var vt = Roo.form.VTypes;
21636             if(!vt[this.vtype](value, this)){
21637                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21638                 return false;
21639             }
21640         }
21641         if(typeof this.validator == "function"){
21642             var msg = this.validator(value);
21643             if(msg !== true){
21644                 this.markInvalid(msg);
21645                 return false;
21646             }
21647         }
21648         if(this.regex && !this.regex.test(value)){
21649             this.markInvalid(this.regexText);
21650             return false;
21651         }
21652         return true;
21653     },
21654
21655     /**
21656      * Selects text in this field
21657      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21658      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21659      */
21660     selectText : function(start, end){
21661         var v = this.getRawValue();
21662         if(v.length > 0){
21663             start = start === undefined ? 0 : start;
21664             end = end === undefined ? v.length : end;
21665             var d = this.el.dom;
21666             if(d.setSelectionRange){
21667                 d.setSelectionRange(start, end);
21668             }else if(d.createTextRange){
21669                 var range = d.createTextRange();
21670                 range.moveStart("character", start);
21671                 range.moveEnd("character", v.length-end);
21672                 range.select();
21673             }
21674         }
21675     },
21676
21677     /**
21678      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21679      * This only takes effect if grow = true, and fires the autosize event.
21680      */
21681     autoSize : function(){
21682         if(!this.grow || !this.rendered){
21683             return;
21684         }
21685         if(!this.metrics){
21686             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21687         }
21688         var el = this.el;
21689         var v = el.dom.value;
21690         var d = document.createElement('div');
21691         d.appendChild(document.createTextNode(v));
21692         v = d.innerHTML;
21693         d = null;
21694         v += "&#160;";
21695         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21696         this.el.setWidth(w);
21697         this.fireEvent("autosize", this, w);
21698     }
21699 });/*
21700  * Based on:
21701  * Ext JS Library 1.1.1
21702  * Copyright(c) 2006-2007, Ext JS, LLC.
21703  *
21704  * Originally Released Under LGPL - original licence link has changed is not relivant.
21705  *
21706  * Fork - LGPL
21707  * <script type="text/javascript">
21708  */
21709  
21710 /**
21711  * @class Roo.form.Hidden
21712  * @extends Roo.form.TextField
21713  * Simple Hidden element used on forms 
21714  * 
21715  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21716  * 
21717  * @constructor
21718  * Creates a new Hidden form element.
21719  * @param {Object} config Configuration options
21720  */
21721
21722
21723
21724 // easy hidden field...
21725 Roo.form.Hidden = function(config){
21726     Roo.form.Hidden.superclass.constructor.call(this, config);
21727 };
21728   
21729 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21730     fieldLabel:      '',
21731     inputType:      'hidden',
21732     width:          50,
21733     allowBlank:     true,
21734     labelSeparator: '',
21735     hidden:         true,
21736     itemCls :       'x-form-item-display-none'
21737
21738
21739 });
21740
21741
21742 /*
21743  * Based on:
21744  * Ext JS Library 1.1.1
21745  * Copyright(c) 2006-2007, Ext JS, LLC.
21746  *
21747  * Originally Released Under LGPL - original licence link has changed is not relivant.
21748  *
21749  * Fork - LGPL
21750  * <script type="text/javascript">
21751  */
21752  
21753 /**
21754  * @class Roo.form.TriggerField
21755  * @extends Roo.form.TextField
21756  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21757  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21758  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21759  * for which you can provide a custom implementation.  For example:
21760  * <pre><code>
21761 var trigger = new Roo.form.TriggerField();
21762 trigger.onTriggerClick = myTriggerFn;
21763 trigger.applyTo('my-field');
21764 </code></pre>
21765  *
21766  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21767  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21768  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21769  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21770  * @constructor
21771  * Create a new TriggerField.
21772  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21773  * to the base TextField)
21774  */
21775 Roo.form.TriggerField = function(config){
21776     this.mimicing = false;
21777     Roo.form.TriggerField.superclass.constructor.call(this, config);
21778 };
21779
21780 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21781     /**
21782      * @cfg {String} triggerClass A CSS class to apply to the trigger
21783      */
21784     /**
21785      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21786      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21787      */
21788     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21789     /**
21790      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21791      */
21792     hideTrigger:false,
21793
21794     /** @cfg {Boolean} grow @hide */
21795     /** @cfg {Number} growMin @hide */
21796     /** @cfg {Number} growMax @hide */
21797
21798     /**
21799      * @hide 
21800      * @method
21801      */
21802     autoSize: Roo.emptyFn,
21803     // private
21804     monitorTab : true,
21805     // private
21806     deferHeight : true,
21807
21808     
21809     actionMode : 'wrap',
21810     // private
21811     onResize : function(w, h){
21812         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21813         if(typeof w == 'number'){
21814             var x = w - this.trigger.getWidth();
21815             this.el.setWidth(this.adjustWidth('input', x));
21816             this.trigger.setStyle('left', x+'px');
21817         }
21818     },
21819
21820     // private
21821     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21822
21823     // private
21824     getResizeEl : function(){
21825         return this.wrap;
21826     },
21827
21828     // private
21829     getPositionEl : function(){
21830         return this.wrap;
21831     },
21832
21833     // private
21834     alignErrorIcon : function(){
21835         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21836     },
21837
21838     // private
21839     onRender : function(ct, position){
21840         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21841         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21842         this.trigger = this.wrap.createChild(this.triggerConfig ||
21843                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21844         if(this.hideTrigger){
21845             this.trigger.setDisplayed(false);
21846         }
21847         this.initTrigger();
21848         if(!this.width){
21849             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21850         }
21851     },
21852
21853     // private
21854     initTrigger : function(){
21855         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21856         this.trigger.addClassOnOver('x-form-trigger-over');
21857         this.trigger.addClassOnClick('x-form-trigger-click');
21858     },
21859
21860     // private
21861     onDestroy : function(){
21862         if(this.trigger){
21863             this.trigger.removeAllListeners();
21864             this.trigger.remove();
21865         }
21866         if(this.wrap){
21867             this.wrap.remove();
21868         }
21869         Roo.form.TriggerField.superclass.onDestroy.call(this);
21870     },
21871
21872     // private
21873     onFocus : function(){
21874         Roo.form.TriggerField.superclass.onFocus.call(this);
21875         if(!this.mimicing){
21876             this.wrap.addClass('x-trigger-wrap-focus');
21877             this.mimicing = true;
21878             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21879             if(this.monitorTab){
21880                 this.el.on("keydown", this.checkTab, this);
21881             }
21882         }
21883     },
21884
21885     // private
21886     checkTab : function(e){
21887         if(e.getKey() == e.TAB){
21888             this.triggerBlur();
21889         }
21890     },
21891
21892     // private
21893     onBlur : function(){
21894         // do nothing
21895     },
21896
21897     // private
21898     mimicBlur : function(e, t){
21899         if(!this.wrap.contains(t) && this.validateBlur()){
21900             this.triggerBlur();
21901         }
21902     },
21903
21904     // private
21905     triggerBlur : function(){
21906         this.mimicing = false;
21907         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21908         if(this.monitorTab){
21909             this.el.un("keydown", this.checkTab, this);
21910         }
21911         this.wrap.removeClass('x-trigger-wrap-focus');
21912         Roo.form.TriggerField.superclass.onBlur.call(this);
21913     },
21914
21915     // private
21916     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21917     validateBlur : function(e, t){
21918         return true;
21919     },
21920
21921     // private
21922     onDisable : function(){
21923         Roo.form.TriggerField.superclass.onDisable.call(this);
21924         if(this.wrap){
21925             this.wrap.addClass('x-item-disabled');
21926         }
21927     },
21928
21929     // private
21930     onEnable : function(){
21931         Roo.form.TriggerField.superclass.onEnable.call(this);
21932         if(this.wrap){
21933             this.wrap.removeClass('x-item-disabled');
21934         }
21935     },
21936
21937     // private
21938     onShow : function(){
21939         var ae = this.getActionEl();
21940         
21941         if(ae){
21942             ae.dom.style.display = '';
21943             ae.dom.style.visibility = 'visible';
21944         }
21945     },
21946
21947     // private
21948     
21949     onHide : function(){
21950         var ae = this.getActionEl();
21951         ae.dom.style.display = 'none';
21952     },
21953
21954     /**
21955      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21956      * by an implementing function.
21957      * @method
21958      * @param {EventObject} e
21959      */
21960     onTriggerClick : Roo.emptyFn
21961 });
21962
21963 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21964 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21965 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21966 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21967     initComponent : function(){
21968         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21969
21970         this.triggerConfig = {
21971             tag:'span', cls:'x-form-twin-triggers', cn:[
21972             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21973             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21974         ]};
21975     },
21976
21977     getTrigger : function(index){
21978         return this.triggers[index];
21979     },
21980
21981     initTrigger : function(){
21982         var ts = this.trigger.select('.x-form-trigger', true);
21983         this.wrap.setStyle('overflow', 'hidden');
21984         var triggerField = this;
21985         ts.each(function(t, all, index){
21986             t.hide = function(){
21987                 var w = triggerField.wrap.getWidth();
21988                 this.dom.style.display = 'none';
21989                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21990             };
21991             t.show = function(){
21992                 var w = triggerField.wrap.getWidth();
21993                 this.dom.style.display = '';
21994                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21995             };
21996             var triggerIndex = 'Trigger'+(index+1);
21997
21998             if(this['hide'+triggerIndex]){
21999                 t.dom.style.display = 'none';
22000             }
22001             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22002             t.addClassOnOver('x-form-trigger-over');
22003             t.addClassOnClick('x-form-trigger-click');
22004         }, this);
22005         this.triggers = ts.elements;
22006     },
22007
22008     onTrigger1Click : Roo.emptyFn,
22009     onTrigger2Click : Roo.emptyFn
22010 });/*
22011  * Based on:
22012  * Ext JS Library 1.1.1
22013  * Copyright(c) 2006-2007, Ext JS, LLC.
22014  *
22015  * Originally Released Under LGPL - original licence link has changed is not relivant.
22016  *
22017  * Fork - LGPL
22018  * <script type="text/javascript">
22019  */
22020  
22021 /**
22022  * @class Roo.form.TextArea
22023  * @extends Roo.form.TextField
22024  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22025  * support for auto-sizing.
22026  * @constructor
22027  * Creates a new TextArea
22028  * @param {Object} config Configuration options
22029  */
22030 Roo.form.TextArea = function(config){
22031     Roo.form.TextArea.superclass.constructor.call(this, config);
22032     // these are provided exchanges for backwards compat
22033     // minHeight/maxHeight were replaced by growMin/growMax to be
22034     // compatible with TextField growing config values
22035     if(this.minHeight !== undefined){
22036         this.growMin = this.minHeight;
22037     }
22038     if(this.maxHeight !== undefined){
22039         this.growMax = this.maxHeight;
22040     }
22041 };
22042
22043 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22044     /**
22045      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22046      */
22047     growMin : 60,
22048     /**
22049      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22050      */
22051     growMax: 1000,
22052     /**
22053      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22054      * in the field (equivalent to setting overflow: hidden, defaults to false)
22055      */
22056     preventScrollbars: false,
22057     /**
22058      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22059      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22060      */
22061
22062     // private
22063     onRender : function(ct, position){
22064         if(!this.el){
22065             this.defaultAutoCreate = {
22066                 tag: "textarea",
22067                 style:"width:300px;height:60px;",
22068                 autocomplete: "off"
22069             };
22070         }
22071         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22072         if(this.grow){
22073             this.textSizeEl = Roo.DomHelper.append(document.body, {
22074                 tag: "pre", cls: "x-form-grow-sizer"
22075             });
22076             if(this.preventScrollbars){
22077                 this.el.setStyle("overflow", "hidden");
22078             }
22079             this.el.setHeight(this.growMin);
22080         }
22081     },
22082
22083     onDestroy : function(){
22084         if(this.textSizeEl){
22085             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22086         }
22087         Roo.form.TextArea.superclass.onDestroy.call(this);
22088     },
22089
22090     // private
22091     onKeyUp : function(e){
22092         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22093             this.autoSize();
22094         }
22095     },
22096
22097     /**
22098      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22099      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22100      */
22101     autoSize : function(){
22102         if(!this.grow || !this.textSizeEl){
22103             return;
22104         }
22105         var el = this.el;
22106         var v = el.dom.value;
22107         var ts = this.textSizeEl;
22108
22109         ts.innerHTML = '';
22110         ts.appendChild(document.createTextNode(v));
22111         v = ts.innerHTML;
22112
22113         Roo.fly(ts).setWidth(this.el.getWidth());
22114         if(v.length < 1){
22115             v = "&#160;&#160;";
22116         }else{
22117             if(Roo.isIE){
22118                 v = v.replace(/\n/g, '<p>&#160;</p>');
22119             }
22120             v += "&#160;\n&#160;";
22121         }
22122         ts.innerHTML = v;
22123         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22124         if(h != this.lastHeight){
22125             this.lastHeight = h;
22126             this.el.setHeight(h);
22127             this.fireEvent("autosize", this, h);
22128         }
22129     }
22130 });/*
22131  * Based on:
22132  * Ext JS Library 1.1.1
22133  * Copyright(c) 2006-2007, Ext JS, LLC.
22134  *
22135  * Originally Released Under LGPL - original licence link has changed is not relivant.
22136  *
22137  * Fork - LGPL
22138  * <script type="text/javascript">
22139  */
22140  
22141
22142 /**
22143  * @class Roo.form.NumberField
22144  * @extends Roo.form.TextField
22145  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22146  * @constructor
22147  * Creates a new NumberField
22148  * @param {Object} config Configuration options
22149  */
22150 Roo.form.NumberField = function(config){
22151     Roo.form.NumberField.superclass.constructor.call(this, config);
22152 };
22153
22154 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22155     /**
22156      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22157      */
22158     fieldClass: "x-form-field x-form-num-field",
22159     /**
22160      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22161      */
22162     allowDecimals : true,
22163     /**
22164      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22165      */
22166     decimalSeparator : ".",
22167     /**
22168      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22169      */
22170     decimalPrecision : 2,
22171     /**
22172      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22173      */
22174     allowNegative : true,
22175     /**
22176      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22177      */
22178     minValue : Number.NEGATIVE_INFINITY,
22179     /**
22180      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22181      */
22182     maxValue : Number.MAX_VALUE,
22183     /**
22184      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22185      */
22186     minText : "The minimum value for this field is {0}",
22187     /**
22188      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22189      */
22190     maxText : "The maximum value for this field is {0}",
22191     /**
22192      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22193      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22194      */
22195     nanText : "{0} is not a valid number",
22196
22197     // private
22198     initEvents : function(){
22199         Roo.form.NumberField.superclass.initEvents.call(this);
22200         var allowed = "0123456789";
22201         if(this.allowDecimals){
22202             allowed += this.decimalSeparator;
22203         }
22204         if(this.allowNegative){
22205             allowed += "-";
22206         }
22207         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22208         var keyPress = function(e){
22209             var k = e.getKey();
22210             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22211                 return;
22212             }
22213             var c = e.getCharCode();
22214             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22215                 e.stopEvent();
22216             }
22217         };
22218         this.el.on("keypress", keyPress, this);
22219     },
22220
22221     // private
22222     validateValue : function(value){
22223         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22224             return false;
22225         }
22226         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22227              return true;
22228         }
22229         var num = this.parseValue(value);
22230         if(isNaN(num)){
22231             this.markInvalid(String.format(this.nanText, value));
22232             return false;
22233         }
22234         if(num < this.minValue){
22235             this.markInvalid(String.format(this.minText, this.minValue));
22236             return false;
22237         }
22238         if(num > this.maxValue){
22239             this.markInvalid(String.format(this.maxText, this.maxValue));
22240             return false;
22241         }
22242         return true;
22243     },
22244
22245     getValue : function(){
22246         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22247     },
22248
22249     // private
22250     parseValue : function(value){
22251         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22252         return isNaN(value) ? '' : value;
22253     },
22254
22255     // private
22256     fixPrecision : function(value){
22257         var nan = isNaN(value);
22258         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22259             return nan ? '' : value;
22260         }
22261         return parseFloat(value).toFixed(this.decimalPrecision);
22262     },
22263
22264     setValue : function(v){
22265         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22266     },
22267
22268     // private
22269     decimalPrecisionFcn : function(v){
22270         return Math.floor(v);
22271     },
22272
22273     beforeBlur : function(){
22274         var v = this.parseValue(this.getRawValue());
22275         if(v){
22276             this.setValue(this.fixPrecision(v));
22277         }
22278     }
22279 });/*
22280  * Based on:
22281  * Ext JS Library 1.1.1
22282  * Copyright(c) 2006-2007, Ext JS, LLC.
22283  *
22284  * Originally Released Under LGPL - original licence link has changed is not relivant.
22285  *
22286  * Fork - LGPL
22287  * <script type="text/javascript">
22288  */
22289  
22290 /**
22291  * @class Roo.form.DateField
22292  * @extends Roo.form.TriggerField
22293  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22294 * @constructor
22295 * Create a new DateField
22296 * @param {Object} config
22297  */
22298 Roo.form.DateField = function(config){
22299     Roo.form.DateField.superclass.constructor.call(this, config);
22300     
22301       this.addEvents({
22302          
22303         /**
22304          * @event select
22305          * Fires when a date is selected
22306              * @param {Roo.form.DateField} combo This combo box
22307              * @param {Date} date The date selected
22308              */
22309         'select' : true
22310          
22311     });
22312     
22313     
22314     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22315     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22316     this.ddMatch = null;
22317     if(this.disabledDates){
22318         var dd = this.disabledDates;
22319         var re = "(?:";
22320         for(var i = 0; i < dd.length; i++){
22321             re += dd[i];
22322             if(i != dd.length-1) re += "|";
22323         }
22324         this.ddMatch = new RegExp(re + ")");
22325     }
22326 };
22327
22328 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22329     /**
22330      * @cfg {String} format
22331      * The default date format string which can be overriden for localization support.  The format must be
22332      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22333      */
22334     format : "m/d/y",
22335     /**
22336      * @cfg {String} altFormats
22337      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22338      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22339      */
22340     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22341     /**
22342      * @cfg {Array} disabledDays
22343      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22344      */
22345     disabledDays : null,
22346     /**
22347      * @cfg {String} disabledDaysText
22348      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22349      */
22350     disabledDaysText : "Disabled",
22351     /**
22352      * @cfg {Array} disabledDates
22353      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22354      * expression so they are very powerful. Some examples:
22355      * <ul>
22356      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22357      * <li>["03/08", "09/16"] would disable those days for every year</li>
22358      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22359      * <li>["03/../2006"] would disable every day in March 2006</li>
22360      * <li>["^03"] would disable every day in every March</li>
22361      * </ul>
22362      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22363      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22364      */
22365     disabledDates : null,
22366     /**
22367      * @cfg {String} disabledDatesText
22368      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22369      */
22370     disabledDatesText : "Disabled",
22371     /**
22372      * @cfg {Date/String} minValue
22373      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22374      * valid format (defaults to null).
22375      */
22376     minValue : null,
22377     /**
22378      * @cfg {Date/String} maxValue
22379      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22380      * valid format (defaults to null).
22381      */
22382     maxValue : null,
22383     /**
22384      * @cfg {String} minText
22385      * The error text to display when the date in the cell is before minValue (defaults to
22386      * 'The date in this field must be after {minValue}').
22387      */
22388     minText : "The date in this field must be equal to or after {0}",
22389     /**
22390      * @cfg {String} maxText
22391      * The error text to display when the date in the cell is after maxValue (defaults to
22392      * 'The date in this field must be before {maxValue}').
22393      */
22394     maxText : "The date in this field must be equal to or before {0}",
22395     /**
22396      * @cfg {String} invalidText
22397      * The error text to display when the date in the field is invalid (defaults to
22398      * '{value} is not a valid date - it must be in the format {format}').
22399      */
22400     invalidText : "{0} is not a valid date - it must be in the format {1}",
22401     /**
22402      * @cfg {String} triggerClass
22403      * An additional CSS class used to style the trigger button.  The trigger will always get the
22404      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22405      * which displays a calendar icon).
22406      */
22407     triggerClass : 'x-form-date-trigger',
22408     
22409
22410     /**
22411      * @cfg {bool} useIso
22412      * if enabled, then the date field will use a hidden field to store the 
22413      * real value as iso formated date. default (false)
22414      */ 
22415     useIso : false,
22416     /**
22417      * @cfg {String/Object} autoCreate
22418      * A DomHelper element spec, or true for a default element spec (defaults to
22419      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22420      */ 
22421     // private
22422     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22423     
22424     // private
22425     hiddenField: false,
22426     
22427     onRender : function(ct, position)
22428     {
22429         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22430         if (this.useIso) {
22431             this.el.dom.removeAttribute('name'); 
22432             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22433                     'before', true);
22434             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22435             // prevent input submission
22436             this.hiddenName = this.name;
22437         }
22438             
22439             
22440     },
22441     
22442     // private
22443     validateValue : function(value)
22444     {
22445         value = this.formatDate(value);
22446         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22447             return false;
22448         }
22449         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22450              return true;
22451         }
22452         var svalue = value;
22453         value = this.parseDate(value);
22454         if(!value){
22455             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22456             return false;
22457         }
22458         var time = value.getTime();
22459         if(this.minValue && time < this.minValue.getTime()){
22460             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22461             return false;
22462         }
22463         if(this.maxValue && time > this.maxValue.getTime()){
22464             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22465             return false;
22466         }
22467         if(this.disabledDays){
22468             var day = value.getDay();
22469             for(var i = 0; i < this.disabledDays.length; i++) {
22470                 if(day === this.disabledDays[i]){
22471                     this.markInvalid(this.disabledDaysText);
22472                     return false;
22473                 }
22474             }
22475         }
22476         var fvalue = this.formatDate(value);
22477         if(this.ddMatch && this.ddMatch.test(fvalue)){
22478             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22479             return false;
22480         }
22481         return true;
22482     },
22483
22484     // private
22485     // Provides logic to override the default TriggerField.validateBlur which just returns true
22486     validateBlur : function(){
22487         return !this.menu || !this.menu.isVisible();
22488     },
22489
22490     /**
22491      * Returns the current date value of the date field.
22492      * @return {Date} The date value
22493      */
22494     getValue : function(){
22495         
22496         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22497     },
22498
22499     /**
22500      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22501      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22502      * (the default format used is "m/d/y").
22503      * <br />Usage:
22504      * <pre><code>
22505 //All of these calls set the same date value (May 4, 2006)
22506
22507 //Pass a date object:
22508 var dt = new Date('5/4/06');
22509 dateField.setValue(dt);
22510
22511 //Pass a date string (default format):
22512 dateField.setValue('5/4/06');
22513
22514 //Pass a date string (custom format):
22515 dateField.format = 'Y-m-d';
22516 dateField.setValue('2006-5-4');
22517 </code></pre>
22518      * @param {String/Date} date The date or valid date string
22519      */
22520     setValue : function(date){
22521         if (this.hiddenField) {
22522             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22523         }
22524         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22525     },
22526
22527     // private
22528     parseDate : function(value){
22529         if(!value || value instanceof Date){
22530             return value;
22531         }
22532         var v = Date.parseDate(value, this.format);
22533         if(!v && this.altFormats){
22534             if(!this.altFormatsArray){
22535                 this.altFormatsArray = this.altFormats.split("|");
22536             }
22537             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22538                 v = Date.parseDate(value, this.altFormatsArray[i]);
22539             }
22540         }
22541         return v;
22542     },
22543
22544     // private
22545     formatDate : function(date, fmt){
22546         return (!date || !(date instanceof Date)) ?
22547                date : date.dateFormat(fmt || this.format);
22548     },
22549
22550     // private
22551     menuListeners : {
22552         select: function(m, d){
22553             this.setValue(d);
22554             this.fireEvent('select', this, d);
22555         },
22556         show : function(){ // retain focus styling
22557             this.onFocus();
22558         },
22559         hide : function(){
22560             this.focus.defer(10, this);
22561             var ml = this.menuListeners;
22562             this.menu.un("select", ml.select,  this);
22563             this.menu.un("show", ml.show,  this);
22564             this.menu.un("hide", ml.hide,  this);
22565         }
22566     },
22567
22568     // private
22569     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22570     onTriggerClick : function(){
22571         if(this.disabled){
22572             return;
22573         }
22574         if(this.menu == null){
22575             this.menu = new Roo.menu.DateMenu();
22576         }
22577         Roo.apply(this.menu.picker,  {
22578             showClear: this.allowBlank,
22579             minDate : this.minValue,
22580             maxDate : this.maxValue,
22581             disabledDatesRE : this.ddMatch,
22582             disabledDatesText : this.disabledDatesText,
22583             disabledDays : this.disabledDays,
22584             disabledDaysText : this.disabledDaysText,
22585             format : this.format,
22586             minText : String.format(this.minText, this.formatDate(this.minValue)),
22587             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22588         });
22589         this.menu.on(Roo.apply({}, this.menuListeners, {
22590             scope:this
22591         }));
22592         this.menu.picker.setValue(this.getValue() || new Date());
22593         this.menu.show(this.el, "tl-bl?");
22594     },
22595
22596     beforeBlur : function(){
22597         var v = this.parseDate(this.getRawValue());
22598         if(v){
22599             this.setValue(v);
22600         }
22601     }
22602
22603     /** @cfg {Boolean} grow @hide */
22604     /** @cfg {Number} growMin @hide */
22605     /** @cfg {Number} growMax @hide */
22606     /**
22607      * @hide
22608      * @method autoSize
22609      */
22610 });/*
22611  * Based on:
22612  * Ext JS Library 1.1.1
22613  * Copyright(c) 2006-2007, Ext JS, LLC.
22614  *
22615  * Originally Released Under LGPL - original licence link has changed is not relivant.
22616  *
22617  * Fork - LGPL
22618  * <script type="text/javascript">
22619  */
22620  
22621
22622 /**
22623  * @class Roo.form.ComboBox
22624  * @extends Roo.form.TriggerField
22625  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22626  * @constructor
22627  * Create a new ComboBox.
22628  * @param {Object} config Configuration options
22629  */
22630 Roo.form.ComboBox = function(config){
22631     Roo.form.ComboBox.superclass.constructor.call(this, config);
22632     this.addEvents({
22633         /**
22634          * @event expand
22635          * Fires when the dropdown list is expanded
22636              * @param {Roo.form.ComboBox} combo This combo box
22637              */
22638         'expand' : true,
22639         /**
22640          * @event collapse
22641          * Fires when the dropdown list is collapsed
22642              * @param {Roo.form.ComboBox} combo This combo box
22643              */
22644         'collapse' : true,
22645         /**
22646          * @event beforeselect
22647          * Fires before a list item is selected. Return false to cancel the selection.
22648              * @param {Roo.form.ComboBox} combo This combo box
22649              * @param {Roo.data.Record} record The data record returned from the underlying store
22650              * @param {Number} index The index of the selected item in the dropdown list
22651              */
22652         'beforeselect' : true,
22653         /**
22654          * @event select
22655          * Fires when a list item is selected
22656              * @param {Roo.form.ComboBox} combo This combo box
22657              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22658              * @param {Number} index The index of the selected item in the dropdown list
22659              */
22660         'select' : true,
22661         /**
22662          * @event beforequery
22663          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22664          * The event object passed has these properties:
22665              * @param {Roo.form.ComboBox} combo This combo box
22666              * @param {String} query The query
22667              * @param {Boolean} forceAll true to force "all" query
22668              * @param {Boolean} cancel true to cancel the query
22669              * @param {Object} e The query event object
22670              */
22671         'beforequery': true,
22672          /**
22673          * @event add
22674          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22675              * @param {Roo.form.ComboBox} combo This combo box
22676              */
22677         'add' : true,
22678         /**
22679          * @event edit
22680          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22681              * @param {Roo.form.ComboBox} combo This combo box
22682              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22683              */
22684         'edit' : true
22685         
22686         
22687     });
22688     if(this.transform){
22689         this.allowDomMove = false;
22690         var s = Roo.getDom(this.transform);
22691         if(!this.hiddenName){
22692             this.hiddenName = s.name;
22693         }
22694         if(!this.store){
22695             this.mode = 'local';
22696             var d = [], opts = s.options;
22697             for(var i = 0, len = opts.length;i < len; i++){
22698                 var o = opts[i];
22699                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22700                 if(o.selected) {
22701                     this.value = value;
22702                 }
22703                 d.push([value, o.text]);
22704             }
22705             this.store = new Roo.data.SimpleStore({
22706                 'id': 0,
22707                 fields: ['value', 'text'],
22708                 data : d
22709             });
22710             this.valueField = 'value';
22711             this.displayField = 'text';
22712         }
22713         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22714         if(!this.lazyRender){
22715             this.target = true;
22716             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22717             s.parentNode.removeChild(s); // remove it
22718             this.render(this.el.parentNode);
22719         }else{
22720             s.parentNode.removeChild(s); // remove it
22721         }
22722
22723     }
22724     if (this.store) {
22725         this.store = Roo.factory(this.store, Roo.data);
22726     }
22727     
22728     this.selectedIndex = -1;
22729     if(this.mode == 'local'){
22730         if(config.queryDelay === undefined){
22731             this.queryDelay = 10;
22732         }
22733         if(config.minChars === undefined){
22734             this.minChars = 0;
22735         }
22736     }
22737 };
22738
22739 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22740     /**
22741      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22742      */
22743     /**
22744      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22745      * rendering into an Roo.Editor, defaults to false)
22746      */
22747     /**
22748      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22749      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22750      */
22751     /**
22752      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22753      */
22754     /**
22755      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22756      * the dropdown list (defaults to undefined, with no header element)
22757      */
22758
22759      /**
22760      * @cfg {String/Roo.Template} tpl The template to use to render the output
22761      */
22762      
22763     // private
22764     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22765     /**
22766      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22767      */
22768     listWidth: undefined,
22769     /**
22770      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22771      * mode = 'remote' or 'text' if mode = 'local')
22772      */
22773     displayField: undefined,
22774     /**
22775      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22776      * mode = 'remote' or 'value' if mode = 'local'). 
22777      * Note: use of a valueField requires the user make a selection
22778      * in order for a value to be mapped.
22779      */
22780     valueField: undefined,
22781     /**
22782      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22783      * field's data value (defaults to the underlying DOM element's name)
22784      */
22785     hiddenName: undefined,
22786     /**
22787      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22788      */
22789     listClass: '',
22790     /**
22791      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22792      */
22793     selectedClass: 'x-combo-selected',
22794     /**
22795      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22796      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22797      * which displays a downward arrow icon).
22798      */
22799     triggerClass : 'x-form-arrow-trigger',
22800     /**
22801      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22802      */
22803     shadow:'sides',
22804     /**
22805      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22806      * anchor positions (defaults to 'tl-bl')
22807      */
22808     listAlign: 'tl-bl?',
22809     /**
22810      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22811      */
22812     maxHeight: 300,
22813     /**
22814      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22815      * query specified by the allQuery config option (defaults to 'query')
22816      */
22817     triggerAction: 'query',
22818     /**
22819      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22820      * (defaults to 4, does not apply if editable = false)
22821      */
22822     minChars : 4,
22823     /**
22824      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22825      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22826      */
22827     typeAhead: false,
22828     /**
22829      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22830      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22831      */
22832     queryDelay: 500,
22833     /**
22834      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22835      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22836      */
22837     pageSize: 0,
22838     /**
22839      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22840      * when editable = true (defaults to false)
22841      */
22842     selectOnFocus:false,
22843     /**
22844      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22845      */
22846     queryParam: 'query',
22847     /**
22848      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22849      * when mode = 'remote' (defaults to 'Loading...')
22850      */
22851     loadingText: 'Loading...',
22852     /**
22853      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22854      */
22855     resizable: false,
22856     /**
22857      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22858      */
22859     handleHeight : 8,
22860     /**
22861      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22862      * traditional select (defaults to true)
22863      */
22864     editable: true,
22865     /**
22866      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22867      */
22868     allQuery: '',
22869     /**
22870      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22871      */
22872     mode: 'remote',
22873     /**
22874      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22875      * listWidth has a higher value)
22876      */
22877     minListWidth : 70,
22878     /**
22879      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22880      * allow the user to set arbitrary text into the field (defaults to false)
22881      */
22882     forceSelection:false,
22883     /**
22884      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22885      * if typeAhead = true (defaults to 250)
22886      */
22887     typeAheadDelay : 250,
22888     /**
22889      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22890      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22891      */
22892     valueNotFoundText : undefined,
22893     /**
22894      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22895      */
22896     blockFocus : false,
22897     
22898     /**
22899      * @cfg {Boolean} disableClear Disable showing of clear button.
22900      */
22901     disableClear : false,
22902     /**
22903      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22904      */
22905     alwaysQuery : false,
22906     
22907     //private
22908     addicon : false,
22909     editicon: false,
22910     
22911     
22912     // private
22913     onRender : function(ct, position){
22914         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22915         if(this.hiddenName){
22916             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22917                     'before', true);
22918             this.hiddenField.value =
22919                 this.hiddenValue !== undefined ? this.hiddenValue :
22920                 this.value !== undefined ? this.value : '';
22921
22922             // prevent input submission
22923             this.el.dom.removeAttribute('name');
22924         }
22925         if(Roo.isGecko){
22926             this.el.dom.setAttribute('autocomplete', 'off');
22927         }
22928
22929         var cls = 'x-combo-list';
22930
22931         this.list = new Roo.Layer({
22932             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22933         });
22934
22935         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22936         this.list.setWidth(lw);
22937         this.list.swallowEvent('mousewheel');
22938         this.assetHeight = 0;
22939
22940         if(this.title){
22941             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22942             this.assetHeight += this.header.getHeight();
22943         }
22944
22945         this.innerList = this.list.createChild({cls:cls+'-inner'});
22946         this.innerList.on('mouseover', this.onViewOver, this);
22947         this.innerList.on('mousemove', this.onViewMove, this);
22948         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22949         
22950         if(this.allowBlank && !this.pageSize && !this.disableClear){
22951             this.footer = this.list.createChild({cls:cls+'-ft'});
22952             this.pageTb = new Roo.Toolbar(this.footer);
22953            
22954         }
22955         if(this.pageSize){
22956             this.footer = this.list.createChild({cls:cls+'-ft'});
22957             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22958                     {pageSize: this.pageSize});
22959             
22960         }
22961         
22962         if (this.pageTb && this.allowBlank && !this.disableClear) {
22963             var _this = this;
22964             this.pageTb.add(new Roo.Toolbar.Fill(), {
22965                 cls: 'x-btn-icon x-btn-clear',
22966                 text: '&#160;',
22967                 handler: function()
22968                 {
22969                     _this.collapse();
22970                     _this.clearValue();
22971                     _this.onSelect(false, -1);
22972                 }
22973             });
22974         }
22975         if (this.footer) {
22976             this.assetHeight += this.footer.getHeight();
22977         }
22978         
22979
22980         if(!this.tpl){
22981             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22982         }
22983
22984         this.view = new Roo.View(this.innerList, this.tpl, {
22985             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22986         });
22987
22988         this.view.on('click', this.onViewClick, this);
22989
22990         this.store.on('beforeload', this.onBeforeLoad, this);
22991         this.store.on('load', this.onLoad, this);
22992         this.store.on('loadexception', this.collapse, this);
22993
22994         if(this.resizable){
22995             this.resizer = new Roo.Resizable(this.list,  {
22996                pinned:true, handles:'se'
22997             });
22998             this.resizer.on('resize', function(r, w, h){
22999                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23000                 this.listWidth = w;
23001                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23002                 this.restrictHeight();
23003             }, this);
23004             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23005         }
23006         if(!this.editable){
23007             this.editable = true;
23008             this.setEditable(false);
23009         }  
23010         
23011         
23012         if (typeof(this.events.add.listeners) != 'undefined') {
23013             
23014             this.addicon = this.wrap.createChild(
23015                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23016        
23017             this.addicon.on('click', function(e) {
23018                 this.fireEvent('add', this);
23019             }, this);
23020         }
23021         if (typeof(this.events.edit.listeners) != 'undefined') {
23022             
23023             this.editicon = this.wrap.createChild(
23024                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23025             if (this.addicon) {
23026                 this.editicon.setStyle('margin-left', '40px');
23027             }
23028             this.editicon.on('click', function(e) {
23029                 
23030                 // we fire even  if inothing is selected..
23031                 this.fireEvent('edit', this, this.lastData );
23032                 
23033             }, this);
23034         }
23035         
23036         
23037         
23038     },
23039
23040     // private
23041     initEvents : function(){
23042         Roo.form.ComboBox.superclass.initEvents.call(this);
23043
23044         this.keyNav = new Roo.KeyNav(this.el, {
23045             "up" : function(e){
23046                 this.inKeyMode = true;
23047                 this.selectPrev();
23048             },
23049
23050             "down" : function(e){
23051                 if(!this.isExpanded()){
23052                     this.onTriggerClick();
23053                 }else{
23054                     this.inKeyMode = true;
23055                     this.selectNext();
23056                 }
23057             },
23058
23059             "enter" : function(e){
23060                 this.onViewClick();
23061                 //return true;
23062             },
23063
23064             "esc" : function(e){
23065                 this.collapse();
23066             },
23067
23068             "tab" : function(e){
23069                 this.onViewClick(false);
23070                 return true;
23071             },
23072
23073             scope : this,
23074
23075             doRelay : function(foo, bar, hname){
23076                 if(hname == 'down' || this.scope.isExpanded()){
23077                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23078                 }
23079                 return true;
23080             },
23081
23082             forceKeyDown: true
23083         });
23084         this.queryDelay = Math.max(this.queryDelay || 10,
23085                 this.mode == 'local' ? 10 : 250);
23086         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23087         if(this.typeAhead){
23088             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23089         }
23090         if(this.editable !== false){
23091             this.el.on("keyup", this.onKeyUp, this);
23092         }
23093         if(this.forceSelection){
23094             this.on('blur', this.doForce, this);
23095         }
23096     },
23097
23098     onDestroy : function(){
23099         if(this.view){
23100             this.view.setStore(null);
23101             this.view.el.removeAllListeners();
23102             this.view.el.remove();
23103             this.view.purgeListeners();
23104         }
23105         if(this.list){
23106             this.list.destroy();
23107         }
23108         if(this.store){
23109             this.store.un('beforeload', this.onBeforeLoad, this);
23110             this.store.un('load', this.onLoad, this);
23111             this.store.un('loadexception', this.collapse, this);
23112         }
23113         Roo.form.ComboBox.superclass.onDestroy.call(this);
23114     },
23115
23116     // private
23117     fireKey : function(e){
23118         if(e.isNavKeyPress() && !this.list.isVisible()){
23119             this.fireEvent("specialkey", this, e);
23120         }
23121     },
23122
23123     // private
23124     onResize: function(w, h){
23125         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23126         
23127         if(typeof w != 'number'){
23128             // we do not handle it!?!?
23129             return;
23130         }
23131         var tw = this.trigger.getWidth();
23132         tw += this.addicon ? this.addicon.getWidth() : 0;
23133         tw += this.editicon ? this.editicon.getWidth() : 0;
23134         var x = w - tw;
23135         this.el.setWidth( this.adjustWidth('input', x));
23136             
23137         this.trigger.setStyle('left', x+'px');
23138         
23139         if(this.list && this.listWidth === undefined){
23140             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23141             this.list.setWidth(lw);
23142             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23143         }
23144         
23145     
23146         
23147     },
23148
23149     /**
23150      * Allow or prevent the user from directly editing the field text.  If false is passed,
23151      * the user will only be able to select from the items defined in the dropdown list.  This method
23152      * is the runtime equivalent of setting the 'editable' config option at config time.
23153      * @param {Boolean} value True to allow the user to directly edit the field text
23154      */
23155     setEditable : function(value){
23156         if(value == this.editable){
23157             return;
23158         }
23159         this.editable = value;
23160         if(!value){
23161             this.el.dom.setAttribute('readOnly', true);
23162             this.el.on('mousedown', this.onTriggerClick,  this);
23163             this.el.addClass('x-combo-noedit');
23164         }else{
23165             this.el.dom.setAttribute('readOnly', false);
23166             this.el.un('mousedown', this.onTriggerClick,  this);
23167             this.el.removeClass('x-combo-noedit');
23168         }
23169     },
23170
23171     // private
23172     onBeforeLoad : function(){
23173         if(!this.hasFocus){
23174             return;
23175         }
23176         this.innerList.update(this.loadingText ?
23177                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23178         this.restrictHeight();
23179         this.selectedIndex = -1;
23180     },
23181
23182     // private
23183     onLoad : function(){
23184         if(!this.hasFocus){
23185             return;
23186         }
23187         if(this.store.getCount() > 0){
23188             this.expand();
23189             this.restrictHeight();
23190             if(this.lastQuery == this.allQuery){
23191                 if(this.editable){
23192                     this.el.dom.select();
23193                 }
23194                 if(!this.selectByValue(this.value, true)){
23195                     this.select(0, true);
23196                 }
23197             }else{
23198                 this.selectNext();
23199                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23200                     this.taTask.delay(this.typeAheadDelay);
23201                 }
23202             }
23203         }else{
23204             this.onEmptyResults();
23205         }
23206         //this.el.focus();
23207     },
23208
23209     // private
23210     onTypeAhead : function(){
23211         if(this.store.getCount() > 0){
23212             var r = this.store.getAt(0);
23213             var newValue = r.data[this.displayField];
23214             var len = newValue.length;
23215             var selStart = this.getRawValue().length;
23216             if(selStart != len){
23217                 this.setRawValue(newValue);
23218                 this.selectText(selStart, newValue.length);
23219             }
23220         }
23221     },
23222
23223     // private
23224     onSelect : function(record, index){
23225         if(this.fireEvent('beforeselect', this, record, index) !== false){
23226             this.setFromData(index > -1 ? record.data : false);
23227             this.collapse();
23228             this.fireEvent('select', this, record, index);
23229         }
23230     },
23231
23232     /**
23233      * Returns the currently selected field value or empty string if no value is set.
23234      * @return {String} value The selected value
23235      */
23236     getValue : function(){
23237         if(this.valueField){
23238             return typeof this.value != 'undefined' ? this.value : '';
23239         }else{
23240             return Roo.form.ComboBox.superclass.getValue.call(this);
23241         }
23242     },
23243
23244     /**
23245      * Clears any text/value currently set in the field
23246      */
23247     clearValue : function(){
23248         if(this.hiddenField){
23249             this.hiddenField.value = '';
23250         }
23251         this.value = '';
23252         this.setRawValue('');
23253         this.lastSelectionText = '';
23254         this.applyEmptyText();
23255     },
23256
23257     /**
23258      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23259      * will be displayed in the field.  If the value does not match the data value of an existing item,
23260      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23261      * Otherwise the field will be blank (although the value will still be set).
23262      * @param {String} value The value to match
23263      */
23264     setValue : function(v){
23265         var text = v;
23266         if(this.valueField){
23267             var r = this.findRecord(this.valueField, v);
23268             if(r){
23269                 text = r.data[this.displayField];
23270             }else if(this.valueNotFoundText !== undefined){
23271                 text = this.valueNotFoundText;
23272             }
23273         }
23274         this.lastSelectionText = text;
23275         if(this.hiddenField){
23276             this.hiddenField.value = v;
23277         }
23278         Roo.form.ComboBox.superclass.setValue.call(this, text);
23279         this.value = v;
23280     },
23281     /**
23282      * @property {Object} the last set data for the element
23283      */
23284     
23285     lastData : false,
23286     /**
23287      * Sets the value of the field based on a object which is related to the record format for the store.
23288      * @param {Object} value the value to set as. or false on reset?
23289      */
23290     setFromData : function(o){
23291         var dv = ''; // display value
23292         var vv = ''; // value value..
23293         this.lastData = o;
23294         if (this.displayField) {
23295             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23296         } else {
23297             // this is an error condition!!!
23298             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23299         }
23300         
23301         if(this.valueField){
23302             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23303         }
23304         if(this.hiddenField){
23305             this.hiddenField.value = vv;
23306             
23307             this.lastSelectionText = dv;
23308             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23309             this.value = vv;
23310             return;
23311         }
23312         // no hidden field.. - we store the value in 'value', but still display
23313         // display field!!!!
23314         this.lastSelectionText = dv;
23315         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23316         this.value = vv;
23317         
23318         
23319     },
23320     // private
23321     reset : function(){
23322         // overridden so that last data is reset..
23323         this.setValue(this.originalValue);
23324         this.clearInvalid();
23325         this.lastData = false;
23326     },
23327     // private
23328     findRecord : function(prop, value){
23329         var record;
23330         if(this.store.getCount() > 0){
23331             this.store.each(function(r){
23332                 if(r.data[prop] == value){
23333                     record = r;
23334                     return false;
23335                 }
23336             });
23337         }
23338         return record;
23339     },
23340
23341     // private
23342     onViewMove : function(e, t){
23343         this.inKeyMode = false;
23344     },
23345
23346     // private
23347     onViewOver : function(e, t){
23348         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23349             return;
23350         }
23351         var item = this.view.findItemFromChild(t);
23352         if(item){
23353             var index = this.view.indexOf(item);
23354             this.select(index, false);
23355         }
23356     },
23357
23358     // private
23359     onViewClick : function(doFocus){
23360         var index = this.view.getSelectedIndexes()[0];
23361         var r = this.store.getAt(index);
23362         if(r){
23363             this.onSelect(r, index);
23364         }
23365         if(doFocus !== false && !this.blockFocus){
23366             this.el.focus();
23367         }
23368     },
23369
23370     // private
23371     restrictHeight : function(){
23372         this.innerList.dom.style.height = '';
23373         var inner = this.innerList.dom;
23374         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23375         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23376         this.list.beginUpdate();
23377         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23378         this.list.alignTo(this.el, this.listAlign);
23379         this.list.endUpdate();
23380     },
23381
23382     // private
23383     onEmptyResults : function(){
23384         this.collapse();
23385     },
23386
23387     /**
23388      * Returns true if the dropdown list is expanded, else false.
23389      */
23390     isExpanded : function(){
23391         return this.list.isVisible();
23392     },
23393
23394     /**
23395      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23396      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23397      * @param {String} value The data value of the item to select
23398      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23399      * selected item if it is not currently in view (defaults to true)
23400      * @return {Boolean} True if the value matched an item in the list, else false
23401      */
23402     selectByValue : function(v, scrollIntoView){
23403         if(v !== undefined && v !== null){
23404             var r = this.findRecord(this.valueField || this.displayField, v);
23405             if(r){
23406                 this.select(this.store.indexOf(r), scrollIntoView);
23407                 return true;
23408             }
23409         }
23410         return false;
23411     },
23412
23413     /**
23414      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23415      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23416      * @param {Number} index The zero-based index of the list item to select
23417      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23418      * selected item if it is not currently in view (defaults to true)
23419      */
23420     select : function(index, scrollIntoView){
23421         this.selectedIndex = index;
23422         this.view.select(index);
23423         if(scrollIntoView !== false){
23424             var el = this.view.getNode(index);
23425             if(el){
23426                 this.innerList.scrollChildIntoView(el, false);
23427             }
23428         }
23429     },
23430
23431     // private
23432     selectNext : function(){
23433         var ct = this.store.getCount();
23434         if(ct > 0){
23435             if(this.selectedIndex == -1){
23436                 this.select(0);
23437             }else if(this.selectedIndex < ct-1){
23438                 this.select(this.selectedIndex+1);
23439             }
23440         }
23441     },
23442
23443     // private
23444     selectPrev : function(){
23445         var ct = this.store.getCount();
23446         if(ct > 0){
23447             if(this.selectedIndex == -1){
23448                 this.select(0);
23449             }else if(this.selectedIndex != 0){
23450                 this.select(this.selectedIndex-1);
23451             }
23452         }
23453     },
23454
23455     // private
23456     onKeyUp : function(e){
23457         if(this.editable !== false && !e.isSpecialKey()){
23458             this.lastKey = e.getKey();
23459             this.dqTask.delay(this.queryDelay);
23460         }
23461     },
23462
23463     // private
23464     validateBlur : function(){
23465         return !this.list || !this.list.isVisible();   
23466     },
23467
23468     // private
23469     initQuery : function(){
23470         this.doQuery(this.getRawValue());
23471     },
23472
23473     // private
23474     doForce : function(){
23475         if(this.el.dom.value.length > 0){
23476             this.el.dom.value =
23477                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23478             this.applyEmptyText();
23479         }
23480     },
23481
23482     /**
23483      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23484      * query allowing the query action to be canceled if needed.
23485      * @param {String} query The SQL query to execute
23486      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23487      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23488      * saved in the current store (defaults to false)
23489      */
23490     doQuery : function(q, forceAll){
23491         if(q === undefined || q === null){
23492             q = '';
23493         }
23494         var qe = {
23495             query: q,
23496             forceAll: forceAll,
23497             combo: this,
23498             cancel:false
23499         };
23500         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23501             return false;
23502         }
23503         q = qe.query;
23504         forceAll = qe.forceAll;
23505         if(forceAll === true || (q.length >= this.minChars)){
23506             if(this.lastQuery != q || this.alwaysQuery){
23507                 this.lastQuery = q;
23508                 if(this.mode == 'local'){
23509                     this.selectedIndex = -1;
23510                     if(forceAll){
23511                         this.store.clearFilter();
23512                     }else{
23513                         this.store.filter(this.displayField, q);
23514                     }
23515                     this.onLoad();
23516                 }else{
23517                     this.store.baseParams[this.queryParam] = q;
23518                     this.store.load({
23519                         params: this.getParams(q)
23520                     });
23521                     this.expand();
23522                 }
23523             }else{
23524                 this.selectedIndex = -1;
23525                 this.onLoad();   
23526             }
23527         }
23528     },
23529
23530     // private
23531     getParams : function(q){
23532         var p = {};
23533         //p[this.queryParam] = q;
23534         if(this.pageSize){
23535             p.start = 0;
23536             p.limit = this.pageSize;
23537         }
23538         return p;
23539     },
23540
23541     /**
23542      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23543      */
23544     collapse : function(){
23545         if(!this.isExpanded()){
23546             return;
23547         }
23548         this.list.hide();
23549         Roo.get(document).un('mousedown', this.collapseIf, this);
23550         Roo.get(document).un('mousewheel', this.collapseIf, this);
23551         if (!this.editable) {
23552             Roo.get(document).un('keydown', this.listKeyPress, this);
23553         }
23554         this.fireEvent('collapse', this);
23555     },
23556
23557     // private
23558     collapseIf : function(e){
23559         if(!e.within(this.wrap) && !e.within(this.list)){
23560             this.collapse();
23561         }
23562     },
23563
23564     /**
23565      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23566      */
23567     expand : function(){
23568         if(this.isExpanded() || !this.hasFocus){
23569             return;
23570         }
23571         this.list.alignTo(this.el, this.listAlign);
23572         this.list.show();
23573         Roo.get(document).on('mousedown', this.collapseIf, this);
23574         Roo.get(document).on('mousewheel', this.collapseIf, this);
23575         if (!this.editable) {
23576             Roo.get(document).on('keydown', this.listKeyPress, this);
23577         }
23578         
23579         this.fireEvent('expand', this);
23580     },
23581
23582     // private
23583     // Implements the default empty TriggerField.onTriggerClick function
23584     onTriggerClick : function(){
23585         if(this.disabled){
23586             return;
23587         }
23588         if(this.isExpanded()){
23589             this.collapse();
23590             if (!this.blockFocus) {
23591                 this.el.focus();
23592             }
23593             
23594         }else {
23595             this.hasFocus = true;
23596             if(this.triggerAction == 'all') {
23597                 this.doQuery(this.allQuery, true);
23598             } else {
23599                 this.doQuery(this.getRawValue());
23600             }
23601             if (!this.blockFocus) {
23602                 this.el.focus();
23603             }
23604         }
23605     },
23606     listKeyPress : function(e)
23607     {
23608         //Roo.log('listkeypress');
23609         // scroll to first matching element based on key pres..
23610         if (e.isSpecialKey()) {
23611             return false;
23612         }
23613         var k = String.fromCharCode(e.getKey()).toUpperCase();
23614         //Roo.log(k);
23615         var match  = false;
23616         var csel = this.view.getSelectedNodes();
23617         var cselitem = false;
23618         if (csel.length) {
23619             var ix = this.view.indexOf(csel[0]);
23620             cselitem  = this.store.getAt(ix);
23621             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23622                 cselitem = false;
23623             }
23624             
23625         }
23626         
23627         this.store.each(function(v) { 
23628             if (cselitem) {
23629                 // start at existing selection.
23630                 if (cselitem.id == v.id) {
23631                     cselitem = false;
23632                 }
23633                 return;
23634             }
23635                 
23636             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23637                 match = this.store.indexOf(v);
23638                 return false;
23639             }
23640         }, this);
23641         
23642         if (match === false) {
23643             return true; // no more action?
23644         }
23645         // scroll to?
23646         this.view.select(match);
23647         var sn = Roo.get(this.view.getSelectedNodes()[0])
23648         sn.scrollIntoView(sn.dom.parentNode, false);
23649     }
23650
23651     /** 
23652     * @cfg {Boolean} grow 
23653     * @hide 
23654     */
23655     /** 
23656     * @cfg {Number} growMin 
23657     * @hide 
23658     */
23659     /** 
23660     * @cfg {Number} growMax 
23661     * @hide 
23662     */
23663     /**
23664      * @hide
23665      * @method autoSize
23666      */
23667 });/*
23668  * Based on:
23669  * Ext JS Library 1.1.1
23670  * Copyright(c) 2006-2007, Ext JS, LLC.
23671  *
23672  * Originally Released Under LGPL - original licence link has changed is not relivant.
23673  *
23674  * Fork - LGPL
23675  * <script type="text/javascript">
23676  */
23677 /**
23678  * @class Roo.form.Checkbox
23679  * @extends Roo.form.Field
23680  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23681  * @constructor
23682  * Creates a new Checkbox
23683  * @param {Object} config Configuration options
23684  */
23685 Roo.form.Checkbox = function(config){
23686     Roo.form.Checkbox.superclass.constructor.call(this, config);
23687     this.addEvents({
23688         /**
23689          * @event check
23690          * Fires when the checkbox is checked or unchecked.
23691              * @param {Roo.form.Checkbox} this This checkbox
23692              * @param {Boolean} checked The new checked value
23693              */
23694         check : true
23695     });
23696 };
23697
23698 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23699     /**
23700      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23701      */
23702     focusClass : undefined,
23703     /**
23704      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23705      */
23706     fieldClass: "x-form-field",
23707     /**
23708      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23709      */
23710     checked: false,
23711     /**
23712      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23713      * {tag: "input", type: "checkbox", autocomplete: "off"})
23714      */
23715     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23716     /**
23717      * @cfg {String} boxLabel The text that appears beside the checkbox
23718      */
23719     boxLabel : "",
23720     /**
23721      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23722      */  
23723     inputValue : '1',
23724     /**
23725      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23726      */
23727      valueOff: '0', // value when not checked..
23728
23729     actionMode : 'viewEl', 
23730     //
23731     // private
23732     itemCls : 'x-menu-check-item x-form-item',
23733     groupClass : 'x-menu-group-item',
23734     inputType : 'hidden',
23735     
23736     
23737     inSetChecked: false, // check that we are not calling self...
23738     
23739     inputElement: false, // real input element?
23740     basedOn: false, // ????
23741     
23742     isFormField: true, // not sure where this is needed!!!!
23743
23744     onResize : function(){
23745         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23746         if(!this.boxLabel){
23747             this.el.alignTo(this.wrap, 'c-c');
23748         }
23749     },
23750
23751     initEvents : function(){
23752         Roo.form.Checkbox.superclass.initEvents.call(this);
23753         this.el.on("click", this.onClick,  this);
23754         this.el.on("change", this.onClick,  this);
23755     },
23756
23757
23758     getResizeEl : function(){
23759         return this.wrap;
23760     },
23761
23762     getPositionEl : function(){
23763         return this.wrap;
23764     },
23765
23766     // private
23767     onRender : function(ct, position){
23768         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23769         /*
23770         if(this.inputValue !== undefined){
23771             this.el.dom.value = this.inputValue;
23772         }
23773         */
23774         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23775         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23776         var viewEl = this.wrap.createChild({ 
23777             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23778         this.viewEl = viewEl;   
23779         this.wrap.on('click', this.onClick,  this); 
23780         
23781         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23782         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23783         
23784         
23785         
23786         if(this.boxLabel){
23787             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23788         //    viewEl.on('click', this.onClick,  this); 
23789         }
23790         //if(this.checked){
23791             this.setChecked(this.checked);
23792         //}else{
23793             //this.checked = this.el.dom;
23794         //}
23795
23796     },
23797
23798     // private
23799     initValue : Roo.emptyFn,
23800
23801     /**
23802      * Returns the checked state of the checkbox.
23803      * @return {Boolean} True if checked, else false
23804      */
23805     getValue : function(){
23806         if(this.el){
23807             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23808         }
23809         return this.valueOff;
23810         
23811     },
23812
23813         // private
23814     onClick : function(){ 
23815         this.setChecked(!this.checked);
23816
23817         //if(this.el.dom.checked != this.checked){
23818         //    this.setValue(this.el.dom.checked);
23819        // }
23820     },
23821
23822     /**
23823      * Sets the checked state of the checkbox.
23824      * On is always based on a string comparison between inputValue and the param.
23825      * @param {Boolean/String} value - the value to set 
23826      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23827      */
23828     setValue : function(v,suppressEvent){
23829         
23830         
23831         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23832         //if(this.el && this.el.dom){
23833         //    this.el.dom.checked = this.checked;
23834         //    this.el.dom.defaultChecked = this.checked;
23835         //}
23836         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23837         //this.fireEvent("check", this, this.checked);
23838     },
23839     // private..
23840     setChecked : function(state,suppressEvent)
23841     {
23842         if (this.inSetChecked) {
23843             this.checked = state;
23844             return;
23845         }
23846         
23847     
23848         if(this.wrap){
23849             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23850         }
23851         this.checked = state;
23852         if(suppressEvent !== true){
23853             this.fireEvent('check', this, state);
23854         }
23855         this.inSetChecked = true;
23856         this.el.dom.value = state ? this.inputValue : this.valueOff;
23857         this.inSetChecked = false;
23858         
23859     },
23860     // handle setting of hidden value by some other method!!?!?
23861     setFromHidden: function()
23862     {
23863         if(!this.el){
23864             return;
23865         }
23866         //console.log("SET FROM HIDDEN");
23867         //alert('setFrom hidden');
23868         this.setValue(this.el.dom.value);
23869     },
23870     
23871     onDestroy : function()
23872     {
23873         if(this.viewEl){
23874             Roo.get(this.viewEl).remove();
23875         }
23876          
23877         Roo.form.Checkbox.superclass.onDestroy.call(this);
23878     }
23879
23880 });/*
23881  * Based on:
23882  * Ext JS Library 1.1.1
23883  * Copyright(c) 2006-2007, Ext JS, LLC.
23884  *
23885  * Originally Released Under LGPL - original licence link has changed is not relivant.
23886  *
23887  * Fork - LGPL
23888  * <script type="text/javascript">
23889  */
23890  
23891 /**
23892  * @class Roo.form.Radio
23893  * @extends Roo.form.Checkbox
23894  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23895  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23896  * @constructor
23897  * Creates a new Radio
23898  * @param {Object} config Configuration options
23899  */
23900 Roo.form.Radio = function(){
23901     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23902 };
23903 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23904     inputType: 'radio',
23905
23906     /**
23907      * If this radio is part of a group, it will return the selected value
23908      * @return {String}
23909      */
23910     getGroupValue : function(){
23911         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23912     }
23913 });//<script type="text/javascript">
23914
23915 /*
23916  * Ext JS Library 1.1.1
23917  * Copyright(c) 2006-2007, Ext JS, LLC.
23918  * licensing@extjs.com
23919  * 
23920  * http://www.extjs.com/license
23921  */
23922  
23923  /*
23924   * 
23925   * Known bugs:
23926   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23927   * - IE ? - no idea how much works there.
23928   * 
23929   * 
23930   * 
23931   */
23932  
23933
23934 /**
23935  * @class Ext.form.HtmlEditor
23936  * @extends Ext.form.Field
23937  * Provides a lightweight HTML Editor component.
23938  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23939  * 
23940  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23941  * supported by this editor.</b><br/><br/>
23942  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23943  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23944  */
23945 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23946       /**
23947      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23948      */
23949     toolbars : false,
23950     /**
23951      * @cfg {String} createLinkText The default text for the create link prompt
23952      */
23953     createLinkText : 'Please enter the URL for the link:',
23954     /**
23955      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23956      */
23957     defaultLinkValue : 'http:/'+'/',
23958    
23959      /**
23960      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23961      *                        Roo.resizable.
23962      */
23963     resizable : false,
23964      /**
23965      * @cfg {Number} height (in pixels)
23966      */   
23967     height: 300,
23968    /**
23969      * @cfg {Number} width (in pixels)
23970      */   
23971     width: 500,
23972     // id of frame..
23973     frameId: false,
23974     
23975     // private properties
23976     validationEvent : false,
23977     deferHeight: true,
23978     initialized : false,
23979     activated : false,
23980     sourceEditMode : false,
23981     onFocus : Roo.emptyFn,
23982     iframePad:3,
23983     hideMode:'offsets',
23984     
23985     defaultAutoCreate : { // modified by initCompnoent..
23986         tag: "textarea",
23987         style:"width:500px;height:300px;",
23988         autocomplete: "off"
23989     },
23990
23991     // private
23992     initComponent : function(){
23993         this.addEvents({
23994             /**
23995              * @event initialize
23996              * Fires when the editor is fully initialized (including the iframe)
23997              * @param {HtmlEditor} this
23998              */
23999             initialize: true,
24000             /**
24001              * @event activate
24002              * Fires when the editor is first receives the focus. Any insertion must wait
24003              * until after this event.
24004              * @param {HtmlEditor} this
24005              */
24006             activate: true,
24007              /**
24008              * @event beforesync
24009              * Fires before the textarea is updated with content from the editor iframe. Return false
24010              * to cancel the sync.
24011              * @param {HtmlEditor} this
24012              * @param {String} html
24013              */
24014             beforesync: true,
24015              /**
24016              * @event beforepush
24017              * Fires before the iframe editor is updated with content from the textarea. Return false
24018              * to cancel the push.
24019              * @param {HtmlEditor} this
24020              * @param {String} html
24021              */
24022             beforepush: true,
24023              /**
24024              * @event sync
24025              * Fires when the textarea is updated with content from the editor iframe.
24026              * @param {HtmlEditor} this
24027              * @param {String} html
24028              */
24029             sync: true,
24030              /**
24031              * @event push
24032              * Fires when the iframe editor is updated with content from the textarea.
24033              * @param {HtmlEditor} this
24034              * @param {String} html
24035              */
24036             push: true,
24037              /**
24038              * @event editmodechange
24039              * Fires when the editor switches edit modes
24040              * @param {HtmlEditor} this
24041              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24042              */
24043             editmodechange: true,
24044             /**
24045              * @event editorevent
24046              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24047              * @param {HtmlEditor} this
24048              */
24049             editorevent: true
24050         });
24051         this.defaultAutoCreate =  {
24052             tag: "textarea",
24053             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24054             autocomplete: "off"
24055         };
24056     },
24057
24058     /**
24059      * Protected method that will not generally be called directly. It
24060      * is called when the editor creates its toolbar. Override this method if you need to
24061      * add custom toolbar buttons.
24062      * @param {HtmlEditor} editor
24063      */
24064     createToolbar : function(editor){
24065         if (!editor.toolbars || !editor.toolbars.length) {
24066             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24067         }
24068         
24069         for (var i =0 ; i < editor.toolbars.length;i++) {
24070             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24071             editor.toolbars[i].init(editor);
24072         }
24073          
24074         
24075     },
24076
24077     /**
24078      * Protected method that will not generally be called directly. It
24079      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24080      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24081      */
24082     getDocMarkup : function(){
24083         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24084     },
24085
24086     // private
24087     onRender : function(ct, position)
24088     {
24089         var _t = this;
24090         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24091         this.el.dom.style.border = '0 none';
24092         this.el.dom.setAttribute('tabIndex', -1);
24093         this.el.addClass('x-hidden');
24094         if(Roo.isIE){ // fix IE 1px bogus margin
24095             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24096         }
24097         this.wrap = this.el.wrap({
24098             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24099         });
24100         
24101         if (this.resizable) {
24102             this.resizeEl = new Roo.Resizable(this.wrap, {
24103                 pinned : true,
24104                 wrap: true,
24105                 dynamic : true,
24106                 minHeight : this.height,
24107                 height: this.height,
24108                 handles : this.resizable,
24109                 width: this.width,
24110                 listeners : {
24111                     resize : function(r, w, h) {
24112                         _t.onResize(w,h); // -something
24113                     }
24114                 }
24115             });
24116             
24117         }
24118
24119         this.frameId = Roo.id();
24120         this.createToolbar(this);
24121         
24122         
24123         
24124         
24125       
24126         
24127         var iframe = this.wrap.createChild({
24128             tag: 'iframe',
24129             id: this.frameId,
24130             name: this.frameId,
24131             frameBorder : 'no',
24132             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24133         });
24134         
24135        // console.log(iframe);
24136         //this.wrap.dom.appendChild(iframe);
24137
24138         this.iframe = iframe.dom;
24139
24140          this.assignDocWin();
24141         
24142         this.doc.designMode = 'on';
24143        
24144         this.doc.open();
24145         this.doc.write(this.getDocMarkup());
24146         this.doc.close();
24147
24148         
24149         var task = { // must defer to wait for browser to be ready
24150             run : function(){
24151                 //console.log("run task?" + this.doc.readyState);
24152                 this.assignDocWin();
24153                 if(this.doc.body || this.doc.readyState == 'complete'){
24154                     try {
24155                         this.doc.designMode="on";
24156                     } catch (e) {
24157                         return;
24158                     }
24159                     Roo.TaskMgr.stop(task);
24160                     this.initEditor.defer(10, this);
24161                 }
24162             },
24163             interval : 10,
24164             duration:10000,
24165             scope: this
24166         };
24167         Roo.TaskMgr.start(task);
24168
24169         if(!this.width){
24170             this.setSize(this.wrap.getSize());
24171         }
24172         if (this.resizeEl) {
24173             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24174             // should trigger onReize..
24175         }
24176     },
24177
24178     // private
24179     onResize : function(w, h)
24180     {
24181         Roo.log('resize: ' +w + ',' + h );
24182         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24183         if(this.el && this.iframe){
24184             if(typeof w == 'number'){
24185                 var aw = w - this.wrap.getFrameWidth('lr');
24186                 this.el.setWidth(this.adjustWidth('textarea', aw));
24187                 this.iframe.style.width = aw + 'px';
24188             }
24189             if(typeof h == 'number'){
24190                 var tbh = 0;
24191                 for (var i =0; i < this.toolbars.length;i++) {
24192                     // fixme - ask toolbars for heights?
24193                     tbh += this.toolbars[i].tb.el.getHeight();
24194                 }
24195                 
24196                 
24197                 
24198                 
24199                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24200                 ah -= 10; // knock a few pixes off for look..
24201                 this.el.setHeight(this.adjustWidth('textarea', ah));
24202                 this.iframe.style.height = ah + 'px';
24203                 if(this.doc){
24204                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24205                 }
24206             }
24207         }
24208     },
24209
24210     /**
24211      * Toggles the editor between standard and source edit mode.
24212      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24213      */
24214     toggleSourceEdit : function(sourceEditMode){
24215         
24216         this.sourceEditMode = sourceEditMode === true;
24217         
24218         if(this.sourceEditMode){
24219           
24220             this.syncValue();
24221             this.iframe.className = 'x-hidden';
24222             this.el.removeClass('x-hidden');
24223             this.el.dom.removeAttribute('tabIndex');
24224             this.el.focus();
24225         }else{
24226              
24227             this.pushValue();
24228             this.iframe.className = '';
24229             this.el.addClass('x-hidden');
24230             this.el.dom.setAttribute('tabIndex', -1);
24231             this.deferFocus();
24232         }
24233         this.setSize(this.wrap.getSize());
24234         this.fireEvent('editmodechange', this, this.sourceEditMode);
24235     },
24236
24237     // private used internally
24238     createLink : function(){
24239         var url = prompt(this.createLinkText, this.defaultLinkValue);
24240         if(url && url != 'http:/'+'/'){
24241             this.relayCmd('createlink', url);
24242         }
24243     },
24244
24245     // private (for BoxComponent)
24246     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24247
24248     // private (for BoxComponent)
24249     getResizeEl : function(){
24250         return this.wrap;
24251     },
24252
24253     // private (for BoxComponent)
24254     getPositionEl : function(){
24255         return this.wrap;
24256     },
24257
24258     // private
24259     initEvents : function(){
24260         this.originalValue = this.getValue();
24261     },
24262
24263     /**
24264      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24265      * @method
24266      */
24267     markInvalid : Roo.emptyFn,
24268     /**
24269      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24270      * @method
24271      */
24272     clearInvalid : Roo.emptyFn,
24273
24274     setValue : function(v){
24275         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24276         this.pushValue();
24277     },
24278
24279     /**
24280      * Protected method that will not generally be called directly. If you need/want
24281      * custom HTML cleanup, this is the method you should override.
24282      * @param {String} html The HTML to be cleaned
24283      * return {String} The cleaned HTML
24284      */
24285     cleanHtml : function(html){
24286         html = String(html);
24287         if(html.length > 5){
24288             if(Roo.isSafari){ // strip safari nonsense
24289                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24290             }
24291         }
24292         if(html == '&nbsp;'){
24293             html = '';
24294         }
24295         return html;
24296     },
24297
24298     /**
24299      * Protected method that will not generally be called directly. Syncs the contents
24300      * of the editor iframe with the textarea.
24301      */
24302     syncValue : function(){
24303         if(this.initialized){
24304             var bd = (this.doc.body || this.doc.documentElement);
24305             this.cleanUpPaste();
24306             var html = bd.innerHTML;
24307             if(Roo.isSafari){
24308                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24309                 var m = bs.match(/text-align:(.*?);/i);
24310                 if(m && m[1]){
24311                     html = '<div style="'+m[0]+'">' + html + '</div>';
24312                 }
24313             }
24314             html = this.cleanHtml(html);
24315             if(this.fireEvent('beforesync', this, html) !== false){
24316                 this.el.dom.value = html;
24317                 this.fireEvent('sync', this, html);
24318             }
24319         }
24320     },
24321
24322     /**
24323      * Protected method that will not generally be called directly. Pushes the value of the textarea
24324      * into the iframe editor.
24325      */
24326     pushValue : function(){
24327         if(this.initialized){
24328             var v = this.el.dom.value;
24329             if(v.length < 1){
24330                 v = '&#160;';
24331             }
24332             
24333             if(this.fireEvent('beforepush', this, v) !== false){
24334                 var d = (this.doc.body || this.doc.documentElement);
24335                 d.innerHTML = v;
24336                 this.cleanUpPaste();
24337                 this.el.dom.value = d.innerHTML;
24338                 this.fireEvent('push', this, v);
24339             }
24340         }
24341     },
24342
24343     // private
24344     deferFocus : function(){
24345         this.focus.defer(10, this);
24346     },
24347
24348     // doc'ed in Field
24349     focus : function(){
24350         if(this.win && !this.sourceEditMode){
24351             this.win.focus();
24352         }else{
24353             this.el.focus();
24354         }
24355     },
24356     
24357     assignDocWin: function()
24358     {
24359         var iframe = this.iframe;
24360         
24361          if(Roo.isIE){
24362             this.doc = iframe.contentWindow.document;
24363             this.win = iframe.contentWindow;
24364         } else {
24365             if (!Roo.get(this.frameId)) {
24366                 return;
24367             }
24368             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24369             this.win = Roo.get(this.frameId).dom.contentWindow;
24370         }
24371     },
24372     
24373     // private
24374     initEditor : function(){
24375         //console.log("INIT EDITOR");
24376         this.assignDocWin();
24377         
24378         
24379         
24380         this.doc.designMode="on";
24381         this.doc.open();
24382         this.doc.write(this.getDocMarkup());
24383         this.doc.close();
24384         
24385         var dbody = (this.doc.body || this.doc.documentElement);
24386         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24387         // this copies styles from the containing element into thsi one..
24388         // not sure why we need all of this..
24389         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24390         ss['background-attachment'] = 'fixed'; // w3c
24391         dbody.bgProperties = 'fixed'; // ie
24392         Roo.DomHelper.applyStyles(dbody, ss);
24393         Roo.EventManager.on(this.doc, {
24394             'mousedown': this.onEditorEvent,
24395             'dblclick': this.onEditorEvent,
24396             'click': this.onEditorEvent,
24397             'keyup': this.onEditorEvent,
24398             buffer:100,
24399             scope: this
24400         });
24401         if(Roo.isGecko){
24402             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24403         }
24404         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24405             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24406         }
24407         this.initialized = true;
24408
24409         this.fireEvent('initialize', this);
24410         this.pushValue();
24411     },
24412
24413     // private
24414     onDestroy : function(){
24415         
24416         
24417         
24418         if(this.rendered){
24419             
24420             for (var i =0; i < this.toolbars.length;i++) {
24421                 // fixme - ask toolbars for heights?
24422                 this.toolbars[i].onDestroy();
24423             }
24424             
24425             this.wrap.dom.innerHTML = '';
24426             this.wrap.remove();
24427         }
24428     },
24429
24430     // private
24431     onFirstFocus : function(){
24432         
24433         this.assignDocWin();
24434         
24435         
24436         this.activated = true;
24437         for (var i =0; i < this.toolbars.length;i++) {
24438             this.toolbars[i].onFirstFocus();
24439         }
24440        
24441         if(Roo.isGecko){ // prevent silly gecko errors
24442             this.win.focus();
24443             var s = this.win.getSelection();
24444             if(!s.focusNode || s.focusNode.nodeType != 3){
24445                 var r = s.getRangeAt(0);
24446                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24447                 r.collapse(true);
24448                 this.deferFocus();
24449             }
24450             try{
24451                 this.execCmd('useCSS', true);
24452                 this.execCmd('styleWithCSS', false);
24453             }catch(e){}
24454         }
24455         this.fireEvent('activate', this);
24456     },
24457
24458     // private
24459     adjustFont: function(btn){
24460         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24461         //if(Roo.isSafari){ // safari
24462         //    adjust *= 2;
24463        // }
24464         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24465         if(Roo.isSafari){ // safari
24466             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24467             v =  (v < 10) ? 10 : v;
24468             v =  (v > 48) ? 48 : v;
24469             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24470             
24471         }
24472         
24473         
24474         v = Math.max(1, v+adjust);
24475         
24476         this.execCmd('FontSize', v  );
24477     },
24478
24479     onEditorEvent : function(e){
24480         this.fireEvent('editorevent', this, e);
24481       //  this.updateToolbar();
24482         this.syncValue();
24483     },
24484
24485     insertTag : function(tg)
24486     {
24487         // could be a bit smarter... -> wrap the current selected tRoo..
24488         
24489         this.execCmd("formatblock",   tg);
24490         
24491     },
24492     
24493     insertText : function(txt)
24494     {
24495         
24496         
24497         range = this.createRange();
24498         range.deleteContents();
24499                //alert(Sender.getAttribute('label'));
24500                
24501         range.insertNode(this.doc.createTextNode(txt));
24502     } ,
24503     
24504     // private
24505     relayBtnCmd : function(btn){
24506         this.relayCmd(btn.cmd);
24507     },
24508
24509     /**
24510      * Executes a Midas editor command on the editor document and performs necessary focus and
24511      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24512      * @param {String} cmd The Midas command
24513      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24514      */
24515     relayCmd : function(cmd, value){
24516         this.win.focus();
24517         this.execCmd(cmd, value);
24518         this.fireEvent('editorevent', this);
24519         //this.updateToolbar();
24520         this.deferFocus();
24521     },
24522
24523     /**
24524      * Executes a Midas editor command directly on the editor document.
24525      * For visual commands, you should use {@link #relayCmd} instead.
24526      * <b>This should only be called after the editor is initialized.</b>
24527      * @param {String} cmd The Midas command
24528      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24529      */
24530     execCmd : function(cmd, value){
24531         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24532         this.syncValue();
24533     },
24534
24535    
24536     /**
24537      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24538      * to insert tRoo.
24539      * @param {String} text
24540      */
24541     insertAtCursor : function(text){
24542         if(!this.activated){
24543             return;
24544         }
24545         if(Roo.isIE){
24546             this.win.focus();
24547             var r = this.doc.selection.createRange();
24548             if(r){
24549                 r.collapse(true);
24550                 r.pasteHTML(text);
24551                 this.syncValue();
24552                 this.deferFocus();
24553             }
24554         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24555             this.win.focus();
24556             this.execCmd('InsertHTML', text);
24557             this.deferFocus();
24558         }
24559     },
24560  // private
24561     mozKeyPress : function(e){
24562         if(e.ctrlKey){
24563             var c = e.getCharCode(), cmd;
24564           
24565             if(c > 0){
24566                 c = String.fromCharCode(c).toLowerCase();
24567                 switch(c){
24568                     case 'b':
24569                         cmd = 'bold';
24570                     break;
24571                     case 'i':
24572                         cmd = 'italic';
24573                     break;
24574                     case 'u':
24575                         cmd = 'underline';
24576                     case 'v':
24577                         this.cleanUpPaste.defer(100, this);
24578                         return;
24579                     break;
24580                 }
24581                 if(cmd){
24582                     this.win.focus();
24583                     this.execCmd(cmd);
24584                     this.deferFocus();
24585                     e.preventDefault();
24586                 }
24587                 
24588             }
24589         }
24590     },
24591
24592     // private
24593     fixKeys : function(){ // load time branching for fastest keydown performance
24594         if(Roo.isIE){
24595             return function(e){
24596                 var k = e.getKey(), r;
24597                 if(k == e.TAB){
24598                     e.stopEvent();
24599                     r = this.doc.selection.createRange();
24600                     if(r){
24601                         r.collapse(true);
24602                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24603                         this.deferFocus();
24604                     }
24605                     return;
24606                 }
24607                 
24608                 if(k == e.ENTER){
24609                     r = this.doc.selection.createRange();
24610                     if(r){
24611                         var target = r.parentElement();
24612                         if(!target || target.tagName.toLowerCase() != 'li'){
24613                             e.stopEvent();
24614                             r.pasteHTML('<br />');
24615                             r.collapse(false);
24616                             r.select();
24617                         }
24618                     }
24619                 }
24620                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24621                     this.cleanUpPaste.defer(100, this);
24622                     return;
24623                 }
24624                 
24625                 
24626             };
24627         }else if(Roo.isOpera){
24628             return function(e){
24629                 var k = e.getKey();
24630                 if(k == e.TAB){
24631                     e.stopEvent();
24632                     this.win.focus();
24633                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24634                     this.deferFocus();
24635                 }
24636                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24637                     this.cleanUpPaste.defer(100, this);
24638                     return;
24639                 }
24640                 
24641             };
24642         }else if(Roo.isSafari){
24643             return function(e){
24644                 var k = e.getKey();
24645                 
24646                 if(k == e.TAB){
24647                     e.stopEvent();
24648                     this.execCmd('InsertText','\t');
24649                     this.deferFocus();
24650                     return;
24651                 }
24652                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24653                     this.cleanUpPaste.defer(100, this);
24654                     return;
24655                 }
24656                 
24657              };
24658         }
24659     }(),
24660     
24661     getAllAncestors: function()
24662     {
24663         var p = this.getSelectedNode();
24664         var a = [];
24665         if (!p) {
24666             a.push(p); // push blank onto stack..
24667             p = this.getParentElement();
24668         }
24669         
24670         
24671         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24672             a.push(p);
24673             p = p.parentNode;
24674         }
24675         a.push(this.doc.body);
24676         return a;
24677     },
24678     lastSel : false,
24679     lastSelNode : false,
24680     
24681     
24682     getSelection : function() 
24683     {
24684         this.assignDocWin();
24685         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24686     },
24687     
24688     getSelectedNode: function() 
24689     {
24690         // this may only work on Gecko!!!
24691         
24692         // should we cache this!!!!
24693         
24694         
24695         
24696          
24697         var range = this.createRange(this.getSelection());
24698         
24699         if (Roo.isIE) {
24700             var parent = range.parentElement();
24701             while (true) {
24702                 var testRange = range.duplicate();
24703                 testRange.moveToElementText(parent);
24704                 if (testRange.inRange(range)) {
24705                     break;
24706                 }
24707                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24708                     break;
24709                 }
24710                 parent = parent.parentElement;
24711             }
24712             return parent;
24713         }
24714         
24715         
24716         var ar = range.endContainer.childNodes;
24717         if (!ar.length) {
24718             ar = range.commonAncestorContainer.childNodes;
24719             //alert(ar.length);
24720         }
24721         var nodes = [];
24722         var other_nodes = [];
24723         var has_other_nodes = false;
24724         for (var i=0;i<ar.length;i++) {
24725             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24726                 continue;
24727             }
24728             // fullly contained node.
24729             
24730             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24731                 nodes.push(ar[i]);
24732                 continue;
24733             }
24734             
24735             // probably selected..
24736             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24737                 other_nodes.push(ar[i]);
24738                 continue;
24739             }
24740             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24741                 continue;
24742             }
24743             
24744             
24745             has_other_nodes = true;
24746         }
24747         if (!nodes.length && other_nodes.length) {
24748             nodes= other_nodes;
24749         }
24750         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24751             return false;
24752         }
24753         
24754         return nodes[0];
24755     },
24756     createRange: function(sel)
24757     {
24758         // this has strange effects when using with 
24759         // top toolbar - not sure if it's a great idea.
24760         //this.editor.contentWindow.focus();
24761         if (typeof sel != "undefined") {
24762             try {
24763                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24764             } catch(e) {
24765                 return this.doc.createRange();
24766             }
24767         } else {
24768             return this.doc.createRange();
24769         }
24770     },
24771     getParentElement: function()
24772     {
24773         
24774         this.assignDocWin();
24775         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24776         
24777         var range = this.createRange(sel);
24778          
24779         try {
24780             var p = range.commonAncestorContainer;
24781             while (p.nodeType == 3) { // text node
24782                 p = p.parentNode;
24783             }
24784             return p;
24785         } catch (e) {
24786             return null;
24787         }
24788     
24789     },
24790     
24791     
24792     
24793     // BC Hacks - cause I cant work out what i was trying to do..
24794     rangeIntersectsNode : function(range, node)
24795     {
24796         var nodeRange = node.ownerDocument.createRange();
24797         try {
24798             nodeRange.selectNode(node);
24799         }
24800         catch (e) {
24801             nodeRange.selectNodeContents(node);
24802         }
24803
24804         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24805                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24806     },
24807     rangeCompareNode : function(range, node) {
24808         var nodeRange = node.ownerDocument.createRange();
24809         try {
24810             nodeRange.selectNode(node);
24811         } catch (e) {
24812             nodeRange.selectNodeContents(node);
24813         }
24814         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24815         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24816
24817         if (nodeIsBefore && !nodeIsAfter)
24818             return 0;
24819         if (!nodeIsBefore && nodeIsAfter)
24820             return 1;
24821         if (nodeIsBefore && nodeIsAfter)
24822             return 2;
24823
24824         return 3;
24825     },
24826
24827     // private? - in a new class?
24828     cleanUpPaste :  function()
24829     {
24830         // cleans up the whole document..
24831       //  console.log('cleanuppaste');
24832         this.cleanUpChildren(this.doc.body);
24833         
24834         
24835     },
24836     cleanUpChildren : function (n)
24837     {
24838         if (!n.childNodes.length) {
24839             return;
24840         }
24841         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24842            this.cleanUpChild(n.childNodes[i]);
24843         }
24844     },
24845     
24846     
24847         
24848     
24849     cleanUpChild : function (node)
24850     {
24851         //console.log(node);
24852         if (node.nodeName == "#text") {
24853             // clean up silly Windows -- stuff?
24854             return; 
24855         }
24856         if (node.nodeName == "#comment") {
24857             node.parentNode.removeChild(node);
24858             // clean up silly Windows -- stuff?
24859             return; 
24860         }
24861         
24862         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24863             // remove node.
24864             node.parentNode.removeChild(node);
24865             return;
24866             
24867         }
24868         if (!node.attributes || !node.attributes.length) {
24869             this.cleanUpChildren(node);
24870             return;
24871         }
24872         
24873         function cleanAttr(n,v)
24874         {
24875             
24876             if (v.match(/^\./) || v.match(/^\//)) {
24877                 return;
24878             }
24879             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24880                 return;
24881             }
24882             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24883             node.removeAttribute(n);
24884             
24885         }
24886         
24887         function cleanStyle(n,v)
24888         {
24889             if (v.match(/expression/)) { //XSS?? should we even bother..
24890                 node.removeAttribute(n);
24891                 return;
24892             }
24893             
24894             
24895             var parts = v.split(/;/);
24896             Roo.each(parts, function(p) {
24897                 p = p.replace(/\s+/g,'');
24898                 if (!p.length) {
24899                     return;
24900                 }
24901                 var l = p.split(':').shift().replace(/\s+/g,'');
24902                 
24903                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24904                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24905                     node.removeAttribute(n);
24906                     return false;
24907                 }
24908             });
24909             
24910             
24911         }
24912         
24913         
24914         for (var i = node.attributes.length-1; i > -1 ; i--) {
24915             var a = node.attributes[i];
24916             //console.log(a);
24917             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24918                 node.removeAttribute(a.name);
24919                 return;
24920             }
24921             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24922                 cleanAttr(a.name,a.value); // fixme..
24923                 return;
24924             }
24925             if (a.name == 'style') {
24926                 cleanStyle(a.name,a.value);
24927             }
24928             /// clean up MS crap..
24929             if (a.name == 'class') {
24930                 if (a.value.match(/^Mso/)) {
24931                     node.className = '';
24932                 }
24933             }
24934             
24935             // style cleanup!?
24936             // class cleanup?
24937             
24938         }
24939         
24940         
24941         this.cleanUpChildren(node);
24942         
24943         
24944     }
24945     
24946     
24947     // hide stuff that is not compatible
24948     /**
24949      * @event blur
24950      * @hide
24951      */
24952     /**
24953      * @event change
24954      * @hide
24955      */
24956     /**
24957      * @event focus
24958      * @hide
24959      */
24960     /**
24961      * @event specialkey
24962      * @hide
24963      */
24964     /**
24965      * @cfg {String} fieldClass @hide
24966      */
24967     /**
24968      * @cfg {String} focusClass @hide
24969      */
24970     /**
24971      * @cfg {String} autoCreate @hide
24972      */
24973     /**
24974      * @cfg {String} inputType @hide
24975      */
24976     /**
24977      * @cfg {String} invalidClass @hide
24978      */
24979     /**
24980      * @cfg {String} invalidText @hide
24981      */
24982     /**
24983      * @cfg {String} msgFx @hide
24984      */
24985     /**
24986      * @cfg {String} validateOnBlur @hide
24987      */
24988 });
24989
24990 Roo.form.HtmlEditor.white = [
24991         'area', 'br', 'img', 'input', 'hr', 'wbr',
24992         
24993        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24994        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24995        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24996        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24997        'table',   'ul',         'xmp', 
24998        
24999        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25000       'thead',   'tr', 
25001      
25002       'dir', 'menu', 'ol', 'ul', 'dl',
25003        
25004       'embed',  'object'
25005 ];
25006
25007
25008 Roo.form.HtmlEditor.black = [
25009     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25010         'applet', // 
25011         'base',   'basefont', 'bgsound', 'blink',  'body', 
25012         'frame',  'frameset', 'head',    'html',   'ilayer', 
25013         'iframe', 'layer',  'link',     'meta',    'object',   
25014         'script', 'style' ,'title',  'xml' // clean later..
25015 ];
25016 Roo.form.HtmlEditor.clean = [
25017     'script', 'style', 'title', 'xml'
25018 ];
25019
25020 // attributes..
25021
25022 Roo.form.HtmlEditor.ablack = [
25023     'on'
25024 ];
25025     
25026 Roo.form.HtmlEditor.aclean = [ 
25027     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25028 ];
25029
25030 // protocols..
25031 Roo.form.HtmlEditor.pwhite= [
25032         'http',  'https',  'mailto'
25033 ];
25034
25035 Roo.form.HtmlEditor.cwhite= [
25036         'text-align',
25037         'font-size'
25038 ];
25039
25040 // <script type="text/javascript">
25041 /*
25042  * Based on
25043  * Ext JS Library 1.1.1
25044  * Copyright(c) 2006-2007, Ext JS, LLC.
25045  *  
25046  
25047  */
25048
25049 /**
25050  * @class Roo.form.HtmlEditorToolbar1
25051  * Basic Toolbar
25052  * 
25053  * Usage:
25054  *
25055  new Roo.form.HtmlEditor({
25056     ....
25057     toolbars : [
25058         new Roo.form.HtmlEditorToolbar1({
25059             disable : { fonts: 1 , format: 1, ..., ... , ...],
25060             btns : [ .... ]
25061         })
25062     }
25063      
25064  * 
25065  * @cfg {Object} disable List of elements to disable..
25066  * @cfg {Array} btns List of additional buttons.
25067  * 
25068  * 
25069  * NEEDS Extra CSS? 
25070  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25071  */
25072  
25073 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25074 {
25075     
25076     Roo.apply(this, config);
25077     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25078     // dont call parent... till later.
25079 }
25080
25081 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25082     
25083     tb: false,
25084     
25085     rendered: false,
25086     
25087     editor : false,
25088     /**
25089      * @cfg {Object} disable  List of toolbar elements to disable
25090          
25091      */
25092     disable : false,
25093       /**
25094      * @cfg {Array} fontFamilies An array of available font families
25095      */
25096     fontFamilies : [
25097         'Arial',
25098         'Courier New',
25099         'Tahoma',
25100         'Times New Roman',
25101         'Verdana'
25102     ],
25103     
25104     specialChars : [
25105            "&#169;",
25106           "&#174;",     
25107           "&#8482;",    
25108           "&#163;" ,    
25109          // "&#8212;",    
25110           "&#8230;",    
25111           "&#247;" ,    
25112         //  "&#225;" ,     ?? a acute?
25113            "&#8364;"    , //Euro
25114        //   "&#8220;"    ,
25115         //  "&#8221;"    ,
25116         //  "&#8226;"    ,
25117           "&#176;"  //   , // degrees
25118
25119          // "&#233;"     , // e ecute
25120          // "&#250;"     , // u ecute?
25121     ],
25122     inputElements : [ 
25123             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25124             "input:submit", "input:button", "select", "textarea", "label" ],
25125     formats : [
25126         ["p"] ,  
25127         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25128         ["pre"],[ "code"], 
25129         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25130     ],
25131      /**
25132      * @cfg {String} defaultFont default font to use.
25133      */
25134     defaultFont: 'tahoma',
25135    
25136     fontSelect : false,
25137     
25138     
25139     formatCombo : false,
25140     
25141     init : function(editor)
25142     {
25143         this.editor = editor;
25144         
25145         
25146         var fid = editor.frameId;
25147         var etb = this;
25148         function btn(id, toggle, handler){
25149             var xid = fid + '-'+ id ;
25150             return {
25151                 id : xid,
25152                 cmd : id,
25153                 cls : 'x-btn-icon x-edit-'+id,
25154                 enableToggle:toggle !== false,
25155                 scope: editor, // was editor...
25156                 handler:handler||editor.relayBtnCmd,
25157                 clickEvent:'mousedown',
25158                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25159                 tabIndex:-1
25160             };
25161         }
25162         
25163         
25164         
25165         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25166         this.tb = tb;
25167          // stop form submits
25168         tb.el.on('click', function(e){
25169             e.preventDefault(); // what does this do?
25170         });
25171
25172         if(!this.disable.font && !Roo.isSafari){
25173             /* why no safari for fonts
25174             editor.fontSelect = tb.el.createChild({
25175                 tag:'select',
25176                 tabIndex: -1,
25177                 cls:'x-font-select',
25178                 html: editor.createFontOptions()
25179             });
25180             editor.fontSelect.on('change', function(){
25181                 var font = editor.fontSelect.dom.value;
25182                 editor.relayCmd('fontname', font);
25183                 editor.deferFocus();
25184             }, editor);
25185             tb.add(
25186                 editor.fontSelect.dom,
25187                 '-'
25188             );
25189             */
25190         };
25191         if(!this.disable.formats){
25192             this.formatCombo = new Roo.form.ComboBox({
25193                 store: new Roo.data.SimpleStore({
25194                     id : 'tag',
25195                     fields: ['tag'],
25196                     data : this.formats // from states.js
25197                 }),
25198                 blockFocus : true,
25199                 //autoCreate : {tag: "div",  size: "20"},
25200                 displayField:'tag',
25201                 typeAhead: false,
25202                 mode: 'local',
25203                 editable : false,
25204                 triggerAction: 'all',
25205                 emptyText:'Add tag',
25206                 selectOnFocus:true,
25207                 width:135,
25208                 listeners : {
25209                     'select': function(c, r, i) {
25210                         editor.insertTag(r.get('tag'));
25211                         editor.focus();
25212                     }
25213                 }
25214
25215             });
25216             tb.addField(this.formatCombo);
25217             
25218         }
25219         
25220         if(!this.disable.format){
25221             tb.add(
25222                 btn('bold'),
25223                 btn('italic'),
25224                 btn('underline')
25225             );
25226         };
25227         if(!this.disable.fontSize){
25228             tb.add(
25229                 '-',
25230                 
25231                 
25232                 btn('increasefontsize', false, editor.adjustFont),
25233                 btn('decreasefontsize', false, editor.adjustFont)
25234             );
25235         };
25236         
25237         
25238         if(this.disable.colors){
25239             tb.add(
25240                 '-', {
25241                     id:editor.frameId +'-forecolor',
25242                     cls:'x-btn-icon x-edit-forecolor',
25243                     clickEvent:'mousedown',
25244                     tooltip: this.buttonTips['forecolor'] || undefined,
25245                     tabIndex:-1,
25246                     menu : new Roo.menu.ColorMenu({
25247                         allowReselect: true,
25248                         focus: Roo.emptyFn,
25249                         value:'000000',
25250                         plain:true,
25251                         selectHandler: function(cp, color){
25252                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25253                             editor.deferFocus();
25254                         },
25255                         scope: editor,
25256                         clickEvent:'mousedown'
25257                     })
25258                 }, {
25259                     id:editor.frameId +'backcolor',
25260                     cls:'x-btn-icon x-edit-backcolor',
25261                     clickEvent:'mousedown',
25262                     tooltip: this.buttonTips['backcolor'] || undefined,
25263                     tabIndex:-1,
25264                     menu : new Roo.menu.ColorMenu({
25265                         focus: Roo.emptyFn,
25266                         value:'FFFFFF',
25267                         plain:true,
25268                         allowReselect: true,
25269                         selectHandler: function(cp, color){
25270                             if(Roo.isGecko){
25271                                 editor.execCmd('useCSS', false);
25272                                 editor.execCmd('hilitecolor', color);
25273                                 editor.execCmd('useCSS', true);
25274                                 editor.deferFocus();
25275                             }else{
25276                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25277                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25278                                 editor.deferFocus();
25279                             }
25280                         },
25281                         scope:editor,
25282                         clickEvent:'mousedown'
25283                     })
25284                 }
25285             );
25286         };
25287         // now add all the items...
25288         
25289
25290         if(!this.disable.alignments){
25291             tb.add(
25292                 '-',
25293                 btn('justifyleft'),
25294                 btn('justifycenter'),
25295                 btn('justifyright')
25296             );
25297         };
25298
25299         //if(!Roo.isSafari){
25300             if(!this.disable.links){
25301                 tb.add(
25302                     '-',
25303                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25304                 );
25305             };
25306
25307             if(!this.disable.lists){
25308                 tb.add(
25309                     '-',
25310                     btn('insertorderedlist'),
25311                     btn('insertunorderedlist')
25312                 );
25313             }
25314             if(!this.disable.sourceEdit){
25315                 tb.add(
25316                     '-',
25317                     btn('sourceedit', true, function(btn){
25318                         this.toggleSourceEdit(btn.pressed);
25319                     })
25320                 );
25321             }
25322         //}
25323         
25324         var smenu = { };
25325         // special menu.. - needs to be tidied up..
25326         if (!this.disable.special) {
25327             smenu = {
25328                 text: "&#169;",
25329                 cls: 'x-edit-none',
25330                 menu : {
25331                     items : []
25332                    }
25333             };
25334             for (var i =0; i < this.specialChars.length; i++) {
25335                 smenu.menu.items.push({
25336                     
25337                     html: this.specialChars[i],
25338                     handler: function(a,b) {
25339                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25340                         
25341                     },
25342                     tabIndex:-1
25343                 });
25344             }
25345             
25346             
25347             tb.add(smenu);
25348             
25349             
25350         }
25351         if (this.btns) {
25352             for(var i =0; i< this.btns.length;i++) {
25353                 var b = this.btns[i];
25354                 b.cls =  'x-edit-none';
25355                 b.scope = editor;
25356                 tb.add(b);
25357             }
25358         
25359         }
25360         
25361         
25362         
25363         // disable everything...
25364         
25365         this.tb.items.each(function(item){
25366            if(item.id != editor.frameId+ '-sourceedit'){
25367                 item.disable();
25368             }
25369         });
25370         this.rendered = true;
25371         
25372         // the all the btns;
25373         editor.on('editorevent', this.updateToolbar, this);
25374         // other toolbars need to implement this..
25375         //editor.on('editmodechange', this.updateToolbar, this);
25376     },
25377     
25378     
25379     
25380     /**
25381      * Protected method that will not generally be called directly. It triggers
25382      * a toolbar update by reading the markup state of the current selection in the editor.
25383      */
25384     updateToolbar: function(){
25385
25386         if(!this.editor.activated){
25387             this.editor.onFirstFocus();
25388             return;
25389         }
25390
25391         var btns = this.tb.items.map, 
25392             doc = this.editor.doc,
25393             frameId = this.editor.frameId;
25394
25395         if(!this.disable.font && !Roo.isSafari){
25396             /*
25397             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25398             if(name != this.fontSelect.dom.value){
25399                 this.fontSelect.dom.value = name;
25400             }
25401             */
25402         }
25403         if(!this.disable.format){
25404             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25405             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25406             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25407         }
25408         if(!this.disable.alignments){
25409             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25410             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25411             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25412         }
25413         if(!Roo.isSafari && !this.disable.lists){
25414             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25415             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25416         }
25417         
25418         var ans = this.editor.getAllAncestors();
25419         if (this.formatCombo) {
25420             
25421             
25422             var store = this.formatCombo.store;
25423             this.formatCombo.setValue("");
25424             for (var i =0; i < ans.length;i++) {
25425                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25426                     // select it..
25427                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25428                     break;
25429                 }
25430             }
25431         }
25432         
25433         
25434         
25435         // hides menus... - so this cant be on a menu...
25436         Roo.menu.MenuMgr.hideAll();
25437
25438         //this.editorsyncValue();
25439     },
25440    
25441     
25442     createFontOptions : function(){
25443         var buf = [], fs = this.fontFamilies, ff, lc;
25444         for(var i = 0, len = fs.length; i< len; i++){
25445             ff = fs[i];
25446             lc = ff.toLowerCase();
25447             buf.push(
25448                 '<option value="',lc,'" style="font-family:',ff,';"',
25449                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25450                     ff,
25451                 '</option>'
25452             );
25453         }
25454         return buf.join('');
25455     },
25456     
25457     toggleSourceEdit : function(sourceEditMode){
25458         if(sourceEditMode === undefined){
25459             sourceEditMode = !this.sourceEditMode;
25460         }
25461         this.sourceEditMode = sourceEditMode === true;
25462         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25463         // just toggle the button?
25464         if(btn.pressed !== this.editor.sourceEditMode){
25465             btn.toggle(this.editor.sourceEditMode);
25466             return;
25467         }
25468         
25469         if(this.sourceEditMode){
25470             this.tb.items.each(function(item){
25471                 if(item.cmd != 'sourceedit'){
25472                     item.disable();
25473                 }
25474             });
25475           
25476         }else{
25477             if(this.initialized){
25478                 this.tb.items.each(function(item){
25479                     item.enable();
25480                 });
25481             }
25482             
25483         }
25484         // tell the editor that it's been pressed..
25485         this.editor.toggleSourceEdit(sourceEditMode);
25486        
25487     },
25488      /**
25489      * Object collection of toolbar tooltips for the buttons in the editor. The key
25490      * is the command id associated with that button and the value is a valid QuickTips object.
25491      * For example:
25492 <pre><code>
25493 {
25494     bold : {
25495         title: 'Bold (Ctrl+B)',
25496         text: 'Make the selected text bold.',
25497         cls: 'x-html-editor-tip'
25498     },
25499     italic : {
25500         title: 'Italic (Ctrl+I)',
25501         text: 'Make the selected text italic.',
25502         cls: 'x-html-editor-tip'
25503     },
25504     ...
25505 </code></pre>
25506     * @type Object
25507      */
25508     buttonTips : {
25509         bold : {
25510             title: 'Bold (Ctrl+B)',
25511             text: 'Make the selected text bold.',
25512             cls: 'x-html-editor-tip'
25513         },
25514         italic : {
25515             title: 'Italic (Ctrl+I)',
25516             text: 'Make the selected text italic.',
25517             cls: 'x-html-editor-tip'
25518         },
25519         underline : {
25520             title: 'Underline (Ctrl+U)',
25521             text: 'Underline the selected text.',
25522             cls: 'x-html-editor-tip'
25523         },
25524         increasefontsize : {
25525             title: 'Grow Text',
25526             text: 'Increase the font size.',
25527             cls: 'x-html-editor-tip'
25528         },
25529         decreasefontsize : {
25530             title: 'Shrink Text',
25531             text: 'Decrease the font size.',
25532             cls: 'x-html-editor-tip'
25533         },
25534         backcolor : {
25535             title: 'Text Highlight Color',
25536             text: 'Change the background color of the selected text.',
25537             cls: 'x-html-editor-tip'
25538         },
25539         forecolor : {
25540             title: 'Font Color',
25541             text: 'Change the color of the selected text.',
25542             cls: 'x-html-editor-tip'
25543         },
25544         justifyleft : {
25545             title: 'Align Text Left',
25546             text: 'Align text to the left.',
25547             cls: 'x-html-editor-tip'
25548         },
25549         justifycenter : {
25550             title: 'Center Text',
25551             text: 'Center text in the editor.',
25552             cls: 'x-html-editor-tip'
25553         },
25554         justifyright : {
25555             title: 'Align Text Right',
25556             text: 'Align text to the right.',
25557             cls: 'x-html-editor-tip'
25558         },
25559         insertunorderedlist : {
25560             title: 'Bullet List',
25561             text: 'Start a bulleted list.',
25562             cls: 'x-html-editor-tip'
25563         },
25564         insertorderedlist : {
25565             title: 'Numbered List',
25566             text: 'Start a numbered list.',
25567             cls: 'x-html-editor-tip'
25568         },
25569         createlink : {
25570             title: 'Hyperlink',
25571             text: 'Make the selected text a hyperlink.',
25572             cls: 'x-html-editor-tip'
25573         },
25574         sourceedit : {
25575             title: 'Source Edit',
25576             text: 'Switch to source editing mode.',
25577             cls: 'x-html-editor-tip'
25578         }
25579     },
25580     // private
25581     onDestroy : function(){
25582         if(this.rendered){
25583             
25584             this.tb.items.each(function(item){
25585                 if(item.menu){
25586                     item.menu.removeAll();
25587                     if(item.menu.el){
25588                         item.menu.el.destroy();
25589                     }
25590                 }
25591                 item.destroy();
25592             });
25593              
25594         }
25595     },
25596     onFirstFocus: function() {
25597         this.tb.items.each(function(item){
25598            item.enable();
25599         });
25600     }
25601 });
25602
25603
25604
25605
25606 // <script type="text/javascript">
25607 /*
25608  * Based on
25609  * Ext JS Library 1.1.1
25610  * Copyright(c) 2006-2007, Ext JS, LLC.
25611  *  
25612  
25613  */
25614
25615  
25616 /**
25617  * @class Roo.form.HtmlEditor.ToolbarContext
25618  * Context Toolbar
25619  * 
25620  * Usage:
25621  *
25622  new Roo.form.HtmlEditor({
25623     ....
25624     toolbars : [
25625         new Roo.form.HtmlEditor.ToolbarStandard(),
25626         new Roo.form.HtmlEditor.ToolbarContext()
25627         })
25628     }
25629      
25630  * 
25631  * @config : {Object} disable List of elements to disable.. (not done yet.)
25632  * 
25633  * 
25634  */
25635
25636 Roo.form.HtmlEditor.ToolbarContext = function(config)
25637 {
25638     
25639     Roo.apply(this, config);
25640     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25641     // dont call parent... till later.
25642 }
25643 Roo.form.HtmlEditor.ToolbarContext.types = {
25644     'IMG' : {
25645         width : {
25646             title: "Width",
25647             width: 40
25648         },
25649         height:  {
25650             title: "Height",
25651             width: 40
25652         },
25653         align: {
25654             title: "Align",
25655             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25656             width : 80
25657             
25658         },
25659         border: {
25660             title: "Border",
25661             width: 40
25662         },
25663         alt: {
25664             title: "Alt",
25665             width: 120
25666         },
25667         src : {
25668             title: "Src",
25669             width: 220
25670         }
25671         
25672     },
25673     'A' : {
25674         name : {
25675             title: "Name",
25676             width: 50
25677         },
25678         href:  {
25679             title: "Href",
25680             width: 220
25681         } // border?
25682         
25683     },
25684     'TABLE' : {
25685         rows : {
25686             title: "Rows",
25687             width: 20
25688         },
25689         cols : {
25690             title: "Cols",
25691             width: 20
25692         },
25693         width : {
25694             title: "Width",
25695             width: 40
25696         },
25697         height : {
25698             title: "Height",
25699             width: 40
25700         },
25701         border : {
25702             title: "Border",
25703             width: 20
25704         }
25705     },
25706     'TD' : {
25707         width : {
25708             title: "Width",
25709             width: 40
25710         },
25711         height : {
25712             title: "Height",
25713             width: 40
25714         },   
25715         align: {
25716             title: "Align",
25717             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25718             width: 40
25719         },
25720         valign: {
25721             title: "Valign",
25722             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25723             width: 40
25724         },
25725         colspan: {
25726             title: "Colspan",
25727             width: 20
25728             
25729         }
25730     },
25731     'INPUT' : {
25732         name : {
25733             title: "name",
25734             width: 120
25735         },
25736         value : {
25737             title: "Value",
25738             width: 120
25739         },
25740         width : {
25741             title: "Width",
25742             width: 40
25743         }
25744     },
25745     'LABEL' : {
25746         'for' : {
25747             title: "For",
25748             width: 120
25749         }
25750     },
25751     'TEXTAREA' : {
25752           name : {
25753             title: "name",
25754             width: 120
25755         },
25756         rows : {
25757             title: "Rows",
25758             width: 20
25759         },
25760         cols : {
25761             title: "Cols",
25762             width: 20
25763         }
25764     },
25765     'SELECT' : {
25766         name : {
25767             title: "name",
25768             width: 120
25769         },
25770         selectoptions : {
25771             title: "Options",
25772             width: 200
25773         }
25774     },
25775     'BODY' : {
25776         title : {
25777             title: "title",
25778             width: 120,
25779             disabled : true
25780         }
25781     }
25782 };
25783
25784
25785
25786 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25787     
25788     tb: false,
25789     
25790     rendered: false,
25791     
25792     editor : false,
25793     /**
25794      * @cfg {Object} disable  List of toolbar elements to disable
25795          
25796      */
25797     disable : false,
25798     
25799     
25800     
25801     toolbars : false,
25802     
25803     init : function(editor)
25804     {
25805         this.editor = editor;
25806         
25807         
25808         var fid = editor.frameId;
25809         var etb = this;
25810         function btn(id, toggle, handler){
25811             var xid = fid + '-'+ id ;
25812             return {
25813                 id : xid,
25814                 cmd : id,
25815                 cls : 'x-btn-icon x-edit-'+id,
25816                 enableToggle:toggle !== false,
25817                 scope: editor, // was editor...
25818                 handler:handler||editor.relayBtnCmd,
25819                 clickEvent:'mousedown',
25820                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25821                 tabIndex:-1
25822             };
25823         }
25824         // create a new element.
25825         var wdiv = editor.wrap.createChild({
25826                 tag: 'div'
25827             }, editor.wrap.dom.firstChild.nextSibling, true);
25828         
25829         // can we do this more than once??
25830         
25831          // stop form submits
25832       
25833  
25834         // disable everything...
25835         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25836         this.toolbars = {};
25837            
25838         for (var i in  ty) {
25839           
25840             this.toolbars[i] = this.buildToolbar(ty[i],i);
25841         }
25842         this.tb = this.toolbars.BODY;
25843         this.tb.el.show();
25844         
25845          
25846         this.rendered = true;
25847         
25848         // the all the btns;
25849         editor.on('editorevent', this.updateToolbar, this);
25850         // other toolbars need to implement this..
25851         //editor.on('editmodechange', this.updateToolbar, this);
25852     },
25853     
25854     
25855     
25856     /**
25857      * Protected method that will not generally be called directly. It triggers
25858      * a toolbar update by reading the markup state of the current selection in the editor.
25859      */
25860     updateToolbar: function(){
25861
25862         if(!this.editor.activated){
25863             this.editor.onFirstFocus();
25864             return;
25865         }
25866
25867         
25868         var ans = this.editor.getAllAncestors();
25869         
25870         // pick
25871         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25872         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25873         sel = sel ? sel : this.editor.doc.body;
25874         sel = sel.tagName.length ? sel : this.editor.doc.body;
25875         var tn = sel.tagName.toUpperCase();
25876         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25877         tn = sel.tagName.toUpperCase();
25878         if (this.tb.name  == tn) {
25879             return; // no change
25880         }
25881         this.tb.el.hide();
25882         ///console.log("show: " + tn);
25883         this.tb =  this.toolbars[tn];
25884         this.tb.el.show();
25885         this.tb.fields.each(function(e) {
25886             e.setValue(sel.getAttribute(e.name));
25887         });
25888         this.tb.selectedNode = sel;
25889         
25890         
25891         Roo.menu.MenuMgr.hideAll();
25892
25893         //this.editorsyncValue();
25894     },
25895    
25896        
25897     // private
25898     onDestroy : function(){
25899         if(this.rendered){
25900             
25901             this.tb.items.each(function(item){
25902                 if(item.menu){
25903                     item.menu.removeAll();
25904                     if(item.menu.el){
25905                         item.menu.el.destroy();
25906                     }
25907                 }
25908                 item.destroy();
25909             });
25910              
25911         }
25912     },
25913     onFirstFocus: function() {
25914         // need to do this for all the toolbars..
25915         this.tb.items.each(function(item){
25916            item.enable();
25917         });
25918     },
25919     buildToolbar: function(tlist, nm)
25920     {
25921         var editor = this.editor;
25922          // create a new element.
25923         var wdiv = editor.wrap.createChild({
25924                 tag: 'div'
25925             }, editor.wrap.dom.firstChild.nextSibling, true);
25926         
25927        
25928         var tb = new Roo.Toolbar(wdiv);
25929         tb.add(nm+ ":&nbsp;");
25930         for (var i in tlist) {
25931             var item = tlist[i];
25932             tb.add(item.title + ":&nbsp;");
25933             if (item.opts) {
25934                 // fixme
25935                 
25936               
25937                 tb.addField( new Roo.form.ComboBox({
25938                     store: new Roo.data.SimpleStore({
25939                         id : 'val',
25940                         fields: ['val'],
25941                         data : item.opts // from states.js
25942                     }),
25943                     name : i,
25944                     displayField:'val',
25945                     typeAhead: false,
25946                     mode: 'local',
25947                     editable : false,
25948                     triggerAction: 'all',
25949                     emptyText:'Select',
25950                     selectOnFocus:true,
25951                     width: item.width ? item.width  : 130,
25952                     listeners : {
25953                         'select': function(c, r, i) {
25954                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25955                         }
25956                     }
25957
25958                 }));
25959                 continue;
25960                     
25961                 
25962                 
25963                 
25964                 
25965                 tb.addField( new Roo.form.TextField({
25966                     name: i,
25967                     width: 100,
25968                     //allowBlank:false,
25969                     value: ''
25970                 }));
25971                 continue;
25972             }
25973             tb.addField( new Roo.form.TextField({
25974                 name: i,
25975                 width: item.width,
25976                 //allowBlank:true,
25977                 value: '',
25978                 listeners: {
25979                     'change' : function(f, nv, ov) {
25980                         tb.selectedNode.setAttribute(f.name, nv);
25981                     }
25982                 }
25983             }));
25984              
25985         }
25986         tb.el.on('click', function(e){
25987             e.preventDefault(); // what does this do?
25988         });
25989         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25990         tb.el.hide();
25991         tb.name = nm;
25992         // dont need to disable them... as they will get hidden
25993         return tb;
25994          
25995         
25996     }
25997     
25998     
25999     
26000     
26001 });
26002
26003
26004
26005
26006
26007 /*
26008  * Based on:
26009  * Ext JS Library 1.1.1
26010  * Copyright(c) 2006-2007, Ext JS, LLC.
26011  *
26012  * Originally Released Under LGPL - original licence link has changed is not relivant.
26013  *
26014  * Fork - LGPL
26015  * <script type="text/javascript">
26016  */
26017  
26018 /**
26019  * @class Roo.form.BasicForm
26020  * @extends Roo.util.Observable
26021  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26022  * @constructor
26023  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26024  * @param {Object} config Configuration options
26025  */
26026 Roo.form.BasicForm = function(el, config){
26027     this.allItems = [];
26028     this.childForms = [];
26029     Roo.apply(this, config);
26030     /*
26031      * The Roo.form.Field items in this form.
26032      * @type MixedCollection
26033      */
26034      
26035      
26036     this.items = new Roo.util.MixedCollection(false, function(o){
26037         return o.id || (o.id = Roo.id());
26038     });
26039     this.addEvents({
26040         /**
26041          * @event beforeaction
26042          * Fires before any action is performed. Return false to cancel the action.
26043          * @param {Form} this
26044          * @param {Action} action The action to be performed
26045          */
26046         beforeaction: true,
26047         /**
26048          * @event actionfailed
26049          * Fires when an action fails.
26050          * @param {Form} this
26051          * @param {Action} action The action that failed
26052          */
26053         actionfailed : true,
26054         /**
26055          * @event actioncomplete
26056          * Fires when an action is completed.
26057          * @param {Form} this
26058          * @param {Action} action The action that completed
26059          */
26060         actioncomplete : true
26061     });
26062     if(el){
26063         this.initEl(el);
26064     }
26065     Roo.form.BasicForm.superclass.constructor.call(this);
26066 };
26067
26068 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26069     /**
26070      * @cfg {String} method
26071      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26072      */
26073     /**
26074      * @cfg {DataReader} reader
26075      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26076      * This is optional as there is built-in support for processing JSON.
26077      */
26078     /**
26079      * @cfg {DataReader} errorReader
26080      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26081      * This is completely optional as there is built-in support for processing JSON.
26082      */
26083     /**
26084      * @cfg {String} url
26085      * The URL to use for form actions if one isn't supplied in the action options.
26086      */
26087     /**
26088      * @cfg {Boolean} fileUpload
26089      * Set to true if this form is a file upload.
26090      */
26091      
26092     /**
26093      * @cfg {Object} baseParams
26094      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26095      */
26096      /**
26097      
26098     /**
26099      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26100      */
26101     timeout: 30,
26102
26103     // private
26104     activeAction : null,
26105
26106     /**
26107      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26108      * or setValues() data instead of when the form was first created.
26109      */
26110     trackResetOnLoad : false,
26111     
26112     
26113     /**
26114      * childForms - used for multi-tab forms
26115      * @type {Array}
26116      */
26117     childForms : false,
26118     
26119     /**
26120      * allItems - full list of fields.
26121      * @type {Array}
26122      */
26123     allItems : false,
26124     
26125     /**
26126      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26127      * element by passing it or its id or mask the form itself by passing in true.
26128      * @type Mixed
26129      */
26130     waitMsgTarget : false,
26131
26132     // private
26133     initEl : function(el){
26134         this.el = Roo.get(el);
26135         this.id = this.el.id || Roo.id();
26136         this.el.on('submit', this.onSubmit, this);
26137         this.el.addClass('x-form');
26138     },
26139
26140     // private
26141     onSubmit : function(e){
26142         e.stopEvent();
26143     },
26144
26145     /**
26146      * Returns true if client-side validation on the form is successful.
26147      * @return Boolean
26148      */
26149     isValid : function(){
26150         var valid = true;
26151         this.items.each(function(f){
26152            if(!f.validate()){
26153                valid = false;
26154            }
26155         });
26156         return valid;
26157     },
26158
26159     /**
26160      * Returns true if any fields in this form have changed since their original load.
26161      * @return Boolean
26162      */
26163     isDirty : function(){
26164         var dirty = false;
26165         this.items.each(function(f){
26166            if(f.isDirty()){
26167                dirty = true;
26168                return false;
26169            }
26170         });
26171         return dirty;
26172     },
26173
26174     /**
26175      * Performs a predefined action (submit or load) or custom actions you define on this form.
26176      * @param {String} actionName The name of the action type
26177      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26178      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26179      * accept other config options):
26180      * <pre>
26181 Property          Type             Description
26182 ----------------  ---------------  ----------------------------------------------------------------------------------
26183 url               String           The url for the action (defaults to the form's url)
26184 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26185 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26186 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26187                                    validate the form on the client (defaults to false)
26188      * </pre>
26189      * @return {BasicForm} this
26190      */
26191     doAction : function(action, options){
26192         if(typeof action == 'string'){
26193             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26194         }
26195         if(this.fireEvent('beforeaction', this, action) !== false){
26196             this.beforeAction(action);
26197             action.run.defer(100, action);
26198         }
26199         return this;
26200     },
26201
26202     /**
26203      * Shortcut to do a submit action.
26204      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26205      * @return {BasicForm} this
26206      */
26207     submit : function(options){
26208         this.doAction('submit', options);
26209         return this;
26210     },
26211
26212     /**
26213      * Shortcut to do a load action.
26214      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26215      * @return {BasicForm} this
26216      */
26217     load : function(options){
26218         this.doAction('load', options);
26219         return this;
26220     },
26221
26222     /**
26223      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26224      * @param {Record} record The record to edit
26225      * @return {BasicForm} this
26226      */
26227     updateRecord : function(record){
26228         record.beginEdit();
26229         var fs = record.fields;
26230         fs.each(function(f){
26231             var field = this.findField(f.name);
26232             if(field){
26233                 record.set(f.name, field.getValue());
26234             }
26235         }, this);
26236         record.endEdit();
26237         return this;
26238     },
26239
26240     /**
26241      * Loads an Roo.data.Record into this form.
26242      * @param {Record} record The record to load
26243      * @return {BasicForm} this
26244      */
26245     loadRecord : function(record){
26246         this.setValues(record.data);
26247         return this;
26248     },
26249
26250     // private
26251     beforeAction : function(action){
26252         var o = action.options;
26253         
26254        
26255         if(this.waitMsgTarget === true){
26256             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26257         }else if(this.waitMsgTarget){
26258             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26259             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26260         }else {
26261             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26262         }
26263          
26264     },
26265
26266     // private
26267     afterAction : function(action, success){
26268         this.activeAction = null;
26269         var o = action.options;
26270         
26271         if(this.waitMsgTarget === true){
26272             this.el.unmask();
26273         }else if(this.waitMsgTarget){
26274             this.waitMsgTarget.unmask();
26275         }else{
26276             Roo.MessageBox.updateProgress(1);
26277             Roo.MessageBox.hide();
26278         }
26279          
26280         if(success){
26281             if(o.reset){
26282                 this.reset();
26283             }
26284             Roo.callback(o.success, o.scope, [this, action]);
26285             this.fireEvent('actioncomplete', this, action);
26286             
26287         }else{
26288             Roo.callback(o.failure, o.scope, [this, action]);
26289             // show an error message if no failed handler is set..
26290             if (!this.hasListener('actionfailed')) {
26291                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
26292             }
26293             
26294             this.fireEvent('actionfailed', this, action);
26295         }
26296         
26297     },
26298
26299     /**
26300      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26301      * @param {String} id The value to search for
26302      * @return Field
26303      */
26304     findField : function(id){
26305         var field = this.items.get(id);
26306         if(!field){
26307             this.items.each(function(f){
26308                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26309                     field = f;
26310                     return false;
26311                 }
26312             });
26313         }
26314         return field || null;
26315     },
26316
26317     /**
26318      * Add a secondary form to this one, 
26319      * Used to provide tabbed forms. One form is primary, with hidden values 
26320      * which mirror the elements from the other forms.
26321      * 
26322      * @param {Roo.form.Form} form to add.
26323      * 
26324      */
26325     addForm : function(form)
26326     {
26327        
26328         if (this.childForms.indexOf(form) > -1) {
26329             // already added..
26330             return;
26331         }
26332         this.childForms.push(form);
26333         var n = '';
26334         Roo.each(form.allItems, function (fe) {
26335             
26336             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26337             if (this.findField(n)) { // already added..
26338                 return;
26339             }
26340             var add = new Roo.form.Hidden({
26341                 name : n
26342             });
26343             add.render(this.el);
26344             
26345             this.add( add );
26346         }, this);
26347         
26348     },
26349     /**
26350      * Mark fields in this form invalid in bulk.
26351      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26352      * @return {BasicForm} this
26353      */
26354     markInvalid : function(errors){
26355         if(errors instanceof Array){
26356             for(var i = 0, len = errors.length; i < len; i++){
26357                 var fieldError = errors[i];
26358                 var f = this.findField(fieldError.id);
26359                 if(f){
26360                     f.markInvalid(fieldError.msg);
26361                 }
26362             }
26363         }else{
26364             var field, id;
26365             for(id in errors){
26366                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26367                     field.markInvalid(errors[id]);
26368                 }
26369             }
26370         }
26371         Roo.each(this.childForms || [], function (f) {
26372             f.markInvalid(errors);
26373         });
26374         
26375         return this;
26376     },
26377
26378     /**
26379      * Set values for fields in this form in bulk.
26380      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26381      * @return {BasicForm} this
26382      */
26383     setValues : function(values){
26384         if(values instanceof Array){ // array of objects
26385             for(var i = 0, len = values.length; i < len; i++){
26386                 var v = values[i];
26387                 var f = this.findField(v.id);
26388                 if(f){
26389                     f.setValue(v.value);
26390                     if(this.trackResetOnLoad){
26391                         f.originalValue = f.getValue();
26392                     }
26393                 }
26394             }
26395         }else{ // object hash
26396             var field, id;
26397             for(id in values){
26398                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26399                     
26400                     if (field.setFromData && 
26401                         field.valueField && 
26402                         field.displayField &&
26403                         // combos' with local stores can 
26404                         // be queried via setValue()
26405                         // to set their value..
26406                         (field.store && !field.store.isLocal)
26407                         ) {
26408                         // it's a combo
26409                         var sd = { };
26410                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26411                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26412                         field.setFromData(sd);
26413                         
26414                     } else {
26415                         field.setValue(values[id]);
26416                     }
26417                     
26418                     
26419                     if(this.trackResetOnLoad){
26420                         field.originalValue = field.getValue();
26421                     }
26422                 }
26423             }
26424         }
26425          
26426         Roo.each(this.childForms || [], function (f) {
26427             f.setValues(values);
26428         });
26429                 
26430         return this;
26431     },
26432
26433     /**
26434      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26435      * they are returned as an array.
26436      * @param {Boolean} asString
26437      * @return {Object}
26438      */
26439     getValues : function(asString){
26440         if (this.childForms) {
26441             // copy values from the child forms
26442             Roo.each(this.childForms, function (f) {
26443                 this.setValues(f.getValues());
26444             }, this);
26445         }
26446         
26447         
26448         
26449         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26450         if(asString === true){
26451             return fs;
26452         }
26453         return Roo.urlDecode(fs);
26454     },
26455     
26456     /**
26457      * Returns the fields in this form as an object with key/value pairs. 
26458      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26459      * @return {Object}
26460      */
26461     getFieldValues : function()
26462     {
26463         if (this.childForms) {
26464             // copy values from the child forms
26465             Roo.each(this.childForms, function (f) {
26466                 this.setValues(f.getValues());
26467             }, this);
26468         }
26469         
26470         var ret = {};
26471         this.items.each(function(f){
26472             if (!f.getName()) {
26473                 return;
26474             }
26475             var v = f.getValue();
26476             if ((typeof(v) == 'object') && f.getRawValue) {
26477                 v = f.getRawValue() ; // dates..
26478             }
26479             ret[f.getName()] = v;
26480         });
26481         
26482         return ret;
26483     },
26484
26485     /**
26486      * Clears all invalid messages in this form.
26487      * @return {BasicForm} this
26488      */
26489     clearInvalid : function(){
26490         this.items.each(function(f){
26491            f.clearInvalid();
26492         });
26493         
26494         Roo.each(this.childForms || [], function (f) {
26495             f.clearInvalid();
26496         });
26497         
26498         
26499         return this;
26500     },
26501
26502     /**
26503      * Resets this form.
26504      * @return {BasicForm} this
26505      */
26506     reset : function(){
26507         this.items.each(function(f){
26508             f.reset();
26509         });
26510         
26511         Roo.each(this.childForms || [], function (f) {
26512             f.reset();
26513         });
26514        
26515         
26516         return this;
26517     },
26518
26519     /**
26520      * Add Roo.form components to this form.
26521      * @param {Field} field1
26522      * @param {Field} field2 (optional)
26523      * @param {Field} etc (optional)
26524      * @return {BasicForm} this
26525      */
26526     add : function(){
26527         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26528         return this;
26529     },
26530
26531
26532     /**
26533      * Removes a field from the items collection (does NOT remove its markup).
26534      * @param {Field} field
26535      * @return {BasicForm} this
26536      */
26537     remove : function(field){
26538         this.items.remove(field);
26539         return this;
26540     },
26541
26542     /**
26543      * Looks at the fields in this form, checks them for an id attribute,
26544      * and calls applyTo on the existing dom element with that id.
26545      * @return {BasicForm} this
26546      */
26547     render : function(){
26548         this.items.each(function(f){
26549             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26550                 f.applyTo(f.id);
26551             }
26552         });
26553         return this;
26554     },
26555
26556     /**
26557      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26558      * @param {Object} values
26559      * @return {BasicForm} this
26560      */
26561     applyToFields : function(o){
26562         this.items.each(function(f){
26563            Roo.apply(f, o);
26564         });
26565         return this;
26566     },
26567
26568     /**
26569      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26570      * @param {Object} values
26571      * @return {BasicForm} this
26572      */
26573     applyIfToFields : function(o){
26574         this.items.each(function(f){
26575            Roo.applyIf(f, o);
26576         });
26577         return this;
26578     }
26579 });
26580
26581 // back compat
26582 Roo.BasicForm = Roo.form.BasicForm;/*
26583  * Based on:
26584  * Ext JS Library 1.1.1
26585  * Copyright(c) 2006-2007, Ext JS, LLC.
26586  *
26587  * Originally Released Under LGPL - original licence link has changed is not relivant.
26588  *
26589  * Fork - LGPL
26590  * <script type="text/javascript">
26591  */
26592
26593 /**
26594  * @class Roo.form.Form
26595  * @extends Roo.form.BasicForm
26596  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26597  * @constructor
26598  * @param {Object} config Configuration options
26599  */
26600 Roo.form.Form = function(config){
26601     var xitems =  [];
26602     if (config.items) {
26603         xitems = config.items;
26604         delete config.items;
26605     }
26606    
26607     
26608     Roo.form.Form.superclass.constructor.call(this, null, config);
26609     this.url = this.url || this.action;
26610     if(!this.root){
26611         this.root = new Roo.form.Layout(Roo.applyIf({
26612             id: Roo.id()
26613         }, config));
26614     }
26615     this.active = this.root;
26616     /**
26617      * Array of all the buttons that have been added to this form via {@link addButton}
26618      * @type Array
26619      */
26620     this.buttons = [];
26621     this.allItems = [];
26622     this.addEvents({
26623         /**
26624          * @event clientvalidation
26625          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26626          * @param {Form} this
26627          * @param {Boolean} valid true if the form has passed client-side validation
26628          */
26629         clientvalidation: true,
26630         /**
26631          * @event rendered
26632          * Fires when the form is rendered
26633          * @param {Roo.form.Form} form
26634          */
26635         rendered : true
26636     });
26637     
26638     if (this.progressUrl) {
26639             // push a hidden field onto the list of fields..
26640             this.addxtype( {
26641                     xns: Roo.form, 
26642                     xtype : 'Hidden', 
26643                     name : 'UPLOAD_IDENTIFIER' 
26644             });
26645         }
26646         
26647     
26648     Roo.each(xitems, this.addxtype, this);
26649     
26650     
26651     
26652 };
26653
26654 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26655     /**
26656      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26657      */
26658     /**
26659      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26660      */
26661     /**
26662      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26663      */
26664     buttonAlign:'center',
26665
26666     /**
26667      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26668      */
26669     minButtonWidth:75,
26670
26671     /**
26672      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26673      * This property cascades to child containers if not set.
26674      */
26675     labelAlign:'left',
26676
26677     /**
26678      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26679      * fires a looping event with that state. This is required to bind buttons to the valid
26680      * state using the config value formBind:true on the button.
26681      */
26682     monitorValid : false,
26683
26684     /**
26685      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26686      */
26687     monitorPoll : 200,
26688     
26689     /**
26690      * @cfg {String} progressUrl - Url to return progress data 
26691      */
26692     
26693     progressUrl : false,
26694   
26695     /**
26696      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26697      * fields are added and the column is closed. If no fields are passed the column remains open
26698      * until end() is called.
26699      * @param {Object} config The config to pass to the column
26700      * @param {Field} field1 (optional)
26701      * @param {Field} field2 (optional)
26702      * @param {Field} etc (optional)
26703      * @return Column The column container object
26704      */
26705     column : function(c){
26706         var col = new Roo.form.Column(c);
26707         this.start(col);
26708         if(arguments.length > 1){ // duplicate code required because of Opera
26709             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26710             this.end();
26711         }
26712         return col;
26713     },
26714
26715     /**
26716      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26717      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26718      * until end() is called.
26719      * @param {Object} config The config to pass to the fieldset
26720      * @param {Field} field1 (optional)
26721      * @param {Field} field2 (optional)
26722      * @param {Field} etc (optional)
26723      * @return FieldSet The fieldset container object
26724      */
26725     fieldset : function(c){
26726         var fs = new Roo.form.FieldSet(c);
26727         this.start(fs);
26728         if(arguments.length > 1){ // duplicate code required because of Opera
26729             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26730             this.end();
26731         }
26732         return fs;
26733     },
26734
26735     /**
26736      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26737      * fields are added and the container is closed. If no fields are passed the container remains open
26738      * until end() is called.
26739      * @param {Object} config The config to pass to the Layout
26740      * @param {Field} field1 (optional)
26741      * @param {Field} field2 (optional)
26742      * @param {Field} etc (optional)
26743      * @return Layout The container object
26744      */
26745     container : function(c){
26746         var l = new Roo.form.Layout(c);
26747         this.start(l);
26748         if(arguments.length > 1){ // duplicate code required because of Opera
26749             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26750             this.end();
26751         }
26752         return l;
26753     },
26754
26755     /**
26756      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26757      * @param {Object} container A Roo.form.Layout or subclass of Layout
26758      * @return {Form} this
26759      */
26760     start : function(c){
26761         // cascade label info
26762         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26763         this.active.stack.push(c);
26764         c.ownerCt = this.active;
26765         this.active = c;
26766         return this;
26767     },
26768
26769     /**
26770      * Closes the current open container
26771      * @return {Form} this
26772      */
26773     end : function(){
26774         if(this.active == this.root){
26775             return this;
26776         }
26777         this.active = this.active.ownerCt;
26778         return this;
26779     },
26780
26781     /**
26782      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26783      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26784      * as the label of the field.
26785      * @param {Field} field1
26786      * @param {Field} field2 (optional)
26787      * @param {Field} etc. (optional)
26788      * @return {Form} this
26789      */
26790     add : function(){
26791         this.active.stack.push.apply(this.active.stack, arguments);
26792         this.allItems.push.apply(this.allItems,arguments);
26793         var r = [];
26794         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26795             if(a[i].isFormField){
26796                 r.push(a[i]);
26797             }
26798         }
26799         if(r.length > 0){
26800             Roo.form.Form.superclass.add.apply(this, r);
26801         }
26802         return this;
26803     },
26804     
26805
26806     
26807     
26808     
26809      /**
26810      * Find any element that has been added to a form, using it's ID or name
26811      * This can include framesets, columns etc. along with regular fields..
26812      * @param {String} id - id or name to find.
26813      
26814      * @return {Element} e - or false if nothing found.
26815      */
26816     findbyId : function(id)
26817     {
26818         var ret = false;
26819         if (!id) {
26820             return ret;
26821         }
26822         Roo.each(this.allItems, function(f){
26823             if (f.id == id || f.name == id ){
26824                 ret = f;
26825                 return false;
26826             }
26827         });
26828         return ret;
26829     },
26830
26831     
26832     
26833     /**
26834      * Render this form into the passed container. This should only be called once!
26835      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26836      * @return {Form} this
26837      */
26838     render : function(ct)
26839     {
26840         
26841         
26842         
26843         ct = Roo.get(ct);
26844         var o = this.autoCreate || {
26845             tag: 'form',
26846             method : this.method || 'POST',
26847             id : this.id || Roo.id()
26848         };
26849         this.initEl(ct.createChild(o));
26850
26851         this.root.render(this.el);
26852         
26853        
26854              
26855         this.items.each(function(f){
26856             f.render('x-form-el-'+f.id);
26857         });
26858
26859         if(this.buttons.length > 0){
26860             // tables are required to maintain order and for correct IE layout
26861             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26862                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26863                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26864             }}, null, true);
26865             var tr = tb.getElementsByTagName('tr')[0];
26866             for(var i = 0, len = this.buttons.length; i < len; i++) {
26867                 var b = this.buttons[i];
26868                 var td = document.createElement('td');
26869                 td.className = 'x-form-btn-td';
26870                 b.render(tr.appendChild(td));
26871             }
26872         }
26873         if(this.monitorValid){ // initialize after render
26874             this.startMonitoring();
26875         }
26876         this.fireEvent('rendered', this);
26877         return this;
26878     },
26879
26880     /**
26881      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26882      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26883      * object or a valid Roo.DomHelper element config
26884      * @param {Function} handler The function called when the button is clicked
26885      * @param {Object} scope (optional) The scope of the handler function
26886      * @return {Roo.Button}
26887      */
26888     addButton : function(config, handler, scope){
26889         var bc = {
26890             handler: handler,
26891             scope: scope,
26892             minWidth: this.minButtonWidth,
26893             hideParent:true
26894         };
26895         if(typeof config == "string"){
26896             bc.text = config;
26897         }else{
26898             Roo.apply(bc, config);
26899         }
26900         var btn = new Roo.Button(null, bc);
26901         this.buttons.push(btn);
26902         return btn;
26903     },
26904
26905      /**
26906      * Adds a series of form elements (using the xtype property as the factory method.
26907      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26908      * @param {Object} config 
26909      */
26910     
26911     addxtype : function()
26912     {
26913         var ar = Array.prototype.slice.call(arguments, 0);
26914         var ret = false;
26915         for(var i = 0; i < ar.length; i++) {
26916             if (!ar[i]) {
26917                 continue; // skip -- if this happends something invalid got sent, we 
26918                 // should ignore it, as basically that interface element will not show up
26919                 // and that should be pretty obvious!!
26920             }
26921             
26922             if (Roo.form[ar[i].xtype]) {
26923                 ar[i].form = this;
26924                 var fe = Roo.factory(ar[i], Roo.form);
26925                 if (!ret) {
26926                     ret = fe;
26927                 }
26928                 fe.form = this;
26929                 if (fe.store) {
26930                     fe.store.form = this;
26931                 }
26932                 if (fe.isLayout) {  
26933                          
26934                     this.start(fe);
26935                     this.allItems.push(fe);
26936                     if (fe.items && fe.addxtype) {
26937                         fe.addxtype.apply(fe, fe.items);
26938                         delete fe.items;
26939                     }
26940                      this.end();
26941                     continue;
26942                 }
26943                 
26944                 
26945                  
26946                 this.add(fe);
26947               //  console.log('adding ' + ar[i].xtype);
26948             }
26949             if (ar[i].xtype == 'Button') {  
26950                 //console.log('adding button');
26951                 //console.log(ar[i]);
26952                 this.addButton(ar[i]);
26953                 this.allItems.push(fe);
26954                 continue;
26955             }
26956             
26957             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26958                 alert('end is not supported on xtype any more, use items');
26959             //    this.end();
26960             //    //console.log('adding end');
26961             }
26962             
26963         }
26964         return ret;
26965     },
26966     
26967     /**
26968      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26969      * option "monitorValid"
26970      */
26971     startMonitoring : function(){
26972         if(!this.bound){
26973             this.bound = true;
26974             Roo.TaskMgr.start({
26975                 run : this.bindHandler,
26976                 interval : this.monitorPoll || 200,
26977                 scope: this
26978             });
26979         }
26980     },
26981
26982     /**
26983      * Stops monitoring of the valid state of this form
26984      */
26985     stopMonitoring : function(){
26986         this.bound = false;
26987     },
26988
26989     // private
26990     bindHandler : function(){
26991         if(!this.bound){
26992             return false; // stops binding
26993         }
26994         var valid = true;
26995         this.items.each(function(f){
26996             if(!f.isValid(true)){
26997                 valid = false;
26998                 return false;
26999             }
27000         });
27001         for(var i = 0, len = this.buttons.length; i < len; i++){
27002             var btn = this.buttons[i];
27003             if(btn.formBind === true && btn.disabled === valid){
27004                 btn.setDisabled(!valid);
27005             }
27006         }
27007         this.fireEvent('clientvalidation', this, valid);
27008     }
27009     
27010     
27011     
27012     
27013     
27014     
27015     
27016     
27017 });
27018
27019
27020 // back compat
27021 Roo.Form = Roo.form.Form;
27022 /*
27023  * Based on:
27024  * Ext JS Library 1.1.1
27025  * Copyright(c) 2006-2007, Ext JS, LLC.
27026  *
27027  * Originally Released Under LGPL - original licence link has changed is not relivant.
27028  *
27029  * Fork - LGPL
27030  * <script type="text/javascript">
27031  */
27032  
27033  /**
27034  * @class Roo.form.Action
27035  * Internal Class used to handle form actions
27036  * @constructor
27037  * @param {Roo.form.BasicForm} el The form element or its id
27038  * @param {Object} config Configuration options
27039  */
27040  
27041  
27042 // define the action interface
27043 Roo.form.Action = function(form, options){
27044     this.form = form;
27045     this.options = options || {};
27046 };
27047 /**
27048  * Client Validation Failed
27049  * @const 
27050  */
27051 Roo.form.Action.CLIENT_INVALID = 'client';
27052 /**
27053  * Server Validation Failed
27054  * @const 
27055  */
27056  Roo.form.Action.SERVER_INVALID = 'server';
27057  /**
27058  * Connect to Server Failed
27059  * @const 
27060  */
27061 Roo.form.Action.CONNECT_FAILURE = 'connect';
27062 /**
27063  * Reading Data from Server Failed
27064  * @const 
27065  */
27066 Roo.form.Action.LOAD_FAILURE = 'load';
27067
27068 Roo.form.Action.prototype = {
27069     type : 'default',
27070     failureType : undefined,
27071     response : undefined,
27072     result : undefined,
27073
27074     // interface method
27075     run : function(options){
27076
27077     },
27078
27079     // interface method
27080     success : function(response){
27081
27082     },
27083
27084     // interface method
27085     handleResponse : function(response){
27086
27087     },
27088
27089     // default connection failure
27090     failure : function(response){
27091         
27092         this.response = response;
27093         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27094         this.form.afterAction(this, false);
27095     },
27096
27097     processResponse : function(response){
27098         this.response = response;
27099         if(!response.responseText){
27100             return true;
27101         }
27102         this.result = this.handleResponse(response);
27103         return this.result;
27104     },
27105
27106     // utility functions used internally
27107     getUrl : function(appendParams){
27108         var url = this.options.url || this.form.url || this.form.el.dom.action;
27109         if(appendParams){
27110             var p = this.getParams();
27111             if(p){
27112                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27113             }
27114         }
27115         return url;
27116     },
27117
27118     getMethod : function(){
27119         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27120     },
27121
27122     getParams : function(){
27123         var bp = this.form.baseParams;
27124         var p = this.options.params;
27125         if(p){
27126             if(typeof p == "object"){
27127                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27128             }else if(typeof p == 'string' && bp){
27129                 p += '&' + Roo.urlEncode(bp);
27130             }
27131         }else if(bp){
27132             p = Roo.urlEncode(bp);
27133         }
27134         return p;
27135     },
27136
27137     createCallback : function(){
27138         return {
27139             success: this.success,
27140             failure: this.failure,
27141             scope: this,
27142             timeout: (this.form.timeout*1000),
27143             upload: this.form.fileUpload ? this.success : undefined
27144         };
27145     }
27146 };
27147
27148 Roo.form.Action.Submit = function(form, options){
27149     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27150 };
27151
27152 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27153     type : 'submit',
27154
27155     haveProgress : false,
27156     uploadComplete : false,
27157     
27158     // uploadProgress indicator.
27159     uploadProgress : function()
27160     {
27161         if (!this.form.progressUrl) {
27162             return;
27163         }
27164         
27165         if (!this.haveProgress) {
27166             Roo.MessageBox.progress("Uploading", "Uploading");
27167         }
27168         if (this.uploadComplete) {
27169            Roo.MessageBox.hide();
27170            return;
27171         }
27172         
27173         this.haveProgress = true;
27174    
27175         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27176         
27177         var c = new Roo.data.Connection();
27178         c.request({
27179             url : this.form.progressUrl,
27180             params: {
27181                 id : uid
27182             },
27183             method: 'GET',
27184             success : function(req){
27185                //console.log(data);
27186                 var rdata = false;
27187                 var edata;
27188                 try  {
27189                    rdata = Roo.decode(req.responseText)
27190                 } catch (e) {
27191                     Roo.log("Invalid data from server..");
27192                     Roo.log(edata);
27193                     return;
27194                 }
27195                 if (!rdata || !rdata.success) {
27196                     Roo.log(rdata);
27197                     return;
27198                 }
27199                 var data = rdata.data;
27200                 
27201                 if (this.uploadComplete) {
27202                    Roo.MessageBox.hide();
27203                    return;
27204                 }
27205                    
27206                 if (data){
27207                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27208                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27209                     );
27210                 }
27211                 this.uploadProgress.defer(2000,this);
27212             },
27213        
27214             failure: function(data) {
27215                 Roo.log('progress url failed ');
27216                 Roo.log(data);
27217             },
27218             scope : this
27219         });
27220            
27221     },
27222     
27223     
27224     run : function()
27225     {
27226         // run get Values on the form, so it syncs any secondary forms.
27227         this.form.getValues();
27228         
27229         var o = this.options;
27230         var method = this.getMethod();
27231         var isPost = method == 'POST';
27232         if(o.clientValidation === false || this.form.isValid()){
27233             
27234             if (this.form.progressUrl) {
27235                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27236                     (new Date() * 1) + '' + Math.random());
27237                     
27238             } 
27239             
27240             
27241             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27242                 form:this.form.el.dom,
27243                 url:this.getUrl(!isPost),
27244                 method: method,
27245                 params:isPost ? this.getParams() : null,
27246                 isUpload: this.form.fileUpload
27247             }));
27248             
27249             this.uploadProgress();
27250
27251         }else if (o.clientValidation !== false){ // client validation failed
27252             this.failureType = Roo.form.Action.CLIENT_INVALID;
27253             this.form.afterAction(this, false);
27254         }
27255     },
27256
27257     success : function(response)
27258     {
27259         this.uploadComplete= true;
27260         if (this.haveProgress) {
27261             Roo.MessageBox.hide();
27262         }
27263         
27264         
27265         var result = this.processResponse(response);
27266         if(result === true || result.success){
27267             this.form.afterAction(this, true);
27268             return;
27269         }
27270         if(result.errors){
27271             this.form.markInvalid(result.errors);
27272             this.failureType = Roo.form.Action.SERVER_INVALID;
27273         }
27274         this.form.afterAction(this, false);
27275     },
27276     failure : function(response)
27277     {
27278         this.uploadComplete= true;
27279         if (this.haveProgress) {
27280             Roo.MessageBox.hide();
27281         }
27282         
27283         
27284         this.response = response;
27285         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27286         this.form.afterAction(this, false);
27287     },
27288     
27289     handleResponse : function(response){
27290         if(this.form.errorReader){
27291             var rs = this.form.errorReader.read(response);
27292             var errors = [];
27293             if(rs.records){
27294                 for(var i = 0, len = rs.records.length; i < len; i++) {
27295                     var r = rs.records[i];
27296                     errors[i] = r.data;
27297                 }
27298             }
27299             if(errors.length < 1){
27300                 errors = null;
27301             }
27302             return {
27303                 success : rs.success,
27304                 errors : errors
27305             };
27306         }
27307         var ret = false;
27308         try {
27309             ret = Roo.decode(response.responseText);
27310         } catch (e) {
27311             ret = {
27312                 success: false,
27313                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27314                 errors : []
27315             };
27316         }
27317         return ret;
27318         
27319     }
27320 });
27321
27322
27323 Roo.form.Action.Load = function(form, options){
27324     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27325     this.reader = this.form.reader;
27326 };
27327
27328 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27329     type : 'load',
27330
27331     run : function(){
27332         
27333         Roo.Ajax.request(Roo.apply(
27334                 this.createCallback(), {
27335                     method:this.getMethod(),
27336                     url:this.getUrl(false),
27337                     params:this.getParams()
27338         }));
27339     },
27340
27341     success : function(response){
27342         
27343         var result = this.processResponse(response);
27344         if(result === true || !result.success || !result.data){
27345             this.failureType = Roo.form.Action.LOAD_FAILURE;
27346             this.form.afterAction(this, false);
27347             return;
27348         }
27349         this.form.clearInvalid();
27350         this.form.setValues(result.data);
27351         this.form.afterAction(this, true);
27352     },
27353
27354     handleResponse : function(response){
27355         if(this.form.reader){
27356             var rs = this.form.reader.read(response);
27357             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27358             return {
27359                 success : rs.success,
27360                 data : data
27361             };
27362         }
27363         return Roo.decode(response.responseText);
27364     }
27365 });
27366
27367 Roo.form.Action.ACTION_TYPES = {
27368     'load' : Roo.form.Action.Load,
27369     'submit' : Roo.form.Action.Submit
27370 };/*
27371  * Based on:
27372  * Ext JS Library 1.1.1
27373  * Copyright(c) 2006-2007, Ext JS, LLC.
27374  *
27375  * Originally Released Under LGPL - original licence link has changed is not relivant.
27376  *
27377  * Fork - LGPL
27378  * <script type="text/javascript">
27379  */
27380  
27381 /**
27382  * @class Roo.form.Layout
27383  * @extends Roo.Component
27384  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27385  * @constructor
27386  * @param {Object} config Configuration options
27387  */
27388 Roo.form.Layout = function(config){
27389     var xitems = [];
27390     if (config.items) {
27391         xitems = config.items;
27392         delete config.items;
27393     }
27394     Roo.form.Layout.superclass.constructor.call(this, config);
27395     this.stack = [];
27396     Roo.each(xitems, this.addxtype, this);
27397      
27398 };
27399
27400 Roo.extend(Roo.form.Layout, Roo.Component, {
27401     /**
27402      * @cfg {String/Object} autoCreate
27403      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27404      */
27405     /**
27406      * @cfg {String/Object/Function} style
27407      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27408      * a function which returns such a specification.
27409      */
27410     /**
27411      * @cfg {String} labelAlign
27412      * Valid values are "left," "top" and "right" (defaults to "left")
27413      */
27414     /**
27415      * @cfg {Number} labelWidth
27416      * Fixed width in pixels of all field labels (defaults to undefined)
27417      */
27418     /**
27419      * @cfg {Boolean} clear
27420      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27421      */
27422     clear : true,
27423     /**
27424      * @cfg {String} labelSeparator
27425      * The separator to use after field labels (defaults to ':')
27426      */
27427     labelSeparator : ':',
27428     /**
27429      * @cfg {Boolean} hideLabels
27430      * True to suppress the display of field labels in this layout (defaults to false)
27431      */
27432     hideLabels : false,
27433
27434     // private
27435     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27436     
27437     isLayout : true,
27438     
27439     // private
27440     onRender : function(ct, position){
27441         if(this.el){ // from markup
27442             this.el = Roo.get(this.el);
27443         }else {  // generate
27444             var cfg = this.getAutoCreate();
27445             this.el = ct.createChild(cfg, position);
27446         }
27447         if(this.style){
27448             this.el.applyStyles(this.style);
27449         }
27450         if(this.labelAlign){
27451             this.el.addClass('x-form-label-'+this.labelAlign);
27452         }
27453         if(this.hideLabels){
27454             this.labelStyle = "display:none";
27455             this.elementStyle = "padding-left:0;";
27456         }else{
27457             if(typeof this.labelWidth == 'number'){
27458                 this.labelStyle = "width:"+this.labelWidth+"px;";
27459                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27460             }
27461             if(this.labelAlign == 'top'){
27462                 this.labelStyle = "width:auto;";
27463                 this.elementStyle = "padding-left:0;";
27464             }
27465         }
27466         var stack = this.stack;
27467         var slen = stack.length;
27468         if(slen > 0){
27469             if(!this.fieldTpl){
27470                 var t = new Roo.Template(
27471                     '<div class="x-form-item {5}">',
27472                         '<label for="{0}" style="{2}">{1}{4}</label>',
27473                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27474                         '</div>',
27475                     '</div><div class="x-form-clear-left"></div>'
27476                 );
27477                 t.disableFormats = true;
27478                 t.compile();
27479                 Roo.form.Layout.prototype.fieldTpl = t;
27480             }
27481             for(var i = 0; i < slen; i++) {
27482                 if(stack[i].isFormField){
27483                     this.renderField(stack[i]);
27484                 }else{
27485                     this.renderComponent(stack[i]);
27486                 }
27487             }
27488         }
27489         if(this.clear){
27490             this.el.createChild({cls:'x-form-clear'});
27491         }
27492     },
27493
27494     // private
27495     renderField : function(f){
27496         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27497                f.id, //0
27498                f.fieldLabel, //1
27499                f.labelStyle||this.labelStyle||'', //2
27500                this.elementStyle||'', //3
27501                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27502                f.itemCls||this.itemCls||''  //5
27503        ], true).getPrevSibling());
27504     },
27505
27506     // private
27507     renderComponent : function(c){
27508         c.render(c.isLayout ? this.el : this.el.createChild());    
27509     },
27510     /**
27511      * Adds a object form elements (using the xtype property as the factory method.)
27512      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27513      * @param {Object} config 
27514      */
27515     addxtype : function(o)
27516     {
27517         // create the lement.
27518         o.form = this.form;
27519         var fe = Roo.factory(o, Roo.form);
27520         this.form.allItems.push(fe);
27521         this.stack.push(fe);
27522         
27523         if (fe.isFormField) {
27524             this.form.items.add(fe);
27525         }
27526          
27527         return fe;
27528     }
27529 });
27530
27531 /**
27532  * @class Roo.form.Column
27533  * @extends Roo.form.Layout
27534  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27535  * @constructor
27536  * @param {Object} config Configuration options
27537  */
27538 Roo.form.Column = function(config){
27539     Roo.form.Column.superclass.constructor.call(this, config);
27540 };
27541
27542 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27543     /**
27544      * @cfg {Number/String} width
27545      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27546      */
27547     /**
27548      * @cfg {String/Object} autoCreate
27549      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27550      */
27551
27552     // private
27553     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27554
27555     // private
27556     onRender : function(ct, position){
27557         Roo.form.Column.superclass.onRender.call(this, ct, position);
27558         if(this.width){
27559             this.el.setWidth(this.width);
27560         }
27561     }
27562 });
27563
27564
27565 /**
27566  * @class Roo.form.Row
27567  * @extends Roo.form.Layout
27568  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27569  * @constructor
27570  * @param {Object} config Configuration options
27571  */
27572
27573  
27574 Roo.form.Row = function(config){
27575     Roo.form.Row.superclass.constructor.call(this, config);
27576 };
27577  
27578 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27579       /**
27580      * @cfg {Number/String} width
27581      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27582      */
27583     /**
27584      * @cfg {Number/String} height
27585      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27586      */
27587     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27588     
27589     padWidth : 20,
27590     // private
27591     onRender : function(ct, position){
27592         //console.log('row render');
27593         if(!this.rowTpl){
27594             var t = new Roo.Template(
27595                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27596                     '<label for="{0}" style="{2}">{1}{4}</label>',
27597                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27598                     '</div>',
27599                 '</div>'
27600             );
27601             t.disableFormats = true;
27602             t.compile();
27603             Roo.form.Layout.prototype.rowTpl = t;
27604         }
27605         this.fieldTpl = this.rowTpl;
27606         
27607         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27608         var labelWidth = 100;
27609         
27610         if ((this.labelAlign != 'top')) {
27611             if (typeof this.labelWidth == 'number') {
27612                 labelWidth = this.labelWidth
27613             }
27614             this.padWidth =  20 + labelWidth;
27615             
27616         }
27617         
27618         Roo.form.Column.superclass.onRender.call(this, ct, position);
27619         if(this.width){
27620             this.el.setWidth(this.width);
27621         }
27622         if(this.height){
27623             this.el.setHeight(this.height);
27624         }
27625     },
27626     
27627     // private
27628     renderField : function(f){
27629         f.fieldEl = this.fieldTpl.append(this.el, [
27630                f.id, f.fieldLabel,
27631                f.labelStyle||this.labelStyle||'',
27632                this.elementStyle||'',
27633                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27634                f.itemCls||this.itemCls||'',
27635                f.width ? f.width + this.padWidth : 160 + this.padWidth
27636        ],true);
27637     }
27638 });
27639  
27640
27641 /**
27642  * @class Roo.form.FieldSet
27643  * @extends Roo.form.Layout
27644  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27645  * @constructor
27646  * @param {Object} config Configuration options
27647  */
27648 Roo.form.FieldSet = function(config){
27649     Roo.form.FieldSet.superclass.constructor.call(this, config);
27650 };
27651
27652 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27653     /**
27654      * @cfg {String} legend
27655      * The text to display as the legend for the FieldSet (defaults to '')
27656      */
27657     /**
27658      * @cfg {String/Object} autoCreate
27659      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27660      */
27661
27662     // private
27663     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27664
27665     // private
27666     onRender : function(ct, position){
27667         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27668         if(this.legend){
27669             this.setLegend(this.legend);
27670         }
27671     },
27672
27673     // private
27674     setLegend : function(text){
27675         if(this.rendered){
27676             this.el.child('legend').update(text);
27677         }
27678     }
27679 });/*
27680  * Based on:
27681  * Ext JS Library 1.1.1
27682  * Copyright(c) 2006-2007, Ext JS, LLC.
27683  *
27684  * Originally Released Under LGPL - original licence link has changed is not relivant.
27685  *
27686  * Fork - LGPL
27687  * <script type="text/javascript">
27688  */
27689 /**
27690  * @class Roo.form.VTypes
27691  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27692  * @singleton
27693  */
27694 Roo.form.VTypes = function(){
27695     // closure these in so they are only created once.
27696     var alpha = /^[a-zA-Z_]+$/;
27697     var alphanum = /^[a-zA-Z0-9_]+$/;
27698     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27699     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27700
27701     // All these messages and functions are configurable
27702     return {
27703         /**
27704          * The function used to validate email addresses
27705          * @param {String} value The email address
27706          */
27707         'email' : function(v){
27708             return email.test(v);
27709         },
27710         /**
27711          * The error text to display when the email validation function returns false
27712          * @type String
27713          */
27714         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27715         /**
27716          * The keystroke filter mask to be applied on email input
27717          * @type RegExp
27718          */
27719         'emailMask' : /[a-z0-9_\.\-@]/i,
27720
27721         /**
27722          * The function used to validate URLs
27723          * @param {String} value The URL
27724          */
27725         'url' : function(v){
27726             return url.test(v);
27727         },
27728         /**
27729          * The error text to display when the url validation function returns false
27730          * @type String
27731          */
27732         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27733         
27734         /**
27735          * The function used to validate alpha values
27736          * @param {String} value The value
27737          */
27738         'alpha' : function(v){
27739             return alpha.test(v);
27740         },
27741         /**
27742          * The error text to display when the alpha validation function returns false
27743          * @type String
27744          */
27745         'alphaText' : 'This field should only contain letters and _',
27746         /**
27747          * The keystroke filter mask to be applied on alpha input
27748          * @type RegExp
27749          */
27750         'alphaMask' : /[a-z_]/i,
27751
27752         /**
27753          * The function used to validate alphanumeric values
27754          * @param {String} value The value
27755          */
27756         'alphanum' : function(v){
27757             return alphanum.test(v);
27758         },
27759         /**
27760          * The error text to display when the alphanumeric validation function returns false
27761          * @type String
27762          */
27763         'alphanumText' : 'This field should only contain letters, numbers and _',
27764         /**
27765          * The keystroke filter mask to be applied on alphanumeric input
27766          * @type RegExp
27767          */
27768         'alphanumMask' : /[a-z0-9_]/i
27769     };
27770 }();//<script type="text/javascript">
27771
27772 /**
27773  * @class Roo.form.FCKeditor
27774  * @extends Roo.form.TextArea
27775  * Wrapper around the FCKEditor http://www.fckeditor.net
27776  * @constructor
27777  * Creates a new FCKeditor
27778  * @param {Object} config Configuration options
27779  */
27780 Roo.form.FCKeditor = function(config){
27781     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27782     this.addEvents({
27783          /**
27784          * @event editorinit
27785          * Fired when the editor is initialized - you can add extra handlers here..
27786          * @param {FCKeditor} this
27787          * @param {Object} the FCK object.
27788          */
27789         editorinit : true
27790     });
27791     
27792     
27793 };
27794 Roo.form.FCKeditor.editors = { };
27795 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27796 {
27797     //defaultAutoCreate : {
27798     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27799     //},
27800     // private
27801     /**
27802      * @cfg {Object} fck options - see fck manual for details.
27803      */
27804     fckconfig : false,
27805     
27806     /**
27807      * @cfg {Object} fck toolbar set (Basic or Default)
27808      */
27809     toolbarSet : 'Basic',
27810     /**
27811      * @cfg {Object} fck BasePath
27812      */ 
27813     basePath : '/fckeditor/',
27814     
27815     
27816     frame : false,
27817     
27818     value : '',
27819     
27820    
27821     onRender : function(ct, position)
27822     {
27823         if(!this.el){
27824             this.defaultAutoCreate = {
27825                 tag: "textarea",
27826                 style:"width:300px;height:60px;",
27827                 autocomplete: "off"
27828             };
27829         }
27830         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27831         /*
27832         if(this.grow){
27833             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27834             if(this.preventScrollbars){
27835                 this.el.setStyle("overflow", "hidden");
27836             }
27837             this.el.setHeight(this.growMin);
27838         }
27839         */
27840         //console.log('onrender' + this.getId() );
27841         Roo.form.FCKeditor.editors[this.getId()] = this;
27842          
27843
27844         this.replaceTextarea() ;
27845         
27846     },
27847     
27848     getEditor : function() {
27849         return this.fckEditor;
27850     },
27851     /**
27852      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27853      * @param {Mixed} value The value to set
27854      */
27855     
27856     
27857     setValue : function(value)
27858     {
27859         //console.log('setValue: ' + value);
27860         
27861         if(typeof(value) == 'undefined') { // not sure why this is happending...
27862             return;
27863         }
27864         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27865         
27866         //if(!this.el || !this.getEditor()) {
27867         //    this.value = value;
27868             //this.setValue.defer(100,this,[value]);    
27869         //    return;
27870         //} 
27871         
27872         if(!this.getEditor()) {
27873             return;
27874         }
27875         
27876         this.getEditor().SetData(value);
27877         
27878         //
27879
27880     },
27881
27882     /**
27883      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27884      * @return {Mixed} value The field value
27885      */
27886     getValue : function()
27887     {
27888         
27889         if (this.frame && this.frame.dom.style.display == 'none') {
27890             return Roo.form.FCKeditor.superclass.getValue.call(this);
27891         }
27892         
27893         if(!this.el || !this.getEditor()) {
27894            
27895            // this.getValue.defer(100,this); 
27896             return this.value;
27897         }
27898        
27899         
27900         var value=this.getEditor().GetData();
27901         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27902         return Roo.form.FCKeditor.superclass.getValue.call(this);
27903         
27904
27905     },
27906
27907     /**
27908      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27909      * @return {Mixed} value The field value
27910      */
27911     getRawValue : function()
27912     {
27913         if (this.frame && this.frame.dom.style.display == 'none') {
27914             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27915         }
27916         
27917         if(!this.el || !this.getEditor()) {
27918             //this.getRawValue.defer(100,this); 
27919             return this.value;
27920             return;
27921         }
27922         
27923         
27924         
27925         var value=this.getEditor().GetData();
27926         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27927         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27928          
27929     },
27930     
27931     setSize : function(w,h) {
27932         
27933         
27934         
27935         //if (this.frame && this.frame.dom.style.display == 'none') {
27936         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27937         //    return;
27938         //}
27939         //if(!this.el || !this.getEditor()) {
27940         //    this.setSize.defer(100,this, [w,h]); 
27941         //    return;
27942         //}
27943         
27944         
27945         
27946         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27947         
27948         this.frame.dom.setAttribute('width', w);
27949         this.frame.dom.setAttribute('height', h);
27950         this.frame.setSize(w,h);
27951         
27952     },
27953     
27954     toggleSourceEdit : function(value) {
27955         
27956       
27957          
27958         this.el.dom.style.display = value ? '' : 'none';
27959         this.frame.dom.style.display = value ?  'none' : '';
27960         
27961     },
27962     
27963     
27964     focus: function(tag)
27965     {
27966         if (this.frame.dom.style.display == 'none') {
27967             return Roo.form.FCKeditor.superclass.focus.call(this);
27968         }
27969         if(!this.el || !this.getEditor()) {
27970             this.focus.defer(100,this, [tag]); 
27971             return;
27972         }
27973         
27974         
27975         
27976         
27977         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27978         this.getEditor().Focus();
27979         if (tgs.length) {
27980             if (!this.getEditor().Selection.GetSelection()) {
27981                 this.focus.defer(100,this, [tag]); 
27982                 return;
27983             }
27984             
27985             
27986             var r = this.getEditor().EditorDocument.createRange();
27987             r.setStart(tgs[0],0);
27988             r.setEnd(tgs[0],0);
27989             this.getEditor().Selection.GetSelection().removeAllRanges();
27990             this.getEditor().Selection.GetSelection().addRange(r);
27991             this.getEditor().Focus();
27992         }
27993         
27994     },
27995     
27996     
27997     
27998     replaceTextarea : function()
27999     {
28000         if ( document.getElementById( this.getId() + '___Frame' ) )
28001             return ;
28002         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28003         //{
28004             // We must check the elements firstly using the Id and then the name.
28005         var oTextarea = document.getElementById( this.getId() );
28006         
28007         var colElementsByName = document.getElementsByName( this.getId() ) ;
28008          
28009         oTextarea.style.display = 'none' ;
28010
28011         if ( oTextarea.tabIndex ) {            
28012             this.TabIndex = oTextarea.tabIndex ;
28013         }
28014         
28015         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28016         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28017         this.frame = Roo.get(this.getId() + '___Frame')
28018     },
28019     
28020     _getConfigHtml : function()
28021     {
28022         var sConfig = '' ;
28023
28024         for ( var o in this.fckconfig ) {
28025             sConfig += sConfig.length > 0  ? '&amp;' : '';
28026             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28027         }
28028
28029         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28030     },
28031     
28032     
28033     _getIFrameHtml : function()
28034     {
28035         var sFile = 'fckeditor.html' ;
28036         /* no idea what this is about..
28037         try
28038         {
28039             if ( (/fcksource=true/i).test( window.top.location.search ) )
28040                 sFile = 'fckeditor.original.html' ;
28041         }
28042         catch (e) { 
28043         */
28044
28045         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28046         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28047         
28048         
28049         var html = '<iframe id="' + this.getId() +
28050             '___Frame" src="' + sLink +
28051             '" width="' + this.width +
28052             '" height="' + this.height + '"' +
28053             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28054             ' frameborder="0" scrolling="no"></iframe>' ;
28055
28056         return html ;
28057     },
28058     
28059     _insertHtmlBefore : function( html, element )
28060     {
28061         if ( element.insertAdjacentHTML )       {
28062             // IE
28063             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28064         } else { // Gecko
28065             var oRange = document.createRange() ;
28066             oRange.setStartBefore( element ) ;
28067             var oFragment = oRange.createContextualFragment( html );
28068             element.parentNode.insertBefore( oFragment, element ) ;
28069         }
28070     }
28071     
28072     
28073   
28074     
28075     
28076     
28077     
28078
28079 });
28080
28081 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28082
28083 function FCKeditor_OnComplete(editorInstance){
28084     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28085     f.fckEditor = editorInstance;
28086     //console.log("loaded");
28087     f.fireEvent('editorinit', f, editorInstance);
28088
28089   
28090
28091  
28092
28093
28094
28095
28096
28097
28098
28099
28100
28101
28102
28103
28104
28105
28106
28107 //<script type="text/javascript">
28108 /**
28109  * @class Roo.form.GridField
28110  * @extends Roo.form.Field
28111  * Embed a grid (or editable grid into a form)
28112  * STATUS ALPHA
28113  * 
28114  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28115  * it needs 
28116  * xgrid.store = Roo.data.Store
28117  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28118  * xgrid.store.reader = Roo.data.JsonReader 
28119  * 
28120  * 
28121  * @constructor
28122  * Creates a new GridField
28123  * @param {Object} config Configuration options
28124  */
28125 Roo.form.GridField = function(config){
28126     Roo.form.GridField.superclass.constructor.call(this, config);
28127      
28128 };
28129
28130 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28131     /**
28132      * @cfg {Number} width  - used to restrict width of grid..
28133      */
28134     width : 100,
28135     /**
28136      * @cfg {Number} height - used to restrict height of grid..
28137      */
28138     height : 50,
28139      /**
28140      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28141          * 
28142          *}
28143      */
28144     xgrid : false, 
28145     /**
28146      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28147      * {tag: "input", type: "checkbox", autocomplete: "off"})
28148      */
28149    // defaultAutoCreate : { tag: 'div' },
28150     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28151     /**
28152      * @cfg {String} addTitle Text to include for adding a title.
28153      */
28154     addTitle : false,
28155     //
28156     onResize : function(){
28157         Roo.form.Field.superclass.onResize.apply(this, arguments);
28158     },
28159
28160     initEvents : function(){
28161         // Roo.form.Checkbox.superclass.initEvents.call(this);
28162         // has no events...
28163        
28164     },
28165
28166
28167     getResizeEl : function(){
28168         return this.wrap;
28169     },
28170
28171     getPositionEl : function(){
28172         return this.wrap;
28173     },
28174
28175     // private
28176     onRender : function(ct, position){
28177         
28178         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28179         var style = this.style;
28180         delete this.style;
28181         
28182         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28183         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28184         this.viewEl = this.wrap.createChild({ tag: 'div' });
28185         if (style) {
28186             this.viewEl.applyStyles(style);
28187         }
28188         if (this.width) {
28189             this.viewEl.setWidth(this.width);
28190         }
28191         if (this.height) {
28192             this.viewEl.setHeight(this.height);
28193         }
28194         //if(this.inputValue !== undefined){
28195         //this.setValue(this.value);
28196         
28197         
28198         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28199         
28200         
28201         this.grid.render();
28202         this.grid.getDataSource().on('remove', this.refreshValue, this);
28203         this.grid.getDataSource().on('update', this.refreshValue, this);
28204         this.grid.on('afteredit', this.refreshValue, this);
28205  
28206     },
28207      
28208     
28209     /**
28210      * Sets the value of the item. 
28211      * @param {String} either an object  or a string..
28212      */
28213     setValue : function(v){
28214         //this.value = v;
28215         v = v || []; // empty set..
28216         // this does not seem smart - it really only affects memoryproxy grids..
28217         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28218             var ds = this.grid.getDataSource();
28219             // assumes a json reader..
28220             var data = {}
28221             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28222             ds.loadData( data);
28223         }
28224         Roo.form.GridField.superclass.setValue.call(this, v);
28225         this.refreshValue();
28226         // should load data in the grid really....
28227     },
28228     
28229     // private
28230     refreshValue: function() {
28231          var val = [];
28232         this.grid.getDataSource().each(function(r) {
28233             val.push(r.data);
28234         });
28235         this.el.dom.value = Roo.encode(val);
28236     }
28237     
28238      
28239     
28240     
28241 });/*
28242  * Based on:
28243  * Ext JS Library 1.1.1
28244  * Copyright(c) 2006-2007, Ext JS, LLC.
28245  *
28246  * Originally Released Under LGPL - original licence link has changed is not relivant.
28247  *
28248  * Fork - LGPL
28249  * <script type="text/javascript">
28250  */
28251 /**
28252  * @class Roo.form.DisplayField
28253  * @extends Roo.form.Field
28254  * A generic Field to display non-editable data.
28255  * @constructor
28256  * Creates a new Display Field item.
28257  * @param {Object} config Configuration options
28258  */
28259 Roo.form.DisplayField = function(config){
28260     Roo.form.DisplayField.superclass.constructor.call(this, config);
28261     
28262 };
28263
28264 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28265     inputType:      'hidden',
28266     allowBlank:     true,
28267     readOnly:         true,
28268     
28269  
28270     /**
28271      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28272      */
28273     focusClass : undefined,
28274     /**
28275      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28276      */
28277     fieldClass: 'x-form-field',
28278     
28279      /**
28280      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28281      */
28282     valueRenderer: undefined,
28283     
28284     width: 100,
28285     /**
28286      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28287      * {tag: "input", type: "checkbox", autocomplete: "off"})
28288      */
28289      
28290  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28291
28292     onResize : function(){
28293         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28294         
28295     },
28296
28297     initEvents : function(){
28298         // Roo.form.Checkbox.superclass.initEvents.call(this);
28299         // has no events...
28300        
28301     },
28302
28303
28304     getResizeEl : function(){
28305         return this.wrap;
28306     },
28307
28308     getPositionEl : function(){
28309         return this.wrap;
28310     },
28311
28312     // private
28313     onRender : function(ct, position){
28314         
28315         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28316         //if(this.inputValue !== undefined){
28317         this.wrap = this.el.wrap();
28318         
28319         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28320         
28321         if (this.bodyStyle) {
28322             this.viewEl.applyStyles(this.bodyStyle);
28323         }
28324         //this.viewEl.setStyle('padding', '2px');
28325         
28326         this.setValue(this.value);
28327         
28328     },
28329 /*
28330     // private
28331     initValue : Roo.emptyFn,
28332
28333   */
28334
28335         // private
28336     onClick : function(){
28337         
28338     },
28339
28340     /**
28341      * Sets the checked state of the checkbox.
28342      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28343      */
28344     setValue : function(v){
28345         this.value = v;
28346         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28347         // this might be called before we have a dom element..
28348         if (!this.viewEl) {
28349             return;
28350         }
28351         this.viewEl.dom.innerHTML = html;
28352         Roo.form.DisplayField.superclass.setValue.call(this, v);
28353
28354     }
28355 });/*
28356  * 
28357  * Licence- LGPL
28358  * 
28359  */
28360
28361 /**
28362  * @class Roo.form.DayPicker
28363  * @extends Roo.form.Field
28364  * A Day picker show [M] [T] [W] ....
28365  * @constructor
28366  * Creates a new Day Picker
28367  * @param {Object} config Configuration options
28368  */
28369 Roo.form.DayPicker= function(config){
28370     Roo.form.DayPicker.superclass.constructor.call(this, config);
28371      
28372 };
28373
28374 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28375     /**
28376      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28377      */
28378     focusClass : undefined,
28379     /**
28380      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28381      */
28382     fieldClass: "x-form-field",
28383    
28384     /**
28385      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28386      * {tag: "input", type: "checkbox", autocomplete: "off"})
28387      */
28388     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
28389     
28390    
28391     actionMode : 'viewEl', 
28392     //
28393     // private
28394  
28395     inputType : 'hidden',
28396     
28397      
28398     inputElement: false, // real input element?
28399     basedOn: false, // ????
28400     
28401     isFormField: true, // not sure where this is needed!!!!
28402
28403     onResize : function(){
28404         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
28405         if(!this.boxLabel){
28406             this.el.alignTo(this.wrap, 'c-c');
28407         }
28408     },
28409
28410     initEvents : function(){
28411         Roo.form.Checkbox.superclass.initEvents.call(this);
28412         this.el.on("click", this.onClick,  this);
28413         this.el.on("change", this.onClick,  this);
28414     },
28415
28416
28417     getResizeEl : function(){
28418         return this.wrap;
28419     },
28420
28421     getPositionEl : function(){
28422         return this.wrap;
28423     },
28424
28425     
28426     // private
28427     onRender : function(ct, position){
28428         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
28429        
28430         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
28431         
28432         var r1 = '<table><tr>';
28433         var r2 = '<tr class="x-form-daypick-icons">';
28434         for (var i=0; i < 7; i++) {
28435             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
28436             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
28437         }
28438         
28439         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
28440         viewEl.select('img').on('click', this.onClick, this);
28441         this.viewEl = viewEl;   
28442         
28443         
28444         // this will not work on Chrome!!!
28445         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
28446         this.el.on('propertychange', this.setFromHidden,  this);  //ie
28447         
28448         
28449           
28450
28451     },
28452
28453     // private
28454     initValue : Roo.emptyFn,
28455
28456     /**
28457      * Returns the checked state of the checkbox.
28458      * @return {Boolean} True if checked, else false
28459      */
28460     getValue : function(){
28461         return this.el.dom.value;
28462         
28463     },
28464
28465         // private
28466     onClick : function(e){ 
28467         //this.setChecked(!this.checked);
28468         Roo.get(e.target).toggleClass('x-menu-item-checked');
28469         this.refreshValue();
28470         //if(this.el.dom.checked != this.checked){
28471         //    this.setValue(this.el.dom.checked);
28472        // }
28473     },
28474     
28475     // private
28476     refreshValue : function()
28477     {
28478         var val = '';
28479         this.viewEl.select('img',true).each(function(e,i,n)  {
28480             val += e.is(".x-menu-item-checked") ? String(n) : '';
28481         });
28482         this.setValue(val, true);
28483     },
28484
28485     /**
28486      * Sets the checked state of the checkbox.
28487      * On is always based on a string comparison between inputValue and the param.
28488      * @param {Boolean/String} value - the value to set 
28489      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
28490      */
28491     setValue : function(v,suppressEvent){
28492         if (!this.el.dom) {
28493             return;
28494         }
28495         var old = this.el.dom.value ;
28496         this.el.dom.value = v;
28497         if (suppressEvent) {
28498             return ;
28499         }
28500          
28501         // update display..
28502         this.viewEl.select('img',true).each(function(e,i,n)  {
28503             
28504             var on = e.is(".x-menu-item-checked");
28505             var newv = v.indexOf(String(n)) > -1;
28506             if (on != newv) {
28507                 e.toggleClass('x-menu-item-checked');
28508             }
28509             
28510         });
28511         
28512         
28513         this.fireEvent('change', this, v, old);
28514         
28515         
28516     },
28517    
28518     // handle setting of hidden value by some other method!!?!?
28519     setFromHidden: function()
28520     {
28521         if(!this.el){
28522             return;
28523         }
28524         //console.log("SET FROM HIDDEN");
28525         //alert('setFrom hidden');
28526         this.setValue(this.el.dom.value);
28527     },
28528     
28529     onDestroy : function()
28530     {
28531         if(this.viewEl){
28532             Roo.get(this.viewEl).remove();
28533         }
28534          
28535         Roo.form.DayPicker.superclass.onDestroy.call(this);
28536     }
28537
28538 });//<script type="text/javasscript">
28539  
28540
28541 /**
28542  * @class Roo.DDView
28543  * A DnD enabled version of Roo.View.
28544  * @param {Element/String} container The Element in which to create the View.
28545  * @param {String} tpl The template string used to create the markup for each element of the View
28546  * @param {Object} config The configuration properties. These include all the config options of
28547  * {@link Roo.View} plus some specific to this class.<br>
28548  * <p>
28549  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28550  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28551  * <p>
28552  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28553 .x-view-drag-insert-above {
28554         border-top:1px dotted #3366cc;
28555 }
28556 .x-view-drag-insert-below {
28557         border-bottom:1px dotted #3366cc;
28558 }
28559 </code></pre>
28560  * 
28561  */
28562  
28563 Roo.DDView = function(container, tpl, config) {
28564     Roo.DDView.superclass.constructor.apply(this, arguments);
28565     this.getEl().setStyle("outline", "0px none");
28566     this.getEl().unselectable();
28567     if (this.dragGroup) {
28568                 this.setDraggable(this.dragGroup.split(","));
28569     }
28570     if (this.dropGroup) {
28571                 this.setDroppable(this.dropGroup.split(","));
28572     }
28573     if (this.deletable) {
28574         this.setDeletable();
28575     }
28576     this.isDirtyFlag = false;
28577         this.addEvents({
28578                 "drop" : true
28579         });
28580 };
28581
28582 Roo.extend(Roo.DDView, Roo.View, {
28583 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28584 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28585 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28586 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28587
28588         isFormField: true,
28589
28590         reset: Roo.emptyFn,
28591         
28592         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28593
28594         validate: function() {
28595                 return true;
28596         },
28597         
28598         destroy: function() {
28599                 this.purgeListeners();
28600                 this.getEl.removeAllListeners();
28601                 this.getEl().remove();
28602                 if (this.dragZone) {
28603                         if (this.dragZone.destroy) {
28604                                 this.dragZone.destroy();
28605                         }
28606                 }
28607                 if (this.dropZone) {
28608                         if (this.dropZone.destroy) {
28609                                 this.dropZone.destroy();
28610                         }
28611                 }
28612         },
28613
28614 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28615         getName: function() {
28616                 return this.name;
28617         },
28618
28619 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28620         setValue: function(v) {
28621                 if (!this.store) {
28622                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28623                 }
28624                 var data = {};
28625                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28626                 this.store.proxy = new Roo.data.MemoryProxy(data);
28627                 this.store.load();
28628         },
28629
28630 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28631         getValue: function() {
28632                 var result = '(';
28633                 this.store.each(function(rec) {
28634                         result += rec.id + ',';
28635                 });
28636                 return result.substr(0, result.length - 1) + ')';
28637         },
28638         
28639         getIds: function() {
28640                 var i = 0, result = new Array(this.store.getCount());
28641                 this.store.each(function(rec) {
28642                         result[i++] = rec.id;
28643                 });
28644                 return result;
28645         },
28646         
28647         isDirty: function() {
28648                 return this.isDirtyFlag;
28649         },
28650
28651 /**
28652  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28653  *      whole Element becomes the target, and this causes the drop gesture to append.
28654  */
28655     getTargetFromEvent : function(e) {
28656                 var target = e.getTarget();
28657                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28658                 target = target.parentNode;
28659                 }
28660                 if (!target) {
28661                         target = this.el.dom.lastChild || this.el.dom;
28662                 }
28663                 return target;
28664     },
28665
28666 /**
28667  *      Create the drag data which consists of an object which has the property "ddel" as
28668  *      the drag proxy element. 
28669  */
28670     getDragData : function(e) {
28671         var target = this.findItemFromChild(e.getTarget());
28672                 if(target) {
28673                         this.handleSelection(e);
28674                         var selNodes = this.getSelectedNodes();
28675             var dragData = {
28676                 source: this,
28677                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28678                 nodes: selNodes,
28679                 records: []
28680                         };
28681                         var selectedIndices = this.getSelectedIndexes();
28682                         for (var i = 0; i < selectedIndices.length; i++) {
28683                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28684                         }
28685                         if (selNodes.length == 1) {
28686                                 dragData.ddel = target.cloneNode(true); // the div element
28687                         } else {
28688                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28689                                 div.className = 'multi-proxy';
28690                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28691                                         div.appendChild(selNodes[i].cloneNode(true));
28692                                 }
28693                                 dragData.ddel = div;
28694                         }
28695             //console.log(dragData)
28696             //console.log(dragData.ddel.innerHTML)
28697                         return dragData;
28698                 }
28699         //console.log('nodragData')
28700                 return false;
28701     },
28702     
28703 /**     Specify to which ddGroup items in this DDView may be dragged. */
28704     setDraggable: function(ddGroup) {
28705         if (ddGroup instanceof Array) {
28706                 Roo.each(ddGroup, this.setDraggable, this);
28707                 return;
28708         }
28709         if (this.dragZone) {
28710                 this.dragZone.addToGroup(ddGroup);
28711         } else {
28712                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28713                                 containerScroll: true,
28714                                 ddGroup: ddGroup 
28715
28716                         });
28717 //                      Draggability implies selection. DragZone's mousedown selects the element.
28718                         if (!this.multiSelect) { this.singleSelect = true; }
28719
28720 //                      Wire the DragZone's handlers up to methods in *this*
28721                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28722                 }
28723     },
28724
28725 /**     Specify from which ddGroup this DDView accepts drops. */
28726     setDroppable: function(ddGroup) {
28727         if (ddGroup instanceof Array) {
28728                 Roo.each(ddGroup, this.setDroppable, this);
28729                 return;
28730         }
28731         if (this.dropZone) {
28732                 this.dropZone.addToGroup(ddGroup);
28733         } else {
28734                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28735                                 containerScroll: true,
28736                                 ddGroup: ddGroup
28737                         });
28738
28739 //                      Wire the DropZone's handlers up to methods in *this*
28740                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28741                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28742                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28743                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28744                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28745                 }
28746     },
28747
28748 /**     Decide whether to drop above or below a View node. */
28749     getDropPoint : function(e, n, dd){
28750         if (n == this.el.dom) { return "above"; }
28751                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28752                 var c = t + (b - t) / 2;
28753                 var y = Roo.lib.Event.getPageY(e);
28754                 if(y <= c) {
28755                         return "above";
28756                 }else{
28757                         return "below";
28758                 }
28759     },
28760
28761     onNodeEnter : function(n, dd, e, data){
28762                 return false;
28763     },
28764     
28765     onNodeOver : function(n, dd, e, data){
28766                 var pt = this.getDropPoint(e, n, dd);
28767                 // set the insert point style on the target node
28768                 var dragElClass = this.dropNotAllowed;
28769                 if (pt) {
28770                         var targetElClass;
28771                         if (pt == "above"){
28772                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28773                                 targetElClass = "x-view-drag-insert-above";
28774                         } else {
28775                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28776                                 targetElClass = "x-view-drag-insert-below";
28777                         }
28778                         if (this.lastInsertClass != targetElClass){
28779                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28780                                 this.lastInsertClass = targetElClass;
28781                         }
28782                 }
28783                 return dragElClass;
28784         },
28785
28786     onNodeOut : function(n, dd, e, data){
28787                 this.removeDropIndicators(n);
28788     },
28789
28790     onNodeDrop : function(n, dd, e, data){
28791         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28792                 return false;
28793         }
28794         var pt = this.getDropPoint(e, n, dd);
28795                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28796                 if (pt == "below") { insertAt++; }
28797                 for (var i = 0; i < data.records.length; i++) {
28798                         var r = data.records[i];
28799                         var dup = this.store.getById(r.id);
28800                         if (dup && (dd != this.dragZone)) {
28801                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28802                         } else {
28803                                 if (data.copy) {
28804                                         this.store.insert(insertAt++, r.copy());
28805                                 } else {
28806                                         data.source.isDirtyFlag = true;
28807                                         r.store.remove(r);
28808                                         this.store.insert(insertAt++, r);
28809                                 }
28810                                 this.isDirtyFlag = true;
28811                         }
28812                 }
28813                 this.dragZone.cachedTarget = null;
28814                 return true;
28815     },
28816
28817     removeDropIndicators : function(n){
28818                 if(n){
28819                         Roo.fly(n).removeClass([
28820                                 "x-view-drag-insert-above",
28821                                 "x-view-drag-insert-below"]);
28822                         this.lastInsertClass = "_noclass";
28823                 }
28824     },
28825
28826 /**
28827  *      Utility method. Add a delete option to the DDView's context menu.
28828  *      @param {String} imageUrl The URL of the "delete" icon image.
28829  */
28830         setDeletable: function(imageUrl) {
28831                 if (!this.singleSelect && !this.multiSelect) {
28832                         this.singleSelect = true;
28833                 }
28834                 var c = this.getContextMenu();
28835                 this.contextMenu.on("itemclick", function(item) {
28836                         switch (item.id) {
28837                                 case "delete":
28838                                         this.remove(this.getSelectedIndexes());
28839                                         break;
28840                         }
28841                 }, this);
28842                 this.contextMenu.add({
28843                         icon: imageUrl,
28844                         id: "delete",
28845                         text: 'Delete'
28846                 });
28847         },
28848         
28849 /**     Return the context menu for this DDView. */
28850         getContextMenu: function() {
28851                 if (!this.contextMenu) {
28852 //                      Create the View's context menu
28853                         this.contextMenu = new Roo.menu.Menu({
28854                                 id: this.id + "-contextmenu"
28855                         });
28856                         this.el.on("contextmenu", this.showContextMenu, this);
28857                 }
28858                 return this.contextMenu;
28859         },
28860         
28861         disableContextMenu: function() {
28862                 if (this.contextMenu) {
28863                         this.el.un("contextmenu", this.showContextMenu, this);
28864                 }
28865         },
28866
28867         showContextMenu: function(e, item) {
28868         item = this.findItemFromChild(e.getTarget());
28869                 if (item) {
28870                         e.stopEvent();
28871                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28872                         this.contextMenu.showAt(e.getXY());
28873             }
28874     },
28875
28876 /**
28877  *      Remove {@link Roo.data.Record}s at the specified indices.
28878  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28879  */
28880     remove: function(selectedIndices) {
28881                 selectedIndices = [].concat(selectedIndices);
28882                 for (var i = 0; i < selectedIndices.length; i++) {
28883                         var rec = this.store.getAt(selectedIndices[i]);
28884                         this.store.remove(rec);
28885                 }
28886     },
28887
28888 /**
28889  *      Double click fires the event, but also, if this is draggable, and there is only one other
28890  *      related DropZone, it transfers the selected node.
28891  */
28892     onDblClick : function(e){
28893         var item = this.findItemFromChild(e.getTarget());
28894         if(item){
28895             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28896                 return false;
28897             }
28898             if (this.dragGroup) {
28899                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28900                     while (targets.indexOf(this.dropZone) > -1) {
28901                             targets.remove(this.dropZone);
28902                                 }
28903                     if (targets.length == 1) {
28904                                         this.dragZone.cachedTarget = null;
28905                         var el = Roo.get(targets[0].getEl());
28906                         var box = el.getBox(true);
28907                         targets[0].onNodeDrop(el.dom, {
28908                                 target: el.dom,
28909                                 xy: [box.x, box.y + box.height - 1]
28910                         }, null, this.getDragData(e));
28911                     }
28912                 }
28913         }
28914     },
28915     
28916     handleSelection: function(e) {
28917                 this.dragZone.cachedTarget = null;
28918         var item = this.findItemFromChild(e.getTarget());
28919         if (!item) {
28920                 this.clearSelections(true);
28921                 return;
28922         }
28923                 if (item && (this.multiSelect || this.singleSelect)){
28924                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28925                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28926                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28927                                 this.unselect(item);
28928                         } else {
28929                                 this.select(item, this.multiSelect && e.ctrlKey);
28930                                 this.lastSelection = item;
28931                         }
28932                 }
28933     },
28934
28935     onItemClick : function(item, index, e){
28936                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28937                         return false;
28938                 }
28939                 return true;
28940     },
28941
28942     unselect : function(nodeInfo, suppressEvent){
28943                 var node = this.getNode(nodeInfo);
28944                 if(node && this.isSelected(node)){
28945                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28946                                 Roo.fly(node).removeClass(this.selectedClass);
28947                                 this.selections.remove(node);
28948                                 if(!suppressEvent){
28949                                         this.fireEvent("selectionchange", this, this.selections);
28950                                 }
28951                         }
28952                 }
28953     }
28954 });
28955 /*
28956  * Based on:
28957  * Ext JS Library 1.1.1
28958  * Copyright(c) 2006-2007, Ext JS, LLC.
28959  *
28960  * Originally Released Under LGPL - original licence link has changed is not relivant.
28961  *
28962  * Fork - LGPL
28963  * <script type="text/javascript">
28964  */
28965  
28966 /**
28967  * @class Roo.LayoutManager
28968  * @extends Roo.util.Observable
28969  * Base class for layout managers.
28970  */
28971 Roo.LayoutManager = function(container, config){
28972     Roo.LayoutManager.superclass.constructor.call(this);
28973     this.el = Roo.get(container);
28974     // ie scrollbar fix
28975     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28976         document.body.scroll = "no";
28977     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28978         this.el.position('relative');
28979     }
28980     this.id = this.el.id;
28981     this.el.addClass("x-layout-container");
28982     /** false to disable window resize monitoring @type Boolean */
28983     this.monitorWindowResize = true;
28984     this.regions = {};
28985     this.addEvents({
28986         /**
28987          * @event layout
28988          * Fires when a layout is performed. 
28989          * @param {Roo.LayoutManager} this
28990          */
28991         "layout" : true,
28992         /**
28993          * @event regionresized
28994          * Fires when the user resizes a region. 
28995          * @param {Roo.LayoutRegion} region The resized region
28996          * @param {Number} newSize The new size (width for east/west, height for north/south)
28997          */
28998         "regionresized" : true,
28999         /**
29000          * @event regioncollapsed
29001          * Fires when a region is collapsed. 
29002          * @param {Roo.LayoutRegion} region The collapsed region
29003          */
29004         "regioncollapsed" : true,
29005         /**
29006          * @event regionexpanded
29007          * Fires when a region is expanded.  
29008          * @param {Roo.LayoutRegion} region The expanded region
29009          */
29010         "regionexpanded" : true
29011     });
29012     this.updating = false;
29013     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29014 };
29015
29016 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29017     /**
29018      * Returns true if this layout is currently being updated
29019      * @return {Boolean}
29020      */
29021     isUpdating : function(){
29022         return this.updating; 
29023     },
29024     
29025     /**
29026      * Suspend the LayoutManager from doing auto-layouts while
29027      * making multiple add or remove calls
29028      */
29029     beginUpdate : function(){
29030         this.updating = true;    
29031     },
29032     
29033     /**
29034      * Restore auto-layouts and optionally disable the manager from performing a layout
29035      * @param {Boolean} noLayout true to disable a layout update 
29036      */
29037     endUpdate : function(noLayout){
29038         this.updating = false;
29039         if(!noLayout){
29040             this.layout();
29041         }    
29042     },
29043     
29044     layout: function(){
29045         
29046     },
29047     
29048     onRegionResized : function(region, newSize){
29049         this.fireEvent("regionresized", region, newSize);
29050         this.layout();
29051     },
29052     
29053     onRegionCollapsed : function(region){
29054         this.fireEvent("regioncollapsed", region);
29055     },
29056     
29057     onRegionExpanded : function(region){
29058         this.fireEvent("regionexpanded", region);
29059     },
29060         
29061     /**
29062      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29063      * performs box-model adjustments.
29064      * @return {Object} The size as an object {width: (the width), height: (the height)}
29065      */
29066     getViewSize : function(){
29067         var size;
29068         if(this.el.dom != document.body){
29069             size = this.el.getSize();
29070         }else{
29071             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29072         }
29073         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29074         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29075         return size;
29076     },
29077     
29078     /**
29079      * Returns the Element this layout is bound to.
29080      * @return {Roo.Element}
29081      */
29082     getEl : function(){
29083         return this.el;
29084     },
29085     
29086     /**
29087      * Returns the specified region.
29088      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29089      * @return {Roo.LayoutRegion}
29090      */
29091     getRegion : function(target){
29092         return this.regions[target.toLowerCase()];
29093     },
29094     
29095     onWindowResize : function(){
29096         if(this.monitorWindowResize){
29097             this.layout();
29098         }
29099     }
29100 });/*
29101  * Based on:
29102  * Ext JS Library 1.1.1
29103  * Copyright(c) 2006-2007, Ext JS, LLC.
29104  *
29105  * Originally Released Under LGPL - original licence link has changed is not relivant.
29106  *
29107  * Fork - LGPL
29108  * <script type="text/javascript">
29109  */
29110 /**
29111  * @class Roo.BorderLayout
29112  * @extends Roo.LayoutManager
29113  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29114  * please see: <br><br>
29115  * <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>
29116  * <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>
29117  * Example:
29118  <pre><code>
29119  var layout = new Roo.BorderLayout(document.body, {
29120     north: {
29121         initialSize: 25,
29122         titlebar: false
29123     },
29124     west: {
29125         split:true,
29126         initialSize: 200,
29127         minSize: 175,
29128         maxSize: 400,
29129         titlebar: true,
29130         collapsible: true
29131     },
29132     east: {
29133         split:true,
29134         initialSize: 202,
29135         minSize: 175,
29136         maxSize: 400,
29137         titlebar: true,
29138         collapsible: true
29139     },
29140     south: {
29141         split:true,
29142         initialSize: 100,
29143         minSize: 100,
29144         maxSize: 200,
29145         titlebar: true,
29146         collapsible: true
29147     },
29148     center: {
29149         titlebar: true,
29150         autoScroll:true,
29151         resizeTabs: true,
29152         minTabWidth: 50,
29153         preferredTabWidth: 150
29154     }
29155 });
29156
29157 // shorthand
29158 var CP = Roo.ContentPanel;
29159
29160 layout.beginUpdate();
29161 layout.add("north", new CP("north", "North"));
29162 layout.add("south", new CP("south", {title: "South", closable: true}));
29163 layout.add("west", new CP("west", {title: "West"}));
29164 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29165 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29166 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29167 layout.getRegion("center").showPanel("center1");
29168 layout.endUpdate();
29169 </code></pre>
29170
29171 <b>The container the layout is rendered into can be either the body element or any other element.
29172 If it is not the body element, the container needs to either be an absolute positioned element,
29173 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29174 the container size if it is not the body element.</b>
29175
29176 * @constructor
29177 * Create a new BorderLayout
29178 * @param {String/HTMLElement/Element} container The container this layout is bound to
29179 * @param {Object} config Configuration options
29180  */
29181 Roo.BorderLayout = function(container, config){
29182     config = config || {};
29183     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29184     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29185     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29186         var target = this.factory.validRegions[i];
29187         if(config[target]){
29188             this.addRegion(target, config[target]);
29189         }
29190     }
29191 };
29192
29193 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29194     /**
29195      * Creates and adds a new region if it doesn't already exist.
29196      * @param {String} target The target region key (north, south, east, west or center).
29197      * @param {Object} config The regions config object
29198      * @return {BorderLayoutRegion} The new region
29199      */
29200     addRegion : function(target, config){
29201         if(!this.regions[target]){
29202             var r = this.factory.create(target, this, config);
29203             this.bindRegion(target, r);
29204         }
29205         return this.regions[target];
29206     },
29207
29208     // private (kinda)
29209     bindRegion : function(name, r){
29210         this.regions[name] = r;
29211         r.on("visibilitychange", this.layout, this);
29212         r.on("paneladded", this.layout, this);
29213         r.on("panelremoved", this.layout, this);
29214         r.on("invalidated", this.layout, this);
29215         r.on("resized", this.onRegionResized, this);
29216         r.on("collapsed", this.onRegionCollapsed, this);
29217         r.on("expanded", this.onRegionExpanded, this);
29218     },
29219
29220     /**
29221      * Performs a layout update.
29222      */
29223     layout : function(){
29224         if(this.updating) return;
29225         var size = this.getViewSize();
29226         var w = size.width;
29227         var h = size.height;
29228         var centerW = w;
29229         var centerH = h;
29230         var centerY = 0;
29231         var centerX = 0;
29232         //var x = 0, y = 0;
29233
29234         var rs = this.regions;
29235         var north = rs["north"];
29236         var south = rs["south"]; 
29237         var west = rs["west"];
29238         var east = rs["east"];
29239         var center = rs["center"];
29240         //if(this.hideOnLayout){ // not supported anymore
29241             //c.el.setStyle("display", "none");
29242         //}
29243         if(north && north.isVisible()){
29244             var b = north.getBox();
29245             var m = north.getMargins();
29246             b.width = w - (m.left+m.right);
29247             b.x = m.left;
29248             b.y = m.top;
29249             centerY = b.height + b.y + m.bottom;
29250             centerH -= centerY;
29251             north.updateBox(this.safeBox(b));
29252         }
29253         if(south && south.isVisible()){
29254             var b = south.getBox();
29255             var m = south.getMargins();
29256             b.width = w - (m.left+m.right);
29257             b.x = m.left;
29258             var totalHeight = (b.height + m.top + m.bottom);
29259             b.y = h - totalHeight + m.top;
29260             centerH -= totalHeight;
29261             south.updateBox(this.safeBox(b));
29262         }
29263         if(west && west.isVisible()){
29264             var b = west.getBox();
29265             var m = west.getMargins();
29266             b.height = centerH - (m.top+m.bottom);
29267             b.x = m.left;
29268             b.y = centerY + m.top;
29269             var totalWidth = (b.width + m.left + m.right);
29270             centerX += totalWidth;
29271             centerW -= totalWidth;
29272             west.updateBox(this.safeBox(b));
29273         }
29274         if(east && east.isVisible()){
29275             var b = east.getBox();
29276             var m = east.getMargins();
29277             b.height = centerH - (m.top+m.bottom);
29278             var totalWidth = (b.width + m.left + m.right);
29279             b.x = w - totalWidth + m.left;
29280             b.y = centerY + m.top;
29281             centerW -= totalWidth;
29282             east.updateBox(this.safeBox(b));
29283         }
29284         if(center){
29285             var m = center.getMargins();
29286             var centerBox = {
29287                 x: centerX + m.left,
29288                 y: centerY + m.top,
29289                 width: centerW - (m.left+m.right),
29290                 height: centerH - (m.top+m.bottom)
29291             };
29292             //if(this.hideOnLayout){
29293                 //center.el.setStyle("display", "block");
29294             //}
29295             center.updateBox(this.safeBox(centerBox));
29296         }
29297         this.el.repaint();
29298         this.fireEvent("layout", this);
29299     },
29300
29301     // private
29302     safeBox : function(box){
29303         box.width = Math.max(0, box.width);
29304         box.height = Math.max(0, box.height);
29305         return box;
29306     },
29307
29308     /**
29309      * Adds a ContentPanel (or subclass) to this layout.
29310      * @param {String} target The target region key (north, south, east, west or center).
29311      * @param {Roo.ContentPanel} panel The panel to add
29312      * @return {Roo.ContentPanel} The added panel
29313      */
29314     add : function(target, panel){
29315          
29316         target = target.toLowerCase();
29317         return this.regions[target].add(panel);
29318     },
29319
29320     /**
29321      * Remove a ContentPanel (or subclass) to this layout.
29322      * @param {String} target The target region key (north, south, east, west or center).
29323      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29324      * @return {Roo.ContentPanel} The removed panel
29325      */
29326     remove : function(target, panel){
29327         target = target.toLowerCase();
29328         return this.regions[target].remove(panel);
29329     },
29330
29331     /**
29332      * Searches all regions for a panel with the specified id
29333      * @param {String} panelId
29334      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29335      */
29336     findPanel : function(panelId){
29337         var rs = this.regions;
29338         for(var target in rs){
29339             if(typeof rs[target] != "function"){
29340                 var p = rs[target].getPanel(panelId);
29341                 if(p){
29342                     return p;
29343                 }
29344             }
29345         }
29346         return null;
29347     },
29348
29349     /**
29350      * Searches all regions for a panel with the specified id and activates (shows) it.
29351      * @param {String/ContentPanel} panelId The panels id or the panel itself
29352      * @return {Roo.ContentPanel} The shown panel or null
29353      */
29354     showPanel : function(panelId) {
29355       var rs = this.regions;
29356       for(var target in rs){
29357          var r = rs[target];
29358          if(typeof r != "function"){
29359             if(r.hasPanel(panelId)){
29360                return r.showPanel(panelId);
29361             }
29362          }
29363       }
29364       return null;
29365    },
29366
29367    /**
29368      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29369      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29370      */
29371     restoreState : function(provider){
29372         if(!provider){
29373             provider = Roo.state.Manager;
29374         }
29375         var sm = new Roo.LayoutStateManager();
29376         sm.init(this, provider);
29377     },
29378
29379     /**
29380      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29381      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29382      * a valid ContentPanel config object.  Example:
29383      * <pre><code>
29384 // Create the main layout
29385 var layout = new Roo.BorderLayout('main-ct', {
29386     west: {
29387         split:true,
29388         minSize: 175,
29389         titlebar: true
29390     },
29391     center: {
29392         title:'Components'
29393     }
29394 }, 'main-ct');
29395
29396 // Create and add multiple ContentPanels at once via configs
29397 layout.batchAdd({
29398    west: {
29399        id: 'source-files',
29400        autoCreate:true,
29401        title:'Ext Source Files',
29402        autoScroll:true,
29403        fitToFrame:true
29404    },
29405    center : {
29406        el: cview,
29407        autoScroll:true,
29408        fitToFrame:true,
29409        toolbar: tb,
29410        resizeEl:'cbody'
29411    }
29412 });
29413 </code></pre>
29414      * @param {Object} regions An object containing ContentPanel configs by region name
29415      */
29416     batchAdd : function(regions){
29417         this.beginUpdate();
29418         for(var rname in regions){
29419             var lr = this.regions[rname];
29420             if(lr){
29421                 this.addTypedPanels(lr, regions[rname]);
29422             }
29423         }
29424         this.endUpdate();
29425     },
29426
29427     // private
29428     addTypedPanels : function(lr, ps){
29429         if(typeof ps == 'string'){
29430             lr.add(new Roo.ContentPanel(ps));
29431         }
29432         else if(ps instanceof Array){
29433             for(var i =0, len = ps.length; i < len; i++){
29434                 this.addTypedPanels(lr, ps[i]);
29435             }
29436         }
29437         else if(!ps.events){ // raw config?
29438             var el = ps.el;
29439             delete ps.el; // prevent conflict
29440             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29441         }
29442         else {  // panel object assumed!
29443             lr.add(ps);
29444         }
29445     },
29446     /**
29447      * Adds a xtype elements to the layout.
29448      * <pre><code>
29449
29450 layout.addxtype({
29451        xtype : 'ContentPanel',
29452        region: 'west',
29453        items: [ .... ]
29454    }
29455 );
29456
29457 layout.addxtype({
29458         xtype : 'NestedLayoutPanel',
29459         region: 'west',
29460         layout: {
29461            center: { },
29462            west: { }   
29463         },
29464         items : [ ... list of content panels or nested layout panels.. ]
29465    }
29466 );
29467 </code></pre>
29468      * @param {Object} cfg Xtype definition of item to add.
29469      */
29470     addxtype : function(cfg)
29471     {
29472         // basically accepts a pannel...
29473         // can accept a layout region..!?!?
29474        // console.log('BorderLayout add ' + cfg.xtype)
29475         
29476         if (!cfg.xtype.match(/Panel$/)) {
29477             return false;
29478         }
29479         var ret = false;
29480         var region = cfg.region;
29481         delete cfg.region;
29482         
29483           
29484         var xitems = [];
29485         if (cfg.items) {
29486             xitems = cfg.items;
29487             delete cfg.items;
29488         }
29489         
29490         
29491         switch(cfg.xtype) 
29492         {
29493             case 'ContentPanel':  // ContentPanel (el, cfg)
29494             case 'ScrollPanel':  // ContentPanel (el, cfg)
29495                 if(cfg.autoCreate) {
29496                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29497                 } else {
29498                     var el = this.el.createChild();
29499                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29500                 }
29501                 
29502                 this.add(region, ret);
29503                 break;
29504             
29505             
29506             case 'TreePanel': // our new panel!
29507                 cfg.el = this.el.createChild();
29508                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29509                 this.add(region, ret);
29510                 break;
29511             
29512             case 'NestedLayoutPanel': 
29513                 // create a new Layout (which is  a Border Layout...
29514                 var el = this.el.createChild();
29515                 var clayout = cfg.layout;
29516                 delete cfg.layout;
29517                 clayout.items   = clayout.items  || [];
29518                 // replace this exitems with the clayout ones..
29519                 xitems = clayout.items;
29520                  
29521                 
29522                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29523                     cfg.background = false;
29524                 }
29525                 var layout = new Roo.BorderLayout(el, clayout);
29526                 
29527                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29528                 //console.log('adding nested layout panel '  + cfg.toSource());
29529                 this.add(region, ret);
29530                 
29531                 break;
29532                 
29533             case 'GridPanel': 
29534             
29535                 // needs grid and region
29536                 
29537                 //var el = this.getRegion(region).el.createChild();
29538                 var el = this.el.createChild();
29539                 // create the grid first...
29540                 
29541                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29542                 delete cfg.grid;
29543                 if (region == 'center' && this.active ) {
29544                     cfg.background = false;
29545                 }
29546                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29547                 
29548                 this.add(region, ret);
29549                 if (cfg.background) {
29550                     ret.on('activate', function(gp) {
29551                         if (!gp.grid.rendered) {
29552                             gp.grid.render();
29553                         }
29554                     });
29555                 } else {
29556                     grid.render();
29557                 }
29558                 break;
29559            
29560                
29561                 
29562                 
29563             default: 
29564                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29565                 return null;
29566              // GridPanel (grid, cfg)
29567             
29568         }
29569         this.beginUpdate();
29570         // add children..
29571         Roo.each(xitems, function(i)  {
29572             ret.addxtype(i);
29573         });
29574         this.endUpdate();
29575         return ret;
29576         
29577     }
29578 });
29579
29580 /**
29581  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29582  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29583  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29584  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29585  * <pre><code>
29586 // shorthand
29587 var CP = Roo.ContentPanel;
29588
29589 var layout = Roo.BorderLayout.create({
29590     north: {
29591         initialSize: 25,
29592         titlebar: false,
29593         panels: [new CP("north", "North")]
29594     },
29595     west: {
29596         split:true,
29597         initialSize: 200,
29598         minSize: 175,
29599         maxSize: 400,
29600         titlebar: true,
29601         collapsible: true,
29602         panels: [new CP("west", {title: "West"})]
29603     },
29604     east: {
29605         split:true,
29606         initialSize: 202,
29607         minSize: 175,
29608         maxSize: 400,
29609         titlebar: true,
29610         collapsible: true,
29611         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29612     },
29613     south: {
29614         split:true,
29615         initialSize: 100,
29616         minSize: 100,
29617         maxSize: 200,
29618         titlebar: true,
29619         collapsible: true,
29620         panels: [new CP("south", {title: "South", closable: true})]
29621     },
29622     center: {
29623         titlebar: true,
29624         autoScroll:true,
29625         resizeTabs: true,
29626         minTabWidth: 50,
29627         preferredTabWidth: 150,
29628         panels: [
29629             new CP("center1", {title: "Close Me", closable: true}),
29630             new CP("center2", {title: "Center Panel", closable: false})
29631         ]
29632     }
29633 }, document.body);
29634
29635 layout.getRegion("center").showPanel("center1");
29636 </code></pre>
29637  * @param config
29638  * @param targetEl
29639  */
29640 Roo.BorderLayout.create = function(config, targetEl){
29641     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29642     layout.beginUpdate();
29643     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29644     for(var j = 0, jlen = regions.length; j < jlen; j++){
29645         var lr = regions[j];
29646         if(layout.regions[lr] && config[lr].panels){
29647             var r = layout.regions[lr];
29648             var ps = config[lr].panels;
29649             layout.addTypedPanels(r, ps);
29650         }
29651     }
29652     layout.endUpdate();
29653     return layout;
29654 };
29655
29656 // private
29657 Roo.BorderLayout.RegionFactory = {
29658     // private
29659     validRegions : ["north","south","east","west","center"],
29660
29661     // private
29662     create : function(target, mgr, config){
29663         target = target.toLowerCase();
29664         if(config.lightweight || config.basic){
29665             return new Roo.BasicLayoutRegion(mgr, config, target);
29666         }
29667         switch(target){
29668             case "north":
29669                 return new Roo.NorthLayoutRegion(mgr, config);
29670             case "south":
29671                 return new Roo.SouthLayoutRegion(mgr, config);
29672             case "east":
29673                 return new Roo.EastLayoutRegion(mgr, config);
29674             case "west":
29675                 return new Roo.WestLayoutRegion(mgr, config);
29676             case "center":
29677                 return new Roo.CenterLayoutRegion(mgr, config);
29678         }
29679         throw 'Layout region "'+target+'" not supported.';
29680     }
29681 };/*
29682  * Based on:
29683  * Ext JS Library 1.1.1
29684  * Copyright(c) 2006-2007, Ext JS, LLC.
29685  *
29686  * Originally Released Under LGPL - original licence link has changed is not relivant.
29687  *
29688  * Fork - LGPL
29689  * <script type="text/javascript">
29690  */
29691  
29692 /**
29693  * @class Roo.BasicLayoutRegion
29694  * @extends Roo.util.Observable
29695  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29696  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29697  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29698  */
29699 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29700     this.mgr = mgr;
29701     this.position  = pos;
29702     this.events = {
29703         /**
29704          * @scope Roo.BasicLayoutRegion
29705          */
29706         
29707         /**
29708          * @event beforeremove
29709          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29710          * @param {Roo.LayoutRegion} this
29711          * @param {Roo.ContentPanel} panel The panel
29712          * @param {Object} e The cancel event object
29713          */
29714         "beforeremove" : true,
29715         /**
29716          * @event invalidated
29717          * Fires when the layout for this region is changed.
29718          * @param {Roo.LayoutRegion} this
29719          */
29720         "invalidated" : true,
29721         /**
29722          * @event visibilitychange
29723          * Fires when this region is shown or hidden 
29724          * @param {Roo.LayoutRegion} this
29725          * @param {Boolean} visibility true or false
29726          */
29727         "visibilitychange" : true,
29728         /**
29729          * @event paneladded
29730          * Fires when a panel is added. 
29731          * @param {Roo.LayoutRegion} this
29732          * @param {Roo.ContentPanel} panel The panel
29733          */
29734         "paneladded" : true,
29735         /**
29736          * @event panelremoved
29737          * Fires when a panel is removed. 
29738          * @param {Roo.LayoutRegion} this
29739          * @param {Roo.ContentPanel} panel The panel
29740          */
29741         "panelremoved" : true,
29742         /**
29743          * @event collapsed
29744          * Fires when this region is collapsed.
29745          * @param {Roo.LayoutRegion} this
29746          */
29747         "collapsed" : true,
29748         /**
29749          * @event expanded
29750          * Fires when this region is expanded.
29751          * @param {Roo.LayoutRegion} this
29752          */
29753         "expanded" : true,
29754         /**
29755          * @event slideshow
29756          * Fires when this region is slid into view.
29757          * @param {Roo.LayoutRegion} this
29758          */
29759         "slideshow" : true,
29760         /**
29761          * @event slidehide
29762          * Fires when this region slides out of view. 
29763          * @param {Roo.LayoutRegion} this
29764          */
29765         "slidehide" : true,
29766         /**
29767          * @event panelactivated
29768          * Fires when a panel is activated. 
29769          * @param {Roo.LayoutRegion} this
29770          * @param {Roo.ContentPanel} panel The activated panel
29771          */
29772         "panelactivated" : true,
29773         /**
29774          * @event resized
29775          * Fires when the user resizes this region. 
29776          * @param {Roo.LayoutRegion} this
29777          * @param {Number} newSize The new size (width for east/west, height for north/south)
29778          */
29779         "resized" : true
29780     };
29781     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29782     this.panels = new Roo.util.MixedCollection();
29783     this.panels.getKey = this.getPanelId.createDelegate(this);
29784     this.box = null;
29785     this.activePanel = null;
29786     // ensure listeners are added...
29787     
29788     if (config.listeners || config.events) {
29789         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29790             listeners : config.listeners || {},
29791             events : config.events || {}
29792         });
29793     }
29794     
29795     if(skipConfig !== true){
29796         this.applyConfig(config);
29797     }
29798 };
29799
29800 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29801     getPanelId : function(p){
29802         return p.getId();
29803     },
29804     
29805     applyConfig : function(config){
29806         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29807         this.config = config;
29808         
29809     },
29810     
29811     /**
29812      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29813      * the width, for horizontal (north, south) the height.
29814      * @param {Number} newSize The new width or height
29815      */
29816     resizeTo : function(newSize){
29817         var el = this.el ? this.el :
29818                  (this.activePanel ? this.activePanel.getEl() : null);
29819         if(el){
29820             switch(this.position){
29821                 case "east":
29822                 case "west":
29823                     el.setWidth(newSize);
29824                     this.fireEvent("resized", this, newSize);
29825                 break;
29826                 case "north":
29827                 case "south":
29828                     el.setHeight(newSize);
29829                     this.fireEvent("resized", this, newSize);
29830                 break;                
29831             }
29832         }
29833     },
29834     
29835     getBox : function(){
29836         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29837     },
29838     
29839     getMargins : function(){
29840         return this.margins;
29841     },
29842     
29843     updateBox : function(box){
29844         this.box = box;
29845         var el = this.activePanel.getEl();
29846         el.dom.style.left = box.x + "px";
29847         el.dom.style.top = box.y + "px";
29848         this.activePanel.setSize(box.width, box.height);
29849     },
29850     
29851     /**
29852      * Returns the container element for this region.
29853      * @return {Roo.Element}
29854      */
29855     getEl : function(){
29856         return this.activePanel;
29857     },
29858     
29859     /**
29860      * Returns true if this region is currently visible.
29861      * @return {Boolean}
29862      */
29863     isVisible : function(){
29864         return this.activePanel ? true : false;
29865     },
29866     
29867     setActivePanel : function(panel){
29868         panel = this.getPanel(panel);
29869         if(this.activePanel && this.activePanel != panel){
29870             this.activePanel.setActiveState(false);
29871             this.activePanel.getEl().setLeftTop(-10000,-10000);
29872         }
29873         this.activePanel = panel;
29874         panel.setActiveState(true);
29875         if(this.box){
29876             panel.setSize(this.box.width, this.box.height);
29877         }
29878         this.fireEvent("panelactivated", this, panel);
29879         this.fireEvent("invalidated");
29880     },
29881     
29882     /**
29883      * Show the specified panel.
29884      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29885      * @return {Roo.ContentPanel} The shown panel or null
29886      */
29887     showPanel : function(panel){
29888         if(panel = this.getPanel(panel)){
29889             this.setActivePanel(panel);
29890         }
29891         return panel;
29892     },
29893     
29894     /**
29895      * Get the active panel for this region.
29896      * @return {Roo.ContentPanel} The active panel or null
29897      */
29898     getActivePanel : function(){
29899         return this.activePanel;
29900     },
29901     
29902     /**
29903      * Add the passed ContentPanel(s)
29904      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29905      * @return {Roo.ContentPanel} The panel added (if only one was added)
29906      */
29907     add : function(panel){
29908         if(arguments.length > 1){
29909             for(var i = 0, len = arguments.length; i < len; i++) {
29910                 this.add(arguments[i]);
29911             }
29912             return null;
29913         }
29914         if(this.hasPanel(panel)){
29915             this.showPanel(panel);
29916             return panel;
29917         }
29918         var el = panel.getEl();
29919         if(el.dom.parentNode != this.mgr.el.dom){
29920             this.mgr.el.dom.appendChild(el.dom);
29921         }
29922         if(panel.setRegion){
29923             panel.setRegion(this);
29924         }
29925         this.panels.add(panel);
29926         el.setStyle("position", "absolute");
29927         if(!panel.background){
29928             this.setActivePanel(panel);
29929             if(this.config.initialSize && this.panels.getCount()==1){
29930                 this.resizeTo(this.config.initialSize);
29931             }
29932         }
29933         this.fireEvent("paneladded", this, panel);
29934         return panel;
29935     },
29936     
29937     /**
29938      * Returns true if the panel is in this region.
29939      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29940      * @return {Boolean}
29941      */
29942     hasPanel : function(panel){
29943         if(typeof panel == "object"){ // must be panel obj
29944             panel = panel.getId();
29945         }
29946         return this.getPanel(panel) ? true : false;
29947     },
29948     
29949     /**
29950      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29951      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29952      * @param {Boolean} preservePanel Overrides the config preservePanel option
29953      * @return {Roo.ContentPanel} The panel that was removed
29954      */
29955     remove : function(panel, preservePanel){
29956         panel = this.getPanel(panel);
29957         if(!panel){
29958             return null;
29959         }
29960         var e = {};
29961         this.fireEvent("beforeremove", this, panel, e);
29962         if(e.cancel === true){
29963             return null;
29964         }
29965         var panelId = panel.getId();
29966         this.panels.removeKey(panelId);
29967         return panel;
29968     },
29969     
29970     /**
29971      * Returns the panel specified or null if it's not in this region.
29972      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29973      * @return {Roo.ContentPanel}
29974      */
29975     getPanel : function(id){
29976         if(typeof id == "object"){ // must be panel obj
29977             return id;
29978         }
29979         return this.panels.get(id);
29980     },
29981     
29982     /**
29983      * Returns this regions position (north/south/east/west/center).
29984      * @return {String} 
29985      */
29986     getPosition: function(){
29987         return this.position;    
29988     }
29989 });/*
29990  * Based on:
29991  * Ext JS Library 1.1.1
29992  * Copyright(c) 2006-2007, Ext JS, LLC.
29993  *
29994  * Originally Released Under LGPL - original licence link has changed is not relivant.
29995  *
29996  * Fork - LGPL
29997  * <script type="text/javascript">
29998  */
29999  
30000 /**
30001  * @class Roo.LayoutRegion
30002  * @extends Roo.BasicLayoutRegion
30003  * This class represents a region in a layout manager.
30004  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30005  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30006  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30007  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30008  * @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})
30009  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
30010  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30011  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30012  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30013  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30014  * @cfg {String}    title           The title for the region (overrides panel titles)
30015  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30016  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30017  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30018  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30019  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30020  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30021  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30022  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30023  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30024  * @cfg {Boolean}   showPin         True to show a pin button
30025  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30026  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30027  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30028  * @cfg {Number}    width           For East/West panels
30029  * @cfg {Number}    height          For North/South panels
30030  * @cfg {Boolean}   split           To show the splitter
30031  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30032  */
30033 Roo.LayoutRegion = function(mgr, config, pos){
30034     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30035     var dh = Roo.DomHelper;
30036     /** This region's container element 
30037     * @type Roo.Element */
30038     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30039     /** This region's title element 
30040     * @type Roo.Element */
30041
30042     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30043         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30044         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30045     ]}, true);
30046     this.titleEl.enableDisplayMode();
30047     /** This region's title text element 
30048     * @type HTMLElement */
30049     this.titleTextEl = this.titleEl.dom.firstChild;
30050     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30051     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30052     this.closeBtn.enableDisplayMode();
30053     this.closeBtn.on("click", this.closeClicked, this);
30054     this.closeBtn.hide();
30055
30056     this.createBody(config);
30057     this.visible = true;
30058     this.collapsed = false;
30059
30060     if(config.hideWhenEmpty){
30061         this.hide();
30062         this.on("paneladded", this.validateVisibility, this);
30063         this.on("panelremoved", this.validateVisibility, this);
30064     }
30065     this.applyConfig(config);
30066 };
30067
30068 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30069
30070     createBody : function(){
30071         /** This region's body element 
30072         * @type Roo.Element */
30073         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30074     },
30075
30076     applyConfig : function(c){
30077         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30078             var dh = Roo.DomHelper;
30079             if(c.titlebar !== false){
30080                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30081                 this.collapseBtn.on("click", this.collapse, this);
30082                 this.collapseBtn.enableDisplayMode();
30083
30084                 if(c.showPin === true || this.showPin){
30085                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30086                     this.stickBtn.enableDisplayMode();
30087                     this.stickBtn.on("click", this.expand, this);
30088                     this.stickBtn.hide();
30089                 }
30090             }
30091             /** This region's collapsed element
30092             * @type Roo.Element */
30093             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30094                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30095             ]}, true);
30096             if(c.floatable !== false){
30097                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30098                this.collapsedEl.on("click", this.collapseClick, this);
30099             }
30100
30101             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30102                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30103                    id: "message", unselectable: "on", style:{"float":"left"}});
30104                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30105              }
30106             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30107             this.expandBtn.on("click", this.expand, this);
30108         }
30109         if(this.collapseBtn){
30110             this.collapseBtn.setVisible(c.collapsible == true);
30111         }
30112         this.cmargins = c.cmargins || this.cmargins ||
30113                          (this.position == "west" || this.position == "east" ?
30114                              {top: 0, left: 2, right:2, bottom: 0} :
30115                              {top: 2, left: 0, right:0, bottom: 2});
30116         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30117         this.bottomTabs = c.tabPosition != "top";
30118         this.autoScroll = c.autoScroll || false;
30119         if(this.autoScroll){
30120             this.bodyEl.setStyle("overflow", "auto");
30121         }else{
30122             this.bodyEl.setStyle("overflow", "hidden");
30123         }
30124         //if(c.titlebar !== false){
30125             if((!c.titlebar && !c.title) || c.titlebar === false){
30126                 this.titleEl.hide();
30127             }else{
30128                 this.titleEl.show();
30129                 if(c.title){
30130                     this.titleTextEl.innerHTML = c.title;
30131                 }
30132             }
30133         //}
30134         this.duration = c.duration || .30;
30135         this.slideDuration = c.slideDuration || .45;
30136         this.config = c;
30137         if(c.collapsed){
30138             this.collapse(true);
30139         }
30140         if(c.hidden){
30141             this.hide();
30142         }
30143     },
30144     /**
30145      * Returns true if this region is currently visible.
30146      * @return {Boolean}
30147      */
30148     isVisible : function(){
30149         return this.visible;
30150     },
30151
30152     /**
30153      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30154      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30155      */
30156     setCollapsedTitle : function(title){
30157         title = title || "&#160;";
30158         if(this.collapsedTitleTextEl){
30159             this.collapsedTitleTextEl.innerHTML = title;
30160         }
30161     },
30162
30163     getBox : function(){
30164         var b;
30165         if(!this.collapsed){
30166             b = this.el.getBox(false, true);
30167         }else{
30168             b = this.collapsedEl.getBox(false, true);
30169         }
30170         return b;
30171     },
30172
30173     getMargins : function(){
30174         return this.collapsed ? this.cmargins : this.margins;
30175     },
30176
30177     highlight : function(){
30178         this.el.addClass("x-layout-panel-dragover");
30179     },
30180
30181     unhighlight : function(){
30182         this.el.removeClass("x-layout-panel-dragover");
30183     },
30184
30185     updateBox : function(box){
30186         this.box = box;
30187         if(!this.collapsed){
30188             this.el.dom.style.left = box.x + "px";
30189             this.el.dom.style.top = box.y + "px";
30190             this.updateBody(box.width, box.height);
30191         }else{
30192             this.collapsedEl.dom.style.left = box.x + "px";
30193             this.collapsedEl.dom.style.top = box.y + "px";
30194             this.collapsedEl.setSize(box.width, box.height);
30195         }
30196         if(this.tabs){
30197             this.tabs.autoSizeTabs();
30198         }
30199     },
30200
30201     updateBody : function(w, h){
30202         if(w !== null){
30203             this.el.setWidth(w);
30204             w -= this.el.getBorderWidth("rl");
30205             if(this.config.adjustments){
30206                 w += this.config.adjustments[0];
30207             }
30208         }
30209         if(h !== null){
30210             this.el.setHeight(h);
30211             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30212             h -= this.el.getBorderWidth("tb");
30213             if(this.config.adjustments){
30214                 h += this.config.adjustments[1];
30215             }
30216             this.bodyEl.setHeight(h);
30217             if(this.tabs){
30218                 h = this.tabs.syncHeight(h);
30219             }
30220         }
30221         if(this.panelSize){
30222             w = w !== null ? w : this.panelSize.width;
30223             h = h !== null ? h : this.panelSize.height;
30224         }
30225         if(this.activePanel){
30226             var el = this.activePanel.getEl();
30227             w = w !== null ? w : el.getWidth();
30228             h = h !== null ? h : el.getHeight();
30229             this.panelSize = {width: w, height: h};
30230             this.activePanel.setSize(w, h);
30231         }
30232         if(Roo.isIE && this.tabs){
30233             this.tabs.el.repaint();
30234         }
30235     },
30236
30237     /**
30238      * Returns the container element for this region.
30239      * @return {Roo.Element}
30240      */
30241     getEl : function(){
30242         return this.el;
30243     },
30244
30245     /**
30246      * Hides this region.
30247      */
30248     hide : function(){
30249         if(!this.collapsed){
30250             this.el.dom.style.left = "-2000px";
30251             this.el.hide();
30252         }else{
30253             this.collapsedEl.dom.style.left = "-2000px";
30254             this.collapsedEl.hide();
30255         }
30256         this.visible = false;
30257         this.fireEvent("visibilitychange", this, false);
30258     },
30259
30260     /**
30261      * Shows this region if it was previously hidden.
30262      */
30263     show : function(){
30264         if(!this.collapsed){
30265             this.el.show();
30266         }else{
30267             this.collapsedEl.show();
30268         }
30269         this.visible = true;
30270         this.fireEvent("visibilitychange", this, true);
30271     },
30272
30273     closeClicked : function(){
30274         if(this.activePanel){
30275             this.remove(this.activePanel);
30276         }
30277     },
30278
30279     collapseClick : function(e){
30280         if(this.isSlid){
30281            e.stopPropagation();
30282            this.slideIn();
30283         }else{
30284            e.stopPropagation();
30285            this.slideOut();
30286         }
30287     },
30288
30289     /**
30290      * Collapses this region.
30291      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30292      */
30293     collapse : function(skipAnim){
30294         if(this.collapsed) return;
30295         this.collapsed = true;
30296         if(this.split){
30297             this.split.el.hide();
30298         }
30299         if(this.config.animate && skipAnim !== true){
30300             this.fireEvent("invalidated", this);
30301             this.animateCollapse();
30302         }else{
30303             this.el.setLocation(-20000,-20000);
30304             this.el.hide();
30305             this.collapsedEl.show();
30306             this.fireEvent("collapsed", this);
30307             this.fireEvent("invalidated", this);
30308         }
30309     },
30310
30311     animateCollapse : function(){
30312         // overridden
30313     },
30314
30315     /**
30316      * Expands this region if it was previously collapsed.
30317      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30318      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30319      */
30320     expand : function(e, skipAnim){
30321         if(e) e.stopPropagation();
30322         if(!this.collapsed || this.el.hasActiveFx()) return;
30323         if(this.isSlid){
30324             this.afterSlideIn();
30325             skipAnim = true;
30326         }
30327         this.collapsed = false;
30328         if(this.config.animate && skipAnim !== true){
30329             this.animateExpand();
30330         }else{
30331             this.el.show();
30332             if(this.split){
30333                 this.split.el.show();
30334             }
30335             this.collapsedEl.setLocation(-2000,-2000);
30336             this.collapsedEl.hide();
30337             this.fireEvent("invalidated", this);
30338             this.fireEvent("expanded", this);
30339         }
30340     },
30341
30342     animateExpand : function(){
30343         // overridden
30344     },
30345
30346     initTabs : function()
30347     {
30348         this.bodyEl.setStyle("overflow", "hidden");
30349         var ts = new Roo.TabPanel(
30350                 this.bodyEl.dom,
30351                 {
30352                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30353                     disableTooltips: this.config.disableTabTips,
30354                     toolbar : this.config.toolbar
30355                 }
30356         );
30357         if(this.config.hideTabs){
30358             ts.stripWrap.setDisplayed(false);
30359         }
30360         this.tabs = ts;
30361         ts.resizeTabs = this.config.resizeTabs === true;
30362         ts.minTabWidth = this.config.minTabWidth || 40;
30363         ts.maxTabWidth = this.config.maxTabWidth || 250;
30364         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30365         ts.monitorResize = false;
30366         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30367         ts.bodyEl.addClass('x-layout-tabs-body');
30368         this.panels.each(this.initPanelAsTab, this);
30369     },
30370
30371     initPanelAsTab : function(panel){
30372         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30373                     this.config.closeOnTab && panel.isClosable());
30374         if(panel.tabTip !== undefined){
30375             ti.setTooltip(panel.tabTip);
30376         }
30377         ti.on("activate", function(){
30378               this.setActivePanel(panel);
30379         }, this);
30380         if(this.config.closeOnTab){
30381             ti.on("beforeclose", function(t, e){
30382                 e.cancel = true;
30383                 this.remove(panel);
30384             }, this);
30385         }
30386         return ti;
30387     },
30388
30389     updatePanelTitle : function(panel, title){
30390         if(this.activePanel == panel){
30391             this.updateTitle(title);
30392         }
30393         if(this.tabs){
30394             var ti = this.tabs.getTab(panel.getEl().id);
30395             ti.setText(title);
30396             if(panel.tabTip !== undefined){
30397                 ti.setTooltip(panel.tabTip);
30398             }
30399         }
30400     },
30401
30402     updateTitle : function(title){
30403         if(this.titleTextEl && !this.config.title){
30404             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30405         }
30406     },
30407
30408     setActivePanel : function(panel){
30409         panel = this.getPanel(panel);
30410         if(this.activePanel && this.activePanel != panel){
30411             this.activePanel.setActiveState(false);
30412         }
30413         this.activePanel = panel;
30414         panel.setActiveState(true);
30415         if(this.panelSize){
30416             panel.setSize(this.panelSize.width, this.panelSize.height);
30417         }
30418         if(this.closeBtn){
30419             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30420         }
30421         this.updateTitle(panel.getTitle());
30422         if(this.tabs){
30423             this.fireEvent("invalidated", this);
30424         }
30425         this.fireEvent("panelactivated", this, panel);
30426     },
30427
30428     /**
30429      * Shows the specified panel.
30430      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30431      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30432      */
30433     showPanel : function(panel){
30434         if(panel = this.getPanel(panel)){
30435             if(this.tabs){
30436                 var tab = this.tabs.getTab(panel.getEl().id);
30437                 if(tab.isHidden()){
30438                     this.tabs.unhideTab(tab.id);
30439                 }
30440                 tab.activate();
30441             }else{
30442                 this.setActivePanel(panel);
30443             }
30444         }
30445         return panel;
30446     },
30447
30448     /**
30449      * Get the active panel for this region.
30450      * @return {Roo.ContentPanel} The active panel or null
30451      */
30452     getActivePanel : function(){
30453         return this.activePanel;
30454     },
30455
30456     validateVisibility : function(){
30457         if(this.panels.getCount() < 1){
30458             this.updateTitle("&#160;");
30459             this.closeBtn.hide();
30460             this.hide();
30461         }else{
30462             if(!this.isVisible()){
30463                 this.show();
30464             }
30465         }
30466     },
30467
30468     /**
30469      * Adds the passed ContentPanel(s) to this region.
30470      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30471      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30472      */
30473     add : function(panel){
30474         if(arguments.length > 1){
30475             for(var i = 0, len = arguments.length; i < len; i++) {
30476                 this.add(arguments[i]);
30477             }
30478             return null;
30479         }
30480         if(this.hasPanel(panel)){
30481             this.showPanel(panel);
30482             return panel;
30483         }
30484         panel.setRegion(this);
30485         this.panels.add(panel);
30486         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30487             this.bodyEl.dom.appendChild(panel.getEl().dom);
30488             if(panel.background !== true){
30489                 this.setActivePanel(panel);
30490             }
30491             this.fireEvent("paneladded", this, panel);
30492             return panel;
30493         }
30494         if(!this.tabs){
30495             this.initTabs();
30496         }else{
30497             this.initPanelAsTab(panel);
30498         }
30499         if(panel.background !== true){
30500             this.tabs.activate(panel.getEl().id);
30501         }
30502         this.fireEvent("paneladded", this, panel);
30503         return panel;
30504     },
30505
30506     /**
30507      * Hides the tab for the specified panel.
30508      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30509      */
30510     hidePanel : function(panel){
30511         if(this.tabs && (panel = this.getPanel(panel))){
30512             this.tabs.hideTab(panel.getEl().id);
30513         }
30514     },
30515
30516     /**
30517      * Unhides the tab for a previously hidden panel.
30518      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30519      */
30520     unhidePanel : function(panel){
30521         if(this.tabs && (panel = this.getPanel(panel))){
30522             this.tabs.unhideTab(panel.getEl().id);
30523         }
30524     },
30525
30526     clearPanels : function(){
30527         while(this.panels.getCount() > 0){
30528              this.remove(this.panels.first());
30529         }
30530     },
30531
30532     /**
30533      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30534      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30535      * @param {Boolean} preservePanel Overrides the config preservePanel option
30536      * @return {Roo.ContentPanel} The panel that was removed
30537      */
30538     remove : function(panel, preservePanel){
30539         panel = this.getPanel(panel);
30540         if(!panel){
30541             return null;
30542         }
30543         var e = {};
30544         this.fireEvent("beforeremove", this, panel, e);
30545         if(e.cancel === true){
30546             return null;
30547         }
30548         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30549         var panelId = panel.getId();
30550         this.panels.removeKey(panelId);
30551         if(preservePanel){
30552             document.body.appendChild(panel.getEl().dom);
30553         }
30554         if(this.tabs){
30555             this.tabs.removeTab(panel.getEl().id);
30556         }else if (!preservePanel){
30557             this.bodyEl.dom.removeChild(panel.getEl().dom);
30558         }
30559         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30560             var p = this.panels.first();
30561             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30562             tempEl.appendChild(p.getEl().dom);
30563             this.bodyEl.update("");
30564             this.bodyEl.dom.appendChild(p.getEl().dom);
30565             tempEl = null;
30566             this.updateTitle(p.getTitle());
30567             this.tabs = null;
30568             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30569             this.setActivePanel(p);
30570         }
30571         panel.setRegion(null);
30572         if(this.activePanel == panel){
30573             this.activePanel = null;
30574         }
30575         if(this.config.autoDestroy !== false && preservePanel !== true){
30576             try{panel.destroy();}catch(e){}
30577         }
30578         this.fireEvent("panelremoved", this, panel);
30579         return panel;
30580     },
30581
30582     /**
30583      * Returns the TabPanel component used by this region
30584      * @return {Roo.TabPanel}
30585      */
30586     getTabs : function(){
30587         return this.tabs;
30588     },
30589
30590     createTool : function(parentEl, className){
30591         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30592             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30593         btn.addClassOnOver("x-layout-tools-button-over");
30594         return btn;
30595     }
30596 });/*
30597  * Based on:
30598  * Ext JS Library 1.1.1
30599  * Copyright(c) 2006-2007, Ext JS, LLC.
30600  *
30601  * Originally Released Under LGPL - original licence link has changed is not relivant.
30602  *
30603  * Fork - LGPL
30604  * <script type="text/javascript">
30605  */
30606  
30607
30608
30609 /**
30610  * @class Roo.SplitLayoutRegion
30611  * @extends Roo.LayoutRegion
30612  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30613  */
30614 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30615     this.cursor = cursor;
30616     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30617 };
30618
30619 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30620     splitTip : "Drag to resize.",
30621     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30622     useSplitTips : false,
30623
30624     applyConfig : function(config){
30625         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30626         if(config.split){
30627             if(!this.split){
30628                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30629                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30630                 /** The SplitBar for this region 
30631                 * @type Roo.SplitBar */
30632                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30633                 this.split.on("moved", this.onSplitMove, this);
30634                 this.split.useShim = config.useShim === true;
30635                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30636                 if(this.useSplitTips){
30637                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30638                 }
30639                 if(config.collapsible){
30640                     this.split.el.on("dblclick", this.collapse,  this);
30641                 }
30642             }
30643             if(typeof config.minSize != "undefined"){
30644                 this.split.minSize = config.minSize;
30645             }
30646             if(typeof config.maxSize != "undefined"){
30647                 this.split.maxSize = config.maxSize;
30648             }
30649             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30650                 this.hideSplitter();
30651             }
30652         }
30653     },
30654
30655     getHMaxSize : function(){
30656          var cmax = this.config.maxSize || 10000;
30657          var center = this.mgr.getRegion("center");
30658          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30659     },
30660
30661     getVMaxSize : function(){
30662          var cmax = this.config.maxSize || 10000;
30663          var center = this.mgr.getRegion("center");
30664          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30665     },
30666
30667     onSplitMove : function(split, newSize){
30668         this.fireEvent("resized", this, newSize);
30669     },
30670     
30671     /** 
30672      * Returns the {@link Roo.SplitBar} for this region.
30673      * @return {Roo.SplitBar}
30674      */
30675     getSplitBar : function(){
30676         return this.split;
30677     },
30678     
30679     hide : function(){
30680         this.hideSplitter();
30681         Roo.SplitLayoutRegion.superclass.hide.call(this);
30682     },
30683
30684     hideSplitter : function(){
30685         if(this.split){
30686             this.split.el.setLocation(-2000,-2000);
30687             this.split.el.hide();
30688         }
30689     },
30690
30691     show : function(){
30692         if(this.split){
30693             this.split.el.show();
30694         }
30695         Roo.SplitLayoutRegion.superclass.show.call(this);
30696     },
30697     
30698     beforeSlide: function(){
30699         if(Roo.isGecko){// firefox overflow auto bug workaround
30700             this.bodyEl.clip();
30701             if(this.tabs) this.tabs.bodyEl.clip();
30702             if(this.activePanel){
30703                 this.activePanel.getEl().clip();
30704                 
30705                 if(this.activePanel.beforeSlide){
30706                     this.activePanel.beforeSlide();
30707                 }
30708             }
30709         }
30710     },
30711     
30712     afterSlide : function(){
30713         if(Roo.isGecko){// firefox overflow auto bug workaround
30714             this.bodyEl.unclip();
30715             if(this.tabs) this.tabs.bodyEl.unclip();
30716             if(this.activePanel){
30717                 this.activePanel.getEl().unclip();
30718                 if(this.activePanel.afterSlide){
30719                     this.activePanel.afterSlide();
30720                 }
30721             }
30722         }
30723     },
30724
30725     initAutoHide : function(){
30726         if(this.autoHide !== false){
30727             if(!this.autoHideHd){
30728                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30729                 this.autoHideHd = {
30730                     "mouseout": function(e){
30731                         if(!e.within(this.el, true)){
30732                             st.delay(500);
30733                         }
30734                     },
30735                     "mouseover" : function(e){
30736                         st.cancel();
30737                     },
30738                     scope : this
30739                 };
30740             }
30741             this.el.on(this.autoHideHd);
30742         }
30743     },
30744
30745     clearAutoHide : function(){
30746         if(this.autoHide !== false){
30747             this.el.un("mouseout", this.autoHideHd.mouseout);
30748             this.el.un("mouseover", this.autoHideHd.mouseover);
30749         }
30750     },
30751
30752     clearMonitor : function(){
30753         Roo.get(document).un("click", this.slideInIf, this);
30754     },
30755
30756     // these names are backwards but not changed for compat
30757     slideOut : function(){
30758         if(this.isSlid || this.el.hasActiveFx()){
30759             return;
30760         }
30761         this.isSlid = true;
30762         if(this.collapseBtn){
30763             this.collapseBtn.hide();
30764         }
30765         this.closeBtnState = this.closeBtn.getStyle('display');
30766         this.closeBtn.hide();
30767         if(this.stickBtn){
30768             this.stickBtn.show();
30769         }
30770         this.el.show();
30771         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30772         this.beforeSlide();
30773         this.el.setStyle("z-index", 10001);
30774         this.el.slideIn(this.getSlideAnchor(), {
30775             callback: function(){
30776                 this.afterSlide();
30777                 this.initAutoHide();
30778                 Roo.get(document).on("click", this.slideInIf, this);
30779                 this.fireEvent("slideshow", this);
30780             },
30781             scope: this,
30782             block: true
30783         });
30784     },
30785
30786     afterSlideIn : function(){
30787         this.clearAutoHide();
30788         this.isSlid = false;
30789         this.clearMonitor();
30790         this.el.setStyle("z-index", "");
30791         if(this.collapseBtn){
30792             this.collapseBtn.show();
30793         }
30794         this.closeBtn.setStyle('display', this.closeBtnState);
30795         if(this.stickBtn){
30796             this.stickBtn.hide();
30797         }
30798         this.fireEvent("slidehide", this);
30799     },
30800
30801     slideIn : function(cb){
30802         if(!this.isSlid || this.el.hasActiveFx()){
30803             Roo.callback(cb);
30804             return;
30805         }
30806         this.isSlid = false;
30807         this.beforeSlide();
30808         this.el.slideOut(this.getSlideAnchor(), {
30809             callback: function(){
30810                 this.el.setLeftTop(-10000, -10000);
30811                 this.afterSlide();
30812                 this.afterSlideIn();
30813                 Roo.callback(cb);
30814             },
30815             scope: this,
30816             block: true
30817         });
30818     },
30819     
30820     slideInIf : function(e){
30821         if(!e.within(this.el)){
30822             this.slideIn();
30823         }
30824     },
30825
30826     animateCollapse : function(){
30827         this.beforeSlide();
30828         this.el.setStyle("z-index", 20000);
30829         var anchor = this.getSlideAnchor();
30830         this.el.slideOut(anchor, {
30831             callback : function(){
30832                 this.el.setStyle("z-index", "");
30833                 this.collapsedEl.slideIn(anchor, {duration:.3});
30834                 this.afterSlide();
30835                 this.el.setLocation(-10000,-10000);
30836                 this.el.hide();
30837                 this.fireEvent("collapsed", this);
30838             },
30839             scope: this,
30840             block: true
30841         });
30842     },
30843
30844     animateExpand : function(){
30845         this.beforeSlide();
30846         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30847         this.el.setStyle("z-index", 20000);
30848         this.collapsedEl.hide({
30849             duration:.1
30850         });
30851         this.el.slideIn(this.getSlideAnchor(), {
30852             callback : function(){
30853                 this.el.setStyle("z-index", "");
30854                 this.afterSlide();
30855                 if(this.split){
30856                     this.split.el.show();
30857                 }
30858                 this.fireEvent("invalidated", this);
30859                 this.fireEvent("expanded", this);
30860             },
30861             scope: this,
30862             block: true
30863         });
30864     },
30865
30866     anchors : {
30867         "west" : "left",
30868         "east" : "right",
30869         "north" : "top",
30870         "south" : "bottom"
30871     },
30872
30873     sanchors : {
30874         "west" : "l",
30875         "east" : "r",
30876         "north" : "t",
30877         "south" : "b"
30878     },
30879
30880     canchors : {
30881         "west" : "tl-tr",
30882         "east" : "tr-tl",
30883         "north" : "tl-bl",
30884         "south" : "bl-tl"
30885     },
30886
30887     getAnchor : function(){
30888         return this.anchors[this.position];
30889     },
30890
30891     getCollapseAnchor : function(){
30892         return this.canchors[this.position];
30893     },
30894
30895     getSlideAnchor : function(){
30896         return this.sanchors[this.position];
30897     },
30898
30899     getAlignAdj : function(){
30900         var cm = this.cmargins;
30901         switch(this.position){
30902             case "west":
30903                 return [0, 0];
30904             break;
30905             case "east":
30906                 return [0, 0];
30907             break;
30908             case "north":
30909                 return [0, 0];
30910             break;
30911             case "south":
30912                 return [0, 0];
30913             break;
30914         }
30915     },
30916
30917     getExpandAdj : function(){
30918         var c = this.collapsedEl, cm = this.cmargins;
30919         switch(this.position){
30920             case "west":
30921                 return [-(cm.right+c.getWidth()+cm.left), 0];
30922             break;
30923             case "east":
30924                 return [cm.right+c.getWidth()+cm.left, 0];
30925             break;
30926             case "north":
30927                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30928             break;
30929             case "south":
30930                 return [0, cm.top+cm.bottom+c.getHeight()];
30931             break;
30932         }
30933     }
30934 });/*
30935  * Based on:
30936  * Ext JS Library 1.1.1
30937  * Copyright(c) 2006-2007, Ext JS, LLC.
30938  *
30939  * Originally Released Under LGPL - original licence link has changed is not relivant.
30940  *
30941  * Fork - LGPL
30942  * <script type="text/javascript">
30943  */
30944 /*
30945  * These classes are private internal classes
30946  */
30947 Roo.CenterLayoutRegion = function(mgr, config){
30948     Roo.LayoutRegion.call(this, mgr, config, "center");
30949     this.visible = true;
30950     this.minWidth = config.minWidth || 20;
30951     this.minHeight = config.minHeight || 20;
30952 };
30953
30954 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30955     hide : function(){
30956         // center panel can't be hidden
30957     },
30958     
30959     show : function(){
30960         // center panel can't be hidden
30961     },
30962     
30963     getMinWidth: function(){
30964         return this.minWidth;
30965     },
30966     
30967     getMinHeight: function(){
30968         return this.minHeight;
30969     }
30970 });
30971
30972
30973 Roo.NorthLayoutRegion = function(mgr, config){
30974     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30975     if(this.split){
30976         this.split.placement = Roo.SplitBar.TOP;
30977         this.split.orientation = Roo.SplitBar.VERTICAL;
30978         this.split.el.addClass("x-layout-split-v");
30979     }
30980     var size = config.initialSize || config.height;
30981     if(typeof size != "undefined"){
30982         this.el.setHeight(size);
30983     }
30984 };
30985 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30986     orientation: Roo.SplitBar.VERTICAL,
30987     getBox : function(){
30988         if(this.collapsed){
30989             return this.collapsedEl.getBox();
30990         }
30991         var box = this.el.getBox();
30992         if(this.split){
30993             box.height += this.split.el.getHeight();
30994         }
30995         return box;
30996     },
30997     
30998     updateBox : function(box){
30999         if(this.split && !this.collapsed){
31000             box.height -= this.split.el.getHeight();
31001             this.split.el.setLeft(box.x);
31002             this.split.el.setTop(box.y+box.height);
31003             this.split.el.setWidth(box.width);
31004         }
31005         if(this.collapsed){
31006             this.updateBody(box.width, null);
31007         }
31008         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31009     }
31010 });
31011
31012 Roo.SouthLayoutRegion = function(mgr, config){
31013     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31014     if(this.split){
31015         this.split.placement = Roo.SplitBar.BOTTOM;
31016         this.split.orientation = Roo.SplitBar.VERTICAL;
31017         this.split.el.addClass("x-layout-split-v");
31018     }
31019     var size = config.initialSize || config.height;
31020     if(typeof size != "undefined"){
31021         this.el.setHeight(size);
31022     }
31023 };
31024 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31025     orientation: Roo.SplitBar.VERTICAL,
31026     getBox : function(){
31027         if(this.collapsed){
31028             return this.collapsedEl.getBox();
31029         }
31030         var box = this.el.getBox();
31031         if(this.split){
31032             var sh = this.split.el.getHeight();
31033             box.height += sh;
31034             box.y -= sh;
31035         }
31036         return box;
31037     },
31038     
31039     updateBox : function(box){
31040         if(this.split && !this.collapsed){
31041             var sh = this.split.el.getHeight();
31042             box.height -= sh;
31043             box.y += sh;
31044             this.split.el.setLeft(box.x);
31045             this.split.el.setTop(box.y-sh);
31046             this.split.el.setWidth(box.width);
31047         }
31048         if(this.collapsed){
31049             this.updateBody(box.width, null);
31050         }
31051         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31052     }
31053 });
31054
31055 Roo.EastLayoutRegion = function(mgr, config){
31056     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31057     if(this.split){
31058         this.split.placement = Roo.SplitBar.RIGHT;
31059         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31060         this.split.el.addClass("x-layout-split-h");
31061     }
31062     var size = config.initialSize || config.width;
31063     if(typeof size != "undefined"){
31064         this.el.setWidth(size);
31065     }
31066 };
31067 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31068     orientation: Roo.SplitBar.HORIZONTAL,
31069     getBox : function(){
31070         if(this.collapsed){
31071             return this.collapsedEl.getBox();
31072         }
31073         var box = this.el.getBox();
31074         if(this.split){
31075             var sw = this.split.el.getWidth();
31076             box.width += sw;
31077             box.x -= sw;
31078         }
31079         return box;
31080     },
31081
31082     updateBox : function(box){
31083         if(this.split && !this.collapsed){
31084             var sw = this.split.el.getWidth();
31085             box.width -= sw;
31086             this.split.el.setLeft(box.x);
31087             this.split.el.setTop(box.y);
31088             this.split.el.setHeight(box.height);
31089             box.x += sw;
31090         }
31091         if(this.collapsed){
31092             this.updateBody(null, box.height);
31093         }
31094         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31095     }
31096 });
31097
31098 Roo.WestLayoutRegion = function(mgr, config){
31099     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31100     if(this.split){
31101         this.split.placement = Roo.SplitBar.LEFT;
31102         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31103         this.split.el.addClass("x-layout-split-h");
31104     }
31105     var size = config.initialSize || config.width;
31106     if(typeof size != "undefined"){
31107         this.el.setWidth(size);
31108     }
31109 };
31110 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31111     orientation: Roo.SplitBar.HORIZONTAL,
31112     getBox : function(){
31113         if(this.collapsed){
31114             return this.collapsedEl.getBox();
31115         }
31116         var box = this.el.getBox();
31117         if(this.split){
31118             box.width += this.split.el.getWidth();
31119         }
31120         return box;
31121     },
31122     
31123     updateBox : function(box){
31124         if(this.split && !this.collapsed){
31125             var sw = this.split.el.getWidth();
31126             box.width -= sw;
31127             this.split.el.setLeft(box.x+box.width);
31128             this.split.el.setTop(box.y);
31129             this.split.el.setHeight(box.height);
31130         }
31131         if(this.collapsed){
31132             this.updateBody(null, box.height);
31133         }
31134         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31135     }
31136 });
31137 /*
31138  * Based on:
31139  * Ext JS Library 1.1.1
31140  * Copyright(c) 2006-2007, Ext JS, LLC.
31141  *
31142  * Originally Released Under LGPL - original licence link has changed is not relivant.
31143  *
31144  * Fork - LGPL
31145  * <script type="text/javascript">
31146  */
31147  
31148  
31149 /*
31150  * Private internal class for reading and applying state
31151  */
31152 Roo.LayoutStateManager = function(layout){
31153      // default empty state
31154      this.state = {
31155         north: {},
31156         south: {},
31157         east: {},
31158         west: {}       
31159     };
31160 };
31161
31162 Roo.LayoutStateManager.prototype = {
31163     init : function(layout, provider){
31164         this.provider = provider;
31165         var state = provider.get(layout.id+"-layout-state");
31166         if(state){
31167             var wasUpdating = layout.isUpdating();
31168             if(!wasUpdating){
31169                 layout.beginUpdate();
31170             }
31171             for(var key in state){
31172                 if(typeof state[key] != "function"){
31173                     var rstate = state[key];
31174                     var r = layout.getRegion(key);
31175                     if(r && rstate){
31176                         if(rstate.size){
31177                             r.resizeTo(rstate.size);
31178                         }
31179                         if(rstate.collapsed == true){
31180                             r.collapse(true);
31181                         }else{
31182                             r.expand(null, true);
31183                         }
31184                     }
31185                 }
31186             }
31187             if(!wasUpdating){
31188                 layout.endUpdate();
31189             }
31190             this.state = state; 
31191         }
31192         this.layout = layout;
31193         layout.on("regionresized", this.onRegionResized, this);
31194         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31195         layout.on("regionexpanded", this.onRegionExpanded, this);
31196     },
31197     
31198     storeState : function(){
31199         this.provider.set(this.layout.id+"-layout-state", this.state);
31200     },
31201     
31202     onRegionResized : function(region, newSize){
31203         this.state[region.getPosition()].size = newSize;
31204         this.storeState();
31205     },
31206     
31207     onRegionCollapsed : function(region){
31208         this.state[region.getPosition()].collapsed = true;
31209         this.storeState();
31210     },
31211     
31212     onRegionExpanded : function(region){
31213         this.state[region.getPosition()].collapsed = false;
31214         this.storeState();
31215     }
31216 };/*
31217  * Based on:
31218  * Ext JS Library 1.1.1
31219  * Copyright(c) 2006-2007, Ext JS, LLC.
31220  *
31221  * Originally Released Under LGPL - original licence link has changed is not relivant.
31222  *
31223  * Fork - LGPL
31224  * <script type="text/javascript">
31225  */
31226 /**
31227  * @class Roo.ContentPanel
31228  * @extends Roo.util.Observable
31229  * A basic ContentPanel element.
31230  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31231  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31232  * @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
31233  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31234  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31235  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31236  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31237  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31238  * @cfg {String} title          The title for this panel
31239  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31240  * @cfg {String} url            Calls {@link #setUrl} with this value
31241  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31242  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31243  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31244  * @cfg {String} content        Raw content to fill content panel with (uses setContent on construction.)
31245
31246  * @constructor
31247  * Create a new ContentPanel.
31248  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31249  * @param {String/Object} config A string to set only the title or a config object
31250  * @param {String} content (optional) Set the HTML content for this panel
31251  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31252  */
31253 Roo.ContentPanel = function(el, config, content){
31254     
31255      
31256     /*
31257     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31258         config = el;
31259         el = Roo.id();
31260     }
31261     if (config && config.parentLayout) { 
31262         el = config.parentLayout.el.createChild(); 
31263     }
31264     */
31265     if(el.autoCreate){ // xtype is available if this is called from factory
31266         config = el;
31267         el = Roo.id();
31268     }
31269     this.el = Roo.get(el);
31270     if(!this.el && config && config.autoCreate){
31271         if(typeof config.autoCreate == "object"){
31272             if(!config.autoCreate.id){
31273                 config.autoCreate.id = config.id||el;
31274             }
31275             this.el = Roo.DomHelper.append(document.body,
31276                         config.autoCreate, true);
31277         }else{
31278             this.el = Roo.DomHelper.append(document.body,
31279                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31280         }
31281     }
31282     this.closable = false;
31283     this.loaded = false;
31284     this.active = false;
31285     if(typeof config == "string"){
31286         this.title = config;
31287     }else{
31288         Roo.apply(this, config);
31289     }
31290     
31291     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31292         this.wrapEl = this.el.wrap();    
31293         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
31294         
31295     }
31296     
31297     
31298     
31299     if(this.resizeEl){
31300         this.resizeEl = Roo.get(this.resizeEl, true);
31301     }else{
31302         this.resizeEl = this.el;
31303     }
31304     this.addEvents({
31305         /**
31306          * @event activate
31307          * Fires when this panel is activated. 
31308          * @param {Roo.ContentPanel} this
31309          */
31310         "activate" : true,
31311         /**
31312          * @event deactivate
31313          * Fires when this panel is activated. 
31314          * @param {Roo.ContentPanel} this
31315          */
31316         "deactivate" : true,
31317
31318         /**
31319          * @event resize
31320          * Fires when this panel is resized if fitToFrame is true.
31321          * @param {Roo.ContentPanel} this
31322          * @param {Number} width The width after any component adjustments
31323          * @param {Number} height The height after any component adjustments
31324          */
31325         "resize" : true
31326     });
31327     if(this.autoScroll){
31328         this.resizeEl.setStyle("overflow", "auto");
31329     } else {
31330         // fix randome scrolling
31331         this.el.on('scroll', function() {
31332             Roo.log('fix random scolling');
31333             this.scrollTo('top',0); 
31334         });
31335     }
31336     content = content || this.content;
31337     if(content){
31338         this.setContent(content);
31339     }
31340     if(config && config.url){
31341         this.setUrl(this.url, this.params, this.loadOnce);
31342     }
31343     
31344     
31345     
31346     Roo.ContentPanel.superclass.constructor.call(this);
31347 };
31348
31349 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31350     tabTip:'',
31351     setRegion : function(region){
31352         this.region = region;
31353         if(region){
31354            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31355         }else{
31356            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31357         } 
31358     },
31359     
31360     /**
31361      * Returns the toolbar for this Panel if one was configured. 
31362      * @return {Roo.Toolbar} 
31363      */
31364     getToolbar : function(){
31365         return this.toolbar;
31366     },
31367     
31368     setActiveState : function(active){
31369         this.active = active;
31370         if(!active){
31371             this.fireEvent("deactivate", this);
31372         }else{
31373             this.fireEvent("activate", this);
31374         }
31375     },
31376     /**
31377      * Updates this panel's element
31378      * @param {String} content The new content
31379      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31380     */
31381     setContent : function(content, loadScripts){
31382         this.el.update(content, loadScripts);
31383     },
31384
31385     ignoreResize : function(w, h){
31386         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31387             return true;
31388         }else{
31389             this.lastSize = {width: w, height: h};
31390             return false;
31391         }
31392     },
31393     /**
31394      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31395      * @return {Roo.UpdateManager} The UpdateManager
31396      */
31397     getUpdateManager : function(){
31398         return this.el.getUpdateManager();
31399     },
31400      /**
31401      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31402      * @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:
31403 <pre><code>
31404 panel.load({
31405     url: "your-url.php",
31406     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31407     callback: yourFunction,
31408     scope: yourObject, //(optional scope)
31409     discardUrl: false,
31410     nocache: false,
31411     text: "Loading...",
31412     timeout: 30,
31413     scripts: false
31414 });
31415 </code></pre>
31416      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31417      * 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.
31418      * @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}
31419      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31420      * @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.
31421      * @return {Roo.ContentPanel} this
31422      */
31423     load : function(){
31424         var um = this.el.getUpdateManager();
31425         um.update.apply(um, arguments);
31426         return this;
31427     },
31428
31429
31430     /**
31431      * 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.
31432      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31433      * @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)
31434      * @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)
31435      * @return {Roo.UpdateManager} The UpdateManager
31436      */
31437     setUrl : function(url, params, loadOnce){
31438         if(this.refreshDelegate){
31439             this.removeListener("activate", this.refreshDelegate);
31440         }
31441         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31442         this.on("activate", this.refreshDelegate);
31443         return this.el.getUpdateManager();
31444     },
31445     
31446     _handleRefresh : function(url, params, loadOnce){
31447         if(!loadOnce || !this.loaded){
31448             var updater = this.el.getUpdateManager();
31449             updater.update(url, params, this._setLoaded.createDelegate(this));
31450         }
31451     },
31452     
31453     _setLoaded : function(){
31454         this.loaded = true;
31455     }, 
31456     
31457     /**
31458      * Returns this panel's id
31459      * @return {String} 
31460      */
31461     getId : function(){
31462         return this.el.id;
31463     },
31464     
31465     /** 
31466      * Returns this panel's element - used by regiosn to add.
31467      * @return {Roo.Element} 
31468      */
31469     getEl : function(){
31470         return this.wrapEl || this.el;
31471     },
31472     
31473     adjustForComponents : function(width, height){
31474         if(this.resizeEl != this.el){
31475             width -= this.el.getFrameWidth('lr');
31476             height -= this.el.getFrameWidth('tb');
31477         }
31478         if(this.toolbar){
31479             var te = this.toolbar.getEl();
31480             height -= te.getHeight();
31481             te.setWidth(width);
31482         }
31483         if(this.adjustments){
31484             width += this.adjustments[0];
31485             height += this.adjustments[1];
31486         }
31487         return {"width": width, "height": height};
31488     },
31489     
31490     setSize : function(width, height){
31491         if(this.fitToFrame && !this.ignoreResize(width, height)){
31492             if(this.fitContainer && this.resizeEl != this.el){
31493                 this.el.setSize(width, height);
31494             }
31495             var size = this.adjustForComponents(width, height);
31496             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31497             this.fireEvent('resize', this, size.width, size.height);
31498         }
31499     },
31500     
31501     /**
31502      * Returns this panel's title
31503      * @return {String} 
31504      */
31505     getTitle : function(){
31506         return this.title;
31507     },
31508     
31509     /**
31510      * Set this panel's title
31511      * @param {String} title
31512      */
31513     setTitle : function(title){
31514         this.title = title;
31515         if(this.region){
31516             this.region.updatePanelTitle(this, title);
31517         }
31518     },
31519     
31520     /**
31521      * Returns true is this panel was configured to be closable
31522      * @return {Boolean} 
31523      */
31524     isClosable : function(){
31525         return this.closable;
31526     },
31527     
31528     beforeSlide : function(){
31529         this.el.clip();
31530         this.resizeEl.clip();
31531     },
31532     
31533     afterSlide : function(){
31534         this.el.unclip();
31535         this.resizeEl.unclip();
31536     },
31537     
31538     /**
31539      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31540      *   Will fail silently if the {@link #setUrl} method has not been called.
31541      *   This does not activate the panel, just updates its content.
31542      */
31543     refresh : function(){
31544         if(this.refreshDelegate){
31545            this.loaded = false;
31546            this.refreshDelegate();
31547         }
31548     },
31549     
31550     /**
31551      * Destroys this panel
31552      */
31553     destroy : function(){
31554         this.el.removeAllListeners();
31555         var tempEl = document.createElement("span");
31556         tempEl.appendChild(this.el.dom);
31557         tempEl.innerHTML = "";
31558         this.el.remove();
31559         this.el = null;
31560     },
31561     
31562     /**
31563      * form - if the content panel contains a form - this is a reference to it.
31564      * @type {Roo.form.Form}
31565      */
31566     form : false,
31567     /**
31568      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31569      *    This contains a reference to it.
31570      * @type {Roo.View}
31571      */
31572     view : false,
31573     
31574       /**
31575      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31576      * <pre><code>
31577
31578 layout.addxtype({
31579        xtype : 'Form',
31580        items: [ .... ]
31581    }
31582 );
31583
31584 </code></pre>
31585      * @param {Object} cfg Xtype definition of item to add.
31586      */
31587     
31588     addxtype : function(cfg) {
31589         // add form..
31590         if (cfg.xtype.match(/^Form$/)) {
31591             var el = this.el.createChild();
31592
31593             this.form = new  Roo.form.Form(cfg);
31594             
31595             
31596             if ( this.form.allItems.length) this.form.render(el.dom);
31597             return this.form;
31598         }
31599         // should only have one of theses..
31600         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31601             // views..
31602             cfg.el = this.el.appendChild(document.createElement("div"));
31603             // factory?
31604             
31605             var ret = new Roo.factory(cfg);
31606             ret.render && ret.render(false, ''); // render blank..
31607             this.view = ret;
31608             return ret;
31609         }
31610         return false;
31611     }
31612 });
31613
31614 /**
31615  * @class Roo.GridPanel
31616  * @extends Roo.ContentPanel
31617  * @constructor
31618  * Create a new GridPanel.
31619  * @param {Roo.grid.Grid} grid The grid for this panel
31620  * @param {String/Object} config A string to set only the panel's title, or a config object
31621  */
31622 Roo.GridPanel = function(grid, config){
31623     
31624   
31625     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31626         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31627         
31628     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31629     
31630     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31631     
31632     if(this.toolbar){
31633         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31634     }
31635     // xtype created footer. - not sure if will work as we normally have to render first..
31636     if (this.footer && !this.footer.el && this.footer.xtype) {
31637         
31638         this.footer.container = this.grid.getView().getFooterPanel(true);
31639         this.footer.dataSource = this.grid.dataSource;
31640         this.footer = Roo.factory(this.footer, Roo);
31641         
31642     }
31643     
31644     grid.monitorWindowResize = false; // turn off autosizing
31645     grid.autoHeight = false;
31646     grid.autoWidth = false;
31647     this.grid = grid;
31648     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31649 };
31650
31651 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31652     getId : function(){
31653         return this.grid.id;
31654     },
31655     
31656     /**
31657      * Returns the grid for this panel
31658      * @return {Roo.grid.Grid} 
31659      */
31660     getGrid : function(){
31661         return this.grid;    
31662     },
31663     
31664     setSize : function(width, height){
31665         if(!this.ignoreResize(width, height)){
31666             var grid = this.grid;
31667             var size = this.adjustForComponents(width, height);
31668             grid.getGridEl().setSize(size.width, size.height);
31669             grid.autoSize();
31670         }
31671     },
31672     
31673     beforeSlide : function(){
31674         this.grid.getView().scroller.clip();
31675     },
31676     
31677     afterSlide : function(){
31678         this.grid.getView().scroller.unclip();
31679     },
31680     
31681     destroy : function(){
31682         this.grid.destroy();
31683         delete this.grid;
31684         Roo.GridPanel.superclass.destroy.call(this); 
31685     }
31686 });
31687
31688
31689 /**
31690  * @class Roo.NestedLayoutPanel
31691  * @extends Roo.ContentPanel
31692  * @constructor
31693  * Create a new NestedLayoutPanel.
31694  * 
31695  * 
31696  * @param {Roo.BorderLayout} layout The layout for this panel
31697  * @param {String/Object} config A string to set only the title or a config object
31698  */
31699 Roo.NestedLayoutPanel = function(layout, config)
31700 {
31701     // construct with only one argument..
31702     /* FIXME - implement nicer consturctors
31703     if (layout.layout) {
31704         config = layout;
31705         layout = config.layout;
31706         delete config.layout;
31707     }
31708     if (layout.xtype && !layout.getEl) {
31709         // then layout needs constructing..
31710         layout = Roo.factory(layout, Roo);
31711     }
31712     */
31713     
31714     
31715     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31716     
31717     layout.monitorWindowResize = false; // turn off autosizing
31718     this.layout = layout;
31719     this.layout.getEl().addClass("x-layout-nested-layout");
31720     
31721     
31722     
31723     
31724 };
31725
31726 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31727
31728     setSize : function(width, height){
31729         if(!this.ignoreResize(width, height)){
31730             var size = this.adjustForComponents(width, height);
31731             var el = this.layout.getEl();
31732             el.setSize(size.width, size.height);
31733             var touch = el.dom.offsetWidth;
31734             this.layout.layout();
31735             // ie requires a double layout on the first pass
31736             if(Roo.isIE && !this.initialized){
31737                 this.initialized = true;
31738                 this.layout.layout();
31739             }
31740         }
31741     },
31742     
31743     // activate all subpanels if not currently active..
31744     
31745     setActiveState : function(active){
31746         this.active = active;
31747         if(!active){
31748             this.fireEvent("deactivate", this);
31749             return;
31750         }
31751         
31752         this.fireEvent("activate", this);
31753         // not sure if this should happen before or after..
31754         if (!this.layout) {
31755             return; // should not happen..
31756         }
31757         var reg = false;
31758         for (var r in this.layout.regions) {
31759             reg = this.layout.getRegion(r);
31760             if (reg.getActivePanel()) {
31761                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31762                 reg.setActivePanel(reg.getActivePanel());
31763                 continue;
31764             }
31765             if (!reg.panels.length) {
31766                 continue;
31767             }
31768             reg.showPanel(reg.getPanel(0));
31769         }
31770         
31771         
31772         
31773         
31774     },
31775     
31776     /**
31777      * Returns the nested BorderLayout for this panel
31778      * @return {Roo.BorderLayout} 
31779      */
31780     getLayout : function(){
31781         return this.layout;
31782     },
31783     
31784      /**
31785      * Adds a xtype elements to the layout of the nested panel
31786      * <pre><code>
31787
31788 panel.addxtype({
31789        xtype : 'ContentPanel',
31790        region: 'west',
31791        items: [ .... ]
31792    }
31793 );
31794
31795 panel.addxtype({
31796         xtype : 'NestedLayoutPanel',
31797         region: 'west',
31798         layout: {
31799            center: { },
31800            west: { }   
31801         },
31802         items : [ ... list of content panels or nested layout panels.. ]
31803    }
31804 );
31805 </code></pre>
31806      * @param {Object} cfg Xtype definition of item to add.
31807      */
31808     addxtype : function(cfg) {
31809         return this.layout.addxtype(cfg);
31810     
31811     }
31812 });
31813
31814 Roo.ScrollPanel = function(el, config, content){
31815     config = config || {};
31816     config.fitToFrame = true;
31817     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31818     
31819     this.el.dom.style.overflow = "hidden";
31820     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31821     this.el.removeClass("x-layout-inactive-content");
31822     this.el.on("mousewheel", this.onWheel, this);
31823
31824     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31825     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31826     up.unselectable(); down.unselectable();
31827     up.on("click", this.scrollUp, this);
31828     down.on("click", this.scrollDown, this);
31829     up.addClassOnOver("x-scroller-btn-over");
31830     down.addClassOnOver("x-scroller-btn-over");
31831     up.addClassOnClick("x-scroller-btn-click");
31832     down.addClassOnClick("x-scroller-btn-click");
31833     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31834
31835     this.resizeEl = this.el;
31836     this.el = wrap; this.up = up; this.down = down;
31837 };
31838
31839 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31840     increment : 100,
31841     wheelIncrement : 5,
31842     scrollUp : function(){
31843         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31844     },
31845
31846     scrollDown : function(){
31847         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31848     },
31849
31850     afterScroll : function(){
31851         var el = this.resizeEl;
31852         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31853         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31854         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31855     },
31856
31857     setSize : function(){
31858         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31859         this.afterScroll();
31860     },
31861
31862     onWheel : function(e){
31863         var d = e.getWheelDelta();
31864         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31865         this.afterScroll();
31866         e.stopEvent();
31867     },
31868
31869     setContent : function(content, loadScripts){
31870         this.resizeEl.update(content, loadScripts);
31871     }
31872
31873 });
31874
31875
31876
31877
31878
31879
31880
31881
31882
31883 /**
31884  * @class Roo.TreePanel
31885  * @extends Roo.ContentPanel
31886  * @constructor
31887  * Create a new TreePanel. - defaults to fit/scoll contents.
31888  * @param {String/Object} config A string to set only the panel's title, or a config object
31889  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31890  */
31891 Roo.TreePanel = function(config){
31892     var el = config.el;
31893     var tree = config.tree;
31894     delete config.tree; 
31895     delete config.el; // hopefull!
31896     
31897     // wrapper for IE7 strict & safari scroll issue
31898     
31899     var treeEl = el.createChild();
31900     config.resizeEl = treeEl;
31901     
31902     
31903     
31904     Roo.TreePanel.superclass.constructor.call(this, el, config);
31905  
31906  
31907     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31908     //console.log(tree);
31909     this.on('activate', function()
31910     {
31911         if (this.tree.rendered) {
31912             return;
31913         }
31914         //console.log('render tree');
31915         this.tree.render();
31916     });
31917     
31918     this.on('resize',  function (cp, w, h) {
31919             this.tree.innerCt.setWidth(w);
31920             this.tree.innerCt.setHeight(h);
31921             this.tree.innerCt.setStyle('overflow-y', 'auto');
31922     });
31923
31924         
31925     
31926 };
31927
31928 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31929     fitToFrame : true,
31930     autoScroll : true
31931 });
31932
31933
31934
31935
31936
31937
31938
31939
31940
31941
31942
31943 /*
31944  * Based on:
31945  * Ext JS Library 1.1.1
31946  * Copyright(c) 2006-2007, Ext JS, LLC.
31947  *
31948  * Originally Released Under LGPL - original licence link has changed is not relivant.
31949  *
31950  * Fork - LGPL
31951  * <script type="text/javascript">
31952  */
31953  
31954
31955 /**
31956  * @class Roo.ReaderLayout
31957  * @extends Roo.BorderLayout
31958  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31959  * center region containing two nested regions (a top one for a list view and one for item preview below),
31960  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31961  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31962  * expedites the setup of the overall layout and regions for this common application style.
31963  * Example:
31964  <pre><code>
31965 var reader = new Roo.ReaderLayout();
31966 var CP = Roo.ContentPanel;  // shortcut for adding
31967
31968 reader.beginUpdate();
31969 reader.add("north", new CP("north", "North"));
31970 reader.add("west", new CP("west", {title: "West"}));
31971 reader.add("east", new CP("east", {title: "East"}));
31972
31973 reader.regions.listView.add(new CP("listView", "List"));
31974 reader.regions.preview.add(new CP("preview", "Preview"));
31975 reader.endUpdate();
31976 </code></pre>
31977 * @constructor
31978 * Create a new ReaderLayout
31979 * @param {Object} config Configuration options
31980 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31981 * document.body if omitted)
31982 */
31983 Roo.ReaderLayout = function(config, renderTo){
31984     var c = config || {size:{}};
31985     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31986         north: c.north !== false ? Roo.apply({
31987             split:false,
31988             initialSize: 32,
31989             titlebar: false
31990         }, c.north) : false,
31991         west: c.west !== false ? Roo.apply({
31992             split:true,
31993             initialSize: 200,
31994             minSize: 175,
31995             maxSize: 400,
31996             titlebar: true,
31997             collapsible: true,
31998             animate: true,
31999             margins:{left:5,right:0,bottom:5,top:5},
32000             cmargins:{left:5,right:5,bottom:5,top:5}
32001         }, c.west) : false,
32002         east: c.east !== false ? Roo.apply({
32003             split:true,
32004             initialSize: 200,
32005             minSize: 175,
32006             maxSize: 400,
32007             titlebar: true,
32008             collapsible: true,
32009             animate: true,
32010             margins:{left:0,right:5,bottom:5,top:5},
32011             cmargins:{left:5,right:5,bottom:5,top:5}
32012         }, c.east) : false,
32013         center: Roo.apply({
32014             tabPosition: 'top',
32015             autoScroll:false,
32016             closeOnTab: true,
32017             titlebar:false,
32018             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32019         }, c.center)
32020     });
32021
32022     this.el.addClass('x-reader');
32023
32024     this.beginUpdate();
32025
32026     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32027         south: c.preview !== false ? Roo.apply({
32028             split:true,
32029             initialSize: 200,
32030             minSize: 100,
32031             autoScroll:true,
32032             collapsible:true,
32033             titlebar: true,
32034             cmargins:{top:5,left:0, right:0, bottom:0}
32035         }, c.preview) : false,
32036         center: Roo.apply({
32037             autoScroll:false,
32038             titlebar:false,
32039             minHeight:200
32040         }, c.listView)
32041     });
32042     this.add('center', new Roo.NestedLayoutPanel(inner,
32043             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32044
32045     this.endUpdate();
32046
32047     this.regions.preview = inner.getRegion('south');
32048     this.regions.listView = inner.getRegion('center');
32049 };
32050
32051 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32052  * Based on:
32053  * Ext JS Library 1.1.1
32054  * Copyright(c) 2006-2007, Ext JS, LLC.
32055  *
32056  * Originally Released Under LGPL - original licence link has changed is not relivant.
32057  *
32058  * Fork - LGPL
32059  * <script type="text/javascript">
32060  */
32061  
32062 /**
32063  * @class Roo.grid.Grid
32064  * @extends Roo.util.Observable
32065  * This class represents the primary interface of a component based grid control.
32066  * <br><br>Usage:<pre><code>
32067  var grid = new Roo.grid.Grid("my-container-id", {
32068      ds: myDataStore,
32069      cm: myColModel,
32070      selModel: mySelectionModel,
32071      autoSizeColumns: true,
32072      monitorWindowResize: false,
32073      trackMouseOver: true
32074  });
32075  // set any options
32076  grid.render();
32077  * </code></pre>
32078  * <b>Common Problems:</b><br/>
32079  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32080  * element will correct this<br/>
32081  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32082  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32083  * are unpredictable.<br/>
32084  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32085  * grid to calculate dimensions/offsets.<br/>
32086   * @constructor
32087  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32088  * The container MUST have some type of size defined for the grid to fill. The container will be
32089  * automatically set to position relative if it isn't already.
32090  * @param {Object} config A config object that sets properties on this grid.
32091  */
32092 Roo.grid.Grid = function(container, config){
32093         // initialize the container
32094         this.container = Roo.get(container);
32095         this.container.update("");
32096         this.container.setStyle("overflow", "hidden");
32097     this.container.addClass('x-grid-container');
32098
32099     this.id = this.container.id;
32100
32101     Roo.apply(this, config);
32102     // check and correct shorthanded configs
32103     if(this.ds){
32104         this.dataSource = this.ds;
32105         delete this.ds;
32106     }
32107     if(this.cm){
32108         this.colModel = this.cm;
32109         delete this.cm;
32110     }
32111     if(this.sm){
32112         this.selModel = this.sm;
32113         delete this.sm;
32114     }
32115
32116     if (this.selModel) {
32117         this.selModel = Roo.factory(this.selModel, Roo.grid);
32118         this.sm = this.selModel;
32119         this.sm.xmodule = this.xmodule || false;
32120     }
32121     if (typeof(this.colModel.config) == 'undefined') {
32122         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32123         this.cm = this.colModel;
32124         this.cm.xmodule = this.xmodule || false;
32125     }
32126     if (this.dataSource) {
32127         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32128         this.ds = this.dataSource;
32129         this.ds.xmodule = this.xmodule || false;
32130          
32131     }
32132     
32133     
32134     
32135     if(this.width){
32136         this.container.setWidth(this.width);
32137     }
32138
32139     if(this.height){
32140         this.container.setHeight(this.height);
32141     }
32142     /** @private */
32143         this.addEvents({
32144         // raw events
32145         /**
32146          * @event click
32147          * The raw click event for the entire grid.
32148          * @param {Roo.EventObject} e
32149          */
32150         "click" : true,
32151         /**
32152          * @event dblclick
32153          * The raw dblclick event for the entire grid.
32154          * @param {Roo.EventObject} e
32155          */
32156         "dblclick" : true,
32157         /**
32158          * @event contextmenu
32159          * The raw contextmenu event for the entire grid.
32160          * @param {Roo.EventObject} e
32161          */
32162         "contextmenu" : true,
32163         /**
32164          * @event mousedown
32165          * The raw mousedown event for the entire grid.
32166          * @param {Roo.EventObject} e
32167          */
32168         "mousedown" : true,
32169         /**
32170          * @event mouseup
32171          * The raw mouseup event for the entire grid.
32172          * @param {Roo.EventObject} e
32173          */
32174         "mouseup" : true,
32175         /**
32176          * @event mouseover
32177          * The raw mouseover event for the entire grid.
32178          * @param {Roo.EventObject} e
32179          */
32180         "mouseover" : true,
32181         /**
32182          * @event mouseout
32183          * The raw mouseout event for the entire grid.
32184          * @param {Roo.EventObject} e
32185          */
32186         "mouseout" : true,
32187         /**
32188          * @event keypress
32189          * The raw keypress event for the entire grid.
32190          * @param {Roo.EventObject} e
32191          */
32192         "keypress" : true,
32193         /**
32194          * @event keydown
32195          * The raw keydown event for the entire grid.
32196          * @param {Roo.EventObject} e
32197          */
32198         "keydown" : true,
32199
32200         // custom events
32201
32202         /**
32203          * @event cellclick
32204          * Fires when a cell is clicked
32205          * @param {Grid} this
32206          * @param {Number} rowIndex
32207          * @param {Number} columnIndex
32208          * @param {Roo.EventObject} e
32209          */
32210         "cellclick" : true,
32211         /**
32212          * @event celldblclick
32213          * Fires when a cell is double clicked
32214          * @param {Grid} this
32215          * @param {Number} rowIndex
32216          * @param {Number} columnIndex
32217          * @param {Roo.EventObject} e
32218          */
32219         "celldblclick" : true,
32220         /**
32221          * @event rowclick
32222          * Fires when a row is clicked
32223          * @param {Grid} this
32224          * @param {Number} rowIndex
32225          * @param {Roo.EventObject} e
32226          */
32227         "rowclick" : true,
32228         /**
32229          * @event rowdblclick
32230          * Fires when a row is double clicked
32231          * @param {Grid} this
32232          * @param {Number} rowIndex
32233          * @param {Roo.EventObject} e
32234          */
32235         "rowdblclick" : true,
32236         /**
32237          * @event headerclick
32238          * Fires when a header is clicked
32239          * @param {Grid} this
32240          * @param {Number} columnIndex
32241          * @param {Roo.EventObject} e
32242          */
32243         "headerclick" : true,
32244         /**
32245          * @event headerdblclick
32246          * Fires when a header cell is double clicked
32247          * @param {Grid} this
32248          * @param {Number} columnIndex
32249          * @param {Roo.EventObject} e
32250          */
32251         "headerdblclick" : true,
32252         /**
32253          * @event rowcontextmenu
32254          * Fires when a row is right clicked
32255          * @param {Grid} this
32256          * @param {Number} rowIndex
32257          * @param {Roo.EventObject} e
32258          */
32259         "rowcontextmenu" : true,
32260         /**
32261          * @event cellcontextmenu
32262          * Fires when a cell is right clicked
32263          * @param {Grid} this
32264          * @param {Number} rowIndex
32265          * @param {Number} cellIndex
32266          * @param {Roo.EventObject} e
32267          */
32268          "cellcontextmenu" : true,
32269         /**
32270          * @event headercontextmenu
32271          * Fires when a header is right clicked
32272          * @param {Grid} this
32273          * @param {Number} columnIndex
32274          * @param {Roo.EventObject} e
32275          */
32276         "headercontextmenu" : true,
32277         /**
32278          * @event bodyscroll
32279          * Fires when the body element is scrolled
32280          * @param {Number} scrollLeft
32281          * @param {Number} scrollTop
32282          */
32283         "bodyscroll" : true,
32284         /**
32285          * @event columnresize
32286          * Fires when the user resizes a column
32287          * @param {Number} columnIndex
32288          * @param {Number} newSize
32289          */
32290         "columnresize" : true,
32291         /**
32292          * @event columnmove
32293          * Fires when the user moves a column
32294          * @param {Number} oldIndex
32295          * @param {Number} newIndex
32296          */
32297         "columnmove" : true,
32298         /**
32299          * @event startdrag
32300          * Fires when row(s) start being dragged
32301          * @param {Grid} this
32302          * @param {Roo.GridDD} dd The drag drop object
32303          * @param {event} e The raw browser event
32304          */
32305         "startdrag" : true,
32306         /**
32307          * @event enddrag
32308          * Fires when a drag operation is complete
32309          * @param {Grid} this
32310          * @param {Roo.GridDD} dd The drag drop object
32311          * @param {event} e The raw browser event
32312          */
32313         "enddrag" : true,
32314         /**
32315          * @event dragdrop
32316          * Fires when dragged row(s) are dropped on a valid DD target
32317          * @param {Grid} this
32318          * @param {Roo.GridDD} dd The drag drop object
32319          * @param {String} targetId The target drag drop object
32320          * @param {event} e The raw browser event
32321          */
32322         "dragdrop" : true,
32323         /**
32324          * @event dragover
32325          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32326          * @param {Grid} this
32327          * @param {Roo.GridDD} dd The drag drop object
32328          * @param {String} targetId The target drag drop object
32329          * @param {event} e The raw browser event
32330          */
32331         "dragover" : true,
32332         /**
32333          * @event dragenter
32334          *  Fires when the dragged row(s) first cross another DD target while being dragged
32335          * @param {Grid} this
32336          * @param {Roo.GridDD} dd The drag drop object
32337          * @param {String} targetId The target drag drop object
32338          * @param {event} e The raw browser event
32339          */
32340         "dragenter" : true,
32341         /**
32342          * @event dragout
32343          * Fires when the dragged row(s) leave another DD target while being dragged
32344          * @param {Grid} this
32345          * @param {Roo.GridDD} dd The drag drop object
32346          * @param {String} targetId The target drag drop object
32347          * @param {event} e The raw browser event
32348          */
32349         "dragout" : true,
32350         /**
32351          * @event rowclass
32352          * Fires when a row is rendered, so you can change add a style to it.
32353          * @param {GridView} gridview   The grid view
32354          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32355          */
32356         'rowclass' : true,
32357
32358         /**
32359          * @event render
32360          * Fires when the grid is rendered
32361          * @param {Grid} grid
32362          */
32363         'render' : true
32364     });
32365
32366     Roo.grid.Grid.superclass.constructor.call(this);
32367 };
32368 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32369     
32370     /**
32371      * @cfg {String} ddGroup - drag drop group.
32372      */
32373
32374     /**
32375      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32376      */
32377     minColumnWidth : 25,
32378
32379     /**
32380      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32381      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32382      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32383      */
32384     autoSizeColumns : false,
32385
32386     /**
32387      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32388      */
32389     autoSizeHeaders : true,
32390
32391     /**
32392      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32393      */
32394     monitorWindowResize : true,
32395
32396     /**
32397      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32398      * rows measured to get a columns size. Default is 0 (all rows).
32399      */
32400     maxRowsToMeasure : 0,
32401
32402     /**
32403      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32404      */
32405     trackMouseOver : true,
32406
32407     /**
32408     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32409     */
32410     
32411     /**
32412     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32413     */
32414     enableDragDrop : false,
32415     
32416     /**
32417     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32418     */
32419     enableColumnMove : true,
32420     
32421     /**
32422     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32423     */
32424     enableColumnHide : true,
32425     
32426     /**
32427     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32428     */
32429     enableRowHeightSync : false,
32430     
32431     /**
32432     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32433     */
32434     stripeRows : true,
32435     
32436     /**
32437     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32438     */
32439     autoHeight : false,
32440
32441     /**
32442      * @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.
32443      */
32444     autoExpandColumn : false,
32445
32446     /**
32447     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32448     * Default is 50.
32449     */
32450     autoExpandMin : 50,
32451
32452     /**
32453     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32454     */
32455     autoExpandMax : 1000,
32456
32457     /**
32458     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32459     */
32460     view : null,
32461
32462     /**
32463     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32464     */
32465     loadMask : false,
32466     /**
32467     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32468     */
32469     dropTarget: false,
32470     
32471    
32472     
32473     // private
32474     rendered : false,
32475
32476     /**
32477     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32478     * of a fixed width. Default is false.
32479     */
32480     /**
32481     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32482     */
32483     /**
32484      * Called once after all setup has been completed and the grid is ready to be rendered.
32485      * @return {Roo.grid.Grid} this
32486      */
32487     render : function()
32488     {
32489         var c = this.container;
32490         // try to detect autoHeight/width mode
32491         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32492             this.autoHeight = true;
32493         }
32494         var view = this.getView();
32495         view.init(this);
32496
32497         c.on("click", this.onClick, this);
32498         c.on("dblclick", this.onDblClick, this);
32499         c.on("contextmenu", this.onContextMenu, this);
32500         c.on("keydown", this.onKeyDown, this);
32501
32502         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32503
32504         this.getSelectionModel().init(this);
32505
32506         view.render();
32507
32508         if(this.loadMask){
32509             this.loadMask = new Roo.LoadMask(this.container,
32510                     Roo.apply({store:this.dataSource}, this.loadMask));
32511         }
32512         
32513         
32514         if (this.toolbar && this.toolbar.xtype) {
32515             this.toolbar.container = this.getView().getHeaderPanel(true);
32516             this.toolbar = new Roo.Toolbar(this.toolbar);
32517         }
32518         if (this.footer && this.footer.xtype) {
32519             this.footer.dataSource = this.getDataSource();
32520             this.footer.container = this.getView().getFooterPanel(true);
32521             this.footer = Roo.factory(this.footer, Roo);
32522         }
32523         if (this.dropTarget && this.dropTarget.xtype) {
32524             delete this.dropTarget.xtype;
32525             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32526         }
32527         
32528         
32529         this.rendered = true;
32530         this.fireEvent('render', this);
32531         return this;
32532     },
32533
32534         /**
32535          * Reconfigures the grid to use a different Store and Column Model.
32536          * The View will be bound to the new objects and refreshed.
32537          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32538          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32539          */
32540     reconfigure : function(dataSource, colModel){
32541         if(this.loadMask){
32542             this.loadMask.destroy();
32543             this.loadMask = new Roo.LoadMask(this.container,
32544                     Roo.apply({store:dataSource}, this.loadMask));
32545         }
32546         this.view.bind(dataSource, colModel);
32547         this.dataSource = dataSource;
32548         this.colModel = colModel;
32549         this.view.refresh(true);
32550     },
32551
32552     // private
32553     onKeyDown : function(e){
32554         this.fireEvent("keydown", e);
32555     },
32556
32557     /**
32558      * Destroy this grid.
32559      * @param {Boolean} removeEl True to remove the element
32560      */
32561     destroy : function(removeEl, keepListeners){
32562         if(this.loadMask){
32563             this.loadMask.destroy();
32564         }
32565         var c = this.container;
32566         c.removeAllListeners();
32567         this.view.destroy();
32568         this.colModel.purgeListeners();
32569         if(!keepListeners){
32570             this.purgeListeners();
32571         }
32572         c.update("");
32573         if(removeEl === true){
32574             c.remove();
32575         }
32576     },
32577
32578     // private
32579     processEvent : function(name, e){
32580         this.fireEvent(name, e);
32581         var t = e.getTarget();
32582         var v = this.view;
32583         var header = v.findHeaderIndex(t);
32584         if(header !== false){
32585             this.fireEvent("header" + name, this, header, e);
32586         }else{
32587             var row = v.findRowIndex(t);
32588             var cell = v.findCellIndex(t);
32589             if(row !== false){
32590                 this.fireEvent("row" + name, this, row, e);
32591                 if(cell !== false){
32592                     this.fireEvent("cell" + name, this, row, cell, e);
32593                 }
32594             }
32595         }
32596     },
32597
32598     // private
32599     onClick : function(e){
32600         this.processEvent("click", e);
32601     },
32602
32603     // private
32604     onContextMenu : function(e, t){
32605         this.processEvent("contextmenu", e);
32606     },
32607
32608     // private
32609     onDblClick : function(e){
32610         this.processEvent("dblclick", e);
32611     },
32612
32613     // private
32614     walkCells : function(row, col, step, fn, scope){
32615         var cm = this.colModel, clen = cm.getColumnCount();
32616         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32617         if(step < 0){
32618             if(col < 0){
32619                 row--;
32620                 first = false;
32621             }
32622             while(row >= 0){
32623                 if(!first){
32624                     col = clen-1;
32625                 }
32626                 first = false;
32627                 while(col >= 0){
32628                     if(fn.call(scope || this, row, col, cm) === true){
32629                         return [row, col];
32630                     }
32631                     col--;
32632                 }
32633                 row--;
32634             }
32635         } else {
32636             if(col >= clen){
32637                 row++;
32638                 first = false;
32639             }
32640             while(row < rlen){
32641                 if(!first){
32642                     col = 0;
32643                 }
32644                 first = false;
32645                 while(col < clen){
32646                     if(fn.call(scope || this, row, col, cm) === true){
32647                         return [row, col];
32648                     }
32649                     col++;
32650                 }
32651                 row++;
32652             }
32653         }
32654         return null;
32655     },
32656
32657     // private
32658     getSelections : function(){
32659         return this.selModel.getSelections();
32660     },
32661
32662     /**
32663      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32664      * but if manual update is required this method will initiate it.
32665      */
32666     autoSize : function(){
32667         if(this.rendered){
32668             this.view.layout();
32669             if(this.view.adjustForScroll){
32670                 this.view.adjustForScroll();
32671             }
32672         }
32673     },
32674
32675     /**
32676      * Returns the grid's underlying element.
32677      * @return {Element} The element
32678      */
32679     getGridEl : function(){
32680         return this.container;
32681     },
32682
32683     // private for compatibility, overridden by editor grid
32684     stopEditing : function(){},
32685
32686     /**
32687      * Returns the grid's SelectionModel.
32688      * @return {SelectionModel}
32689      */
32690     getSelectionModel : function(){
32691         if(!this.selModel){
32692             this.selModel = new Roo.grid.RowSelectionModel();
32693         }
32694         return this.selModel;
32695     },
32696
32697     /**
32698      * Returns the grid's DataSource.
32699      * @return {DataSource}
32700      */
32701     getDataSource : function(){
32702         return this.dataSource;
32703     },
32704
32705     /**
32706      * Returns the grid's ColumnModel.
32707      * @return {ColumnModel}
32708      */
32709     getColumnModel : function(){
32710         return this.colModel;
32711     },
32712
32713     /**
32714      * Returns the grid's GridView object.
32715      * @return {GridView}
32716      */
32717     getView : function(){
32718         if(!this.view){
32719             this.view = new Roo.grid.GridView(this.viewConfig);
32720         }
32721         return this.view;
32722     },
32723     /**
32724      * Called to get grid's drag proxy text, by default returns this.ddText.
32725      * @return {String}
32726      */
32727     getDragDropText : function(){
32728         var count = this.selModel.getCount();
32729         return String.format(this.ddText, count, count == 1 ? '' : 's');
32730     }
32731 });
32732 /**
32733  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32734  * %0 is replaced with the number of selected rows.
32735  * @type String
32736  */
32737 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32738  * Based on:
32739  * Ext JS Library 1.1.1
32740  * Copyright(c) 2006-2007, Ext JS, LLC.
32741  *
32742  * Originally Released Under LGPL - original licence link has changed is not relivant.
32743  *
32744  * Fork - LGPL
32745  * <script type="text/javascript">
32746  */
32747  
32748 Roo.grid.AbstractGridView = function(){
32749         this.grid = null;
32750         
32751         this.events = {
32752             "beforerowremoved" : true,
32753             "beforerowsinserted" : true,
32754             "beforerefresh" : true,
32755             "rowremoved" : true,
32756             "rowsinserted" : true,
32757             "rowupdated" : true,
32758             "refresh" : true
32759         };
32760     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32761 };
32762
32763 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32764     rowClass : "x-grid-row",
32765     cellClass : "x-grid-cell",
32766     tdClass : "x-grid-td",
32767     hdClass : "x-grid-hd",
32768     splitClass : "x-grid-hd-split",
32769     
32770         init: function(grid){
32771         this.grid = grid;
32772                 var cid = this.grid.getGridEl().id;
32773         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32774         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32775         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32776         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32777         },
32778         
32779         getColumnRenderers : function(){
32780         var renderers = [];
32781         var cm = this.grid.colModel;
32782         var colCount = cm.getColumnCount();
32783         for(var i = 0; i < colCount; i++){
32784             renderers[i] = cm.getRenderer(i);
32785         }
32786         return renderers;
32787     },
32788     
32789     getColumnIds : function(){
32790         var ids = [];
32791         var cm = this.grid.colModel;
32792         var colCount = cm.getColumnCount();
32793         for(var i = 0; i < colCount; i++){
32794             ids[i] = cm.getColumnId(i);
32795         }
32796         return ids;
32797     },
32798     
32799     getDataIndexes : function(){
32800         if(!this.indexMap){
32801             this.indexMap = this.buildIndexMap();
32802         }
32803         return this.indexMap.colToData;
32804     },
32805     
32806     getColumnIndexByDataIndex : function(dataIndex){
32807         if(!this.indexMap){
32808             this.indexMap = this.buildIndexMap();
32809         }
32810         return this.indexMap.dataToCol[dataIndex];
32811     },
32812     
32813     /**
32814      * Set a css style for a column dynamically. 
32815      * @param {Number} colIndex The index of the column
32816      * @param {String} name The css property name
32817      * @param {String} value The css value
32818      */
32819     setCSSStyle : function(colIndex, name, value){
32820         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32821         Roo.util.CSS.updateRule(selector, name, value);
32822     },
32823     
32824     generateRules : function(cm){
32825         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32826         Roo.util.CSS.removeStyleSheet(rulesId);
32827         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32828             var cid = cm.getColumnId(i);
32829             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32830                          this.tdSelector, cid, " {\n}\n",
32831                          this.hdSelector, cid, " {\n}\n",
32832                          this.splitSelector, cid, " {\n}\n");
32833         }
32834         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32835     }
32836 });/*
32837  * Based on:
32838  * Ext JS Library 1.1.1
32839  * Copyright(c) 2006-2007, Ext JS, LLC.
32840  *
32841  * Originally Released Under LGPL - original licence link has changed is not relivant.
32842  *
32843  * Fork - LGPL
32844  * <script type="text/javascript">
32845  */
32846
32847 // private
32848 // This is a support class used internally by the Grid components
32849 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32850     this.grid = grid;
32851     this.view = grid.getView();
32852     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32853     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32854     if(hd2){
32855         this.setHandleElId(Roo.id(hd));
32856         this.setOuterHandleElId(Roo.id(hd2));
32857     }
32858     this.scroll = false;
32859 };
32860 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32861     maxDragWidth: 120,
32862     getDragData : function(e){
32863         var t = Roo.lib.Event.getTarget(e);
32864         var h = this.view.findHeaderCell(t);
32865         if(h){
32866             return {ddel: h.firstChild, header:h};
32867         }
32868         return false;
32869     },
32870
32871     onInitDrag : function(e){
32872         this.view.headersDisabled = true;
32873         var clone = this.dragData.ddel.cloneNode(true);
32874         clone.id = Roo.id();
32875         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32876         this.proxy.update(clone);
32877         return true;
32878     },
32879
32880     afterValidDrop : function(){
32881         var v = this.view;
32882         setTimeout(function(){
32883             v.headersDisabled = false;
32884         }, 50);
32885     },
32886
32887     afterInvalidDrop : function(){
32888         var v = this.view;
32889         setTimeout(function(){
32890             v.headersDisabled = false;
32891         }, 50);
32892     }
32893 });
32894 /*
32895  * Based on:
32896  * Ext JS Library 1.1.1
32897  * Copyright(c) 2006-2007, Ext JS, LLC.
32898  *
32899  * Originally Released Under LGPL - original licence link has changed is not relivant.
32900  *
32901  * Fork - LGPL
32902  * <script type="text/javascript">
32903  */
32904 // private
32905 // This is a support class used internally by the Grid components
32906 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32907     this.grid = grid;
32908     this.view = grid.getView();
32909     // split the proxies so they don't interfere with mouse events
32910     this.proxyTop = Roo.DomHelper.append(document.body, {
32911         cls:"col-move-top", html:"&#160;"
32912     }, true);
32913     this.proxyBottom = Roo.DomHelper.append(document.body, {
32914         cls:"col-move-bottom", html:"&#160;"
32915     }, true);
32916     this.proxyTop.hide = this.proxyBottom.hide = function(){
32917         this.setLeftTop(-100,-100);
32918         this.setStyle("visibility", "hidden");
32919     };
32920     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32921     // temporarily disabled
32922     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32923     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32924 };
32925 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32926     proxyOffsets : [-4, -9],
32927     fly: Roo.Element.fly,
32928
32929     getTargetFromEvent : function(e){
32930         var t = Roo.lib.Event.getTarget(e);
32931         var cindex = this.view.findCellIndex(t);
32932         if(cindex !== false){
32933             return this.view.getHeaderCell(cindex);
32934         }
32935         return null;
32936     },
32937
32938     nextVisible : function(h){
32939         var v = this.view, cm = this.grid.colModel;
32940         h = h.nextSibling;
32941         while(h){
32942             if(!cm.isHidden(v.getCellIndex(h))){
32943                 return h;
32944             }
32945             h = h.nextSibling;
32946         }
32947         return null;
32948     },
32949
32950     prevVisible : function(h){
32951         var v = this.view, cm = this.grid.colModel;
32952         h = h.prevSibling;
32953         while(h){
32954             if(!cm.isHidden(v.getCellIndex(h))){
32955                 return h;
32956             }
32957             h = h.prevSibling;
32958         }
32959         return null;
32960     },
32961
32962     positionIndicator : function(h, n, e){
32963         var x = Roo.lib.Event.getPageX(e);
32964         var r = Roo.lib.Dom.getRegion(n.firstChild);
32965         var px, pt, py = r.top + this.proxyOffsets[1];
32966         if((r.right - x) <= (r.right-r.left)/2){
32967             px = r.right+this.view.borderWidth;
32968             pt = "after";
32969         }else{
32970             px = r.left;
32971             pt = "before";
32972         }
32973         var oldIndex = this.view.getCellIndex(h);
32974         var newIndex = this.view.getCellIndex(n);
32975
32976         if(this.grid.colModel.isFixed(newIndex)){
32977             return false;
32978         }
32979
32980         var locked = this.grid.colModel.isLocked(newIndex);
32981
32982         if(pt == "after"){
32983             newIndex++;
32984         }
32985         if(oldIndex < newIndex){
32986             newIndex--;
32987         }
32988         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32989             return false;
32990         }
32991         px +=  this.proxyOffsets[0];
32992         this.proxyTop.setLeftTop(px, py);
32993         this.proxyTop.show();
32994         if(!this.bottomOffset){
32995             this.bottomOffset = this.view.mainHd.getHeight();
32996         }
32997         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32998         this.proxyBottom.show();
32999         return pt;
33000     },
33001
33002     onNodeEnter : function(n, dd, e, data){
33003         if(data.header != n){
33004             this.positionIndicator(data.header, n, e);
33005         }
33006     },
33007
33008     onNodeOver : function(n, dd, e, data){
33009         var result = false;
33010         if(data.header != n){
33011             result = this.positionIndicator(data.header, n, e);
33012         }
33013         if(!result){
33014             this.proxyTop.hide();
33015             this.proxyBottom.hide();
33016         }
33017         return result ? this.dropAllowed : this.dropNotAllowed;
33018     },
33019
33020     onNodeOut : function(n, dd, e, data){
33021         this.proxyTop.hide();
33022         this.proxyBottom.hide();
33023     },
33024
33025     onNodeDrop : function(n, dd, e, data){
33026         var h = data.header;
33027         if(h != n){
33028             var cm = this.grid.colModel;
33029             var x = Roo.lib.Event.getPageX(e);
33030             var r = Roo.lib.Dom.getRegion(n.firstChild);
33031             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33032             var oldIndex = this.view.getCellIndex(h);
33033             var newIndex = this.view.getCellIndex(n);
33034             var locked = cm.isLocked(newIndex);
33035             if(pt == "after"){
33036                 newIndex++;
33037             }
33038             if(oldIndex < newIndex){
33039                 newIndex--;
33040             }
33041             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33042                 return false;
33043             }
33044             cm.setLocked(oldIndex, locked, true);
33045             cm.moveColumn(oldIndex, newIndex);
33046             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33047             return true;
33048         }
33049         return false;
33050     }
33051 });
33052 /*
33053  * Based on:
33054  * Ext JS Library 1.1.1
33055  * Copyright(c) 2006-2007, Ext JS, LLC.
33056  *
33057  * Originally Released Under LGPL - original licence link has changed is not relivant.
33058  *
33059  * Fork - LGPL
33060  * <script type="text/javascript">
33061  */
33062   
33063 /**
33064  * @class Roo.grid.GridView
33065  * @extends Roo.util.Observable
33066  *
33067  * @constructor
33068  * @param {Object} config
33069  */
33070 Roo.grid.GridView = function(config){
33071     Roo.grid.GridView.superclass.constructor.call(this);
33072     this.el = null;
33073
33074     Roo.apply(this, config);
33075 };
33076
33077 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33078
33079     /**
33080      * Override this function to apply custom css classes to rows during rendering
33081      * @param {Record} record The record
33082      * @param {Number} index
33083      * @method getRowClass
33084      */
33085     rowClass : "x-grid-row",
33086
33087     cellClass : "x-grid-col",
33088
33089     tdClass : "x-grid-td",
33090
33091     hdClass : "x-grid-hd",
33092
33093     splitClass : "x-grid-split",
33094
33095     sortClasses : ["sort-asc", "sort-desc"],
33096
33097     enableMoveAnim : false,
33098
33099     hlColor: "C3DAF9",
33100
33101     dh : Roo.DomHelper,
33102
33103     fly : Roo.Element.fly,
33104
33105     css : Roo.util.CSS,
33106
33107     borderWidth: 1,
33108
33109     splitOffset: 3,
33110
33111     scrollIncrement : 22,
33112
33113     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33114
33115     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33116
33117     bind : function(ds, cm){
33118         if(this.ds){
33119             this.ds.un("load", this.onLoad, this);
33120             this.ds.un("datachanged", this.onDataChange, this);
33121             this.ds.un("add", this.onAdd, this);
33122             this.ds.un("remove", this.onRemove, this);
33123             this.ds.un("update", this.onUpdate, this);
33124             this.ds.un("clear", this.onClear, this);
33125         }
33126         if(ds){
33127             ds.on("load", this.onLoad, this);
33128             ds.on("datachanged", this.onDataChange, this);
33129             ds.on("add", this.onAdd, this);
33130             ds.on("remove", this.onRemove, this);
33131             ds.on("update", this.onUpdate, this);
33132             ds.on("clear", this.onClear, this);
33133         }
33134         this.ds = ds;
33135
33136         if(this.cm){
33137             this.cm.un("widthchange", this.onColWidthChange, this);
33138             this.cm.un("headerchange", this.onHeaderChange, this);
33139             this.cm.un("hiddenchange", this.onHiddenChange, this);
33140             this.cm.un("columnmoved", this.onColumnMove, this);
33141             this.cm.un("columnlockchange", this.onColumnLock, this);
33142         }
33143         if(cm){
33144             this.generateRules(cm);
33145             cm.on("widthchange", this.onColWidthChange, this);
33146             cm.on("headerchange", this.onHeaderChange, this);
33147             cm.on("hiddenchange", this.onHiddenChange, this);
33148             cm.on("columnmoved", this.onColumnMove, this);
33149             cm.on("columnlockchange", this.onColumnLock, this);
33150         }
33151         this.cm = cm;
33152     },
33153
33154     init: function(grid){
33155         Roo.grid.GridView.superclass.init.call(this, grid);
33156
33157         this.bind(grid.dataSource, grid.colModel);
33158
33159         grid.on("headerclick", this.handleHeaderClick, this);
33160
33161         if(grid.trackMouseOver){
33162             grid.on("mouseover", this.onRowOver, this);
33163             grid.on("mouseout", this.onRowOut, this);
33164         }
33165         grid.cancelTextSelection = function(){};
33166         this.gridId = grid.id;
33167
33168         var tpls = this.templates || {};
33169
33170         if(!tpls.master){
33171             tpls.master = new Roo.Template(
33172                '<div class="x-grid" hidefocus="true">',
33173                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33174                   '<div class="x-grid-topbar"></div>',
33175                   '<div class="x-grid-scroller"><div></div></div>',
33176                   '<div class="x-grid-locked">',
33177                       '<div class="x-grid-header">{lockedHeader}</div>',
33178                       '<div class="x-grid-body">{lockedBody}</div>',
33179                   "</div>",
33180                   '<div class="x-grid-viewport">',
33181                       '<div class="x-grid-header">{header}</div>',
33182                       '<div class="x-grid-body">{body}</div>',
33183                   "</div>",
33184                   '<div class="x-grid-bottombar"></div>',
33185                  
33186                   '<div class="x-grid-resize-proxy">&#160;</div>',
33187                "</div>"
33188             );
33189             tpls.master.disableformats = true;
33190         }
33191
33192         if(!tpls.header){
33193             tpls.header = new Roo.Template(
33194                '<table border="0" cellspacing="0" cellpadding="0">',
33195                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33196                "</table>{splits}"
33197             );
33198             tpls.header.disableformats = true;
33199         }
33200         tpls.header.compile();
33201
33202         if(!tpls.hcell){
33203             tpls.hcell = new Roo.Template(
33204                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33205                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33206                 "</div></td>"
33207              );
33208              tpls.hcell.disableFormats = true;
33209         }
33210         tpls.hcell.compile();
33211
33212         if(!tpls.hsplit){
33213             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
33214             tpls.hsplit.disableFormats = true;
33215         }
33216         tpls.hsplit.compile();
33217
33218         if(!tpls.body){
33219             tpls.body = new Roo.Template(
33220                '<table border="0" cellspacing="0" cellpadding="0">',
33221                "<tbody>{rows}</tbody>",
33222                "</table>"
33223             );
33224             tpls.body.disableFormats = true;
33225         }
33226         tpls.body.compile();
33227
33228         if(!tpls.row){
33229             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33230             tpls.row.disableFormats = true;
33231         }
33232         tpls.row.compile();
33233
33234         if(!tpls.cell){
33235             tpls.cell = new Roo.Template(
33236                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33237                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
33238                 "</td>"
33239             );
33240             tpls.cell.disableFormats = true;
33241         }
33242         tpls.cell.compile();
33243
33244         this.templates = tpls;
33245     },
33246
33247     // remap these for backwards compat
33248     onColWidthChange : function(){
33249         this.updateColumns.apply(this, arguments);
33250     },
33251     onHeaderChange : function(){
33252         this.updateHeaders.apply(this, arguments);
33253     }, 
33254     onHiddenChange : function(){
33255         this.handleHiddenChange.apply(this, arguments);
33256     },
33257     onColumnMove : function(){
33258         this.handleColumnMove.apply(this, arguments);
33259     },
33260     onColumnLock : function(){
33261         this.handleLockChange.apply(this, arguments);
33262     },
33263
33264     onDataChange : function(){
33265         this.refresh();
33266         this.updateHeaderSortState();
33267     },
33268
33269     onClear : function(){
33270         this.refresh();
33271     },
33272
33273     onUpdate : function(ds, record){
33274         this.refreshRow(record);
33275     },
33276
33277     refreshRow : function(record){
33278         var ds = this.ds, index;
33279         if(typeof record == 'number'){
33280             index = record;
33281             record = ds.getAt(index);
33282         }else{
33283             index = ds.indexOf(record);
33284         }
33285         this.insertRows(ds, index, index, true);
33286         this.onRemove(ds, record, index+1, true);
33287         this.syncRowHeights(index, index);
33288         this.layout();
33289         this.fireEvent("rowupdated", this, index, record);
33290     },
33291
33292     onAdd : function(ds, records, index){
33293         this.insertRows(ds, index, index + (records.length-1));
33294     },
33295
33296     onRemove : function(ds, record, index, isUpdate){
33297         if(isUpdate !== true){
33298             this.fireEvent("beforerowremoved", this, index, record);
33299         }
33300         var bt = this.getBodyTable(), lt = this.getLockedTable();
33301         if(bt.rows[index]){
33302             bt.firstChild.removeChild(bt.rows[index]);
33303         }
33304         if(lt.rows[index]){
33305             lt.firstChild.removeChild(lt.rows[index]);
33306         }
33307         if(isUpdate !== true){
33308             this.stripeRows(index);
33309             this.syncRowHeights(index, index);
33310             this.layout();
33311             this.fireEvent("rowremoved", this, index, record);
33312         }
33313     },
33314
33315     onLoad : function(){
33316         this.scrollToTop();
33317     },
33318
33319     /**
33320      * Scrolls the grid to the top
33321      */
33322     scrollToTop : function(){
33323         if(this.scroller){
33324             this.scroller.dom.scrollTop = 0;
33325             this.syncScroll();
33326         }
33327     },
33328
33329     /**
33330      * Gets a panel in the header of the grid that can be used for toolbars etc.
33331      * After modifying the contents of this panel a call to grid.autoSize() may be
33332      * required to register any changes in size.
33333      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33334      * @return Roo.Element
33335      */
33336     getHeaderPanel : function(doShow){
33337         if(doShow){
33338             this.headerPanel.show();
33339         }
33340         return this.headerPanel;
33341     },
33342
33343     /**
33344      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33345      * After modifying the contents of this panel a call to grid.autoSize() may be
33346      * required to register any changes in size.
33347      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33348      * @return Roo.Element
33349      */
33350     getFooterPanel : function(doShow){
33351         if(doShow){
33352             this.footerPanel.show();
33353         }
33354         return this.footerPanel;
33355     },
33356
33357     initElements : function(){
33358         var E = Roo.Element;
33359         var el = this.grid.getGridEl().dom.firstChild;
33360         var cs = el.childNodes;
33361
33362         this.el = new E(el);
33363         
33364          this.focusEl = new E(el.firstChild);
33365         this.focusEl.swallowEvent("click", true);
33366         
33367         this.headerPanel = new E(cs[1]);
33368         this.headerPanel.enableDisplayMode("block");
33369
33370         this.scroller = new E(cs[2]);
33371         this.scrollSizer = new E(this.scroller.dom.firstChild);
33372
33373         this.lockedWrap = new E(cs[3]);
33374         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33375         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33376
33377         this.mainWrap = new E(cs[4]);
33378         this.mainHd = new E(this.mainWrap.dom.firstChild);
33379         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33380
33381         this.footerPanel = new E(cs[5]);
33382         this.footerPanel.enableDisplayMode("block");
33383
33384         this.resizeProxy = new E(cs[6]);
33385
33386         this.headerSelector = String.format(
33387            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33388            this.lockedHd.id, this.mainHd.id
33389         );
33390
33391         this.splitterSelector = String.format(
33392            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33393            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33394         );
33395     },
33396     idToCssName : function(s)
33397     {
33398         return s.replace(/[^a-z0-9]+/ig, '-');
33399     },
33400
33401     getHeaderCell : function(index){
33402         return Roo.DomQuery.select(this.headerSelector)[index];
33403     },
33404
33405     getHeaderCellMeasure : function(index){
33406         return this.getHeaderCell(index).firstChild;
33407     },
33408
33409     getHeaderCellText : function(index){
33410         return this.getHeaderCell(index).firstChild.firstChild;
33411     },
33412
33413     getLockedTable : function(){
33414         return this.lockedBody.dom.firstChild;
33415     },
33416
33417     getBodyTable : function(){
33418         return this.mainBody.dom.firstChild;
33419     },
33420
33421     getLockedRow : function(index){
33422         return this.getLockedTable().rows[index];
33423     },
33424
33425     getRow : function(index){
33426         return this.getBodyTable().rows[index];
33427     },
33428
33429     getRowComposite : function(index){
33430         if(!this.rowEl){
33431             this.rowEl = new Roo.CompositeElementLite();
33432         }
33433         var els = [], lrow, mrow;
33434         if(lrow = this.getLockedRow(index)){
33435             els.push(lrow);
33436         }
33437         if(mrow = this.getRow(index)){
33438             els.push(mrow);
33439         }
33440         this.rowEl.elements = els;
33441         return this.rowEl;
33442     },
33443
33444     getCell : function(rowIndex, colIndex){
33445         var locked = this.cm.getLockedCount();
33446         var source;
33447         if(colIndex < locked){
33448             source = this.lockedBody.dom.firstChild;
33449         }else{
33450             source = this.mainBody.dom.firstChild;
33451             colIndex -= locked;
33452         }
33453         return source.rows[rowIndex].childNodes[colIndex];
33454     },
33455
33456     getCellText : function(rowIndex, colIndex){
33457         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33458     },
33459
33460     getCellBox : function(cell){
33461         var b = this.fly(cell).getBox();
33462         if(Roo.isOpera){ // opera fails to report the Y
33463             b.y = cell.offsetTop + this.mainBody.getY();
33464         }
33465         return b;
33466     },
33467
33468     getCellIndex : function(cell){
33469         var id = String(cell.className).match(this.cellRE);
33470         if(id){
33471             return parseInt(id[1], 10);
33472         }
33473         return 0;
33474     },
33475
33476     findHeaderIndex : function(n){
33477         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33478         return r ? this.getCellIndex(r) : false;
33479     },
33480
33481     findHeaderCell : function(n){
33482         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33483         return r ? r : false;
33484     },
33485
33486     findRowIndex : function(n){
33487         if(!n){
33488             return false;
33489         }
33490         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33491         return r ? r.rowIndex : false;
33492     },
33493
33494     findCellIndex : function(node){
33495         var stop = this.el.dom;
33496         while(node && node != stop){
33497             if(this.findRE.test(node.className)){
33498                 return this.getCellIndex(node);
33499             }
33500             node = node.parentNode;
33501         }
33502         return false;
33503     },
33504
33505     getColumnId : function(index){
33506         return this.cm.getColumnId(index);
33507     },
33508
33509     getSplitters : function()
33510     {
33511         if(this.splitterSelector){
33512            return Roo.DomQuery.select(this.splitterSelector);
33513         }else{
33514             return null;
33515       }
33516     },
33517
33518     getSplitter : function(index){
33519         return this.getSplitters()[index];
33520     },
33521
33522     onRowOver : function(e, t){
33523         var row;
33524         if((row = this.findRowIndex(t)) !== false){
33525             this.getRowComposite(row).addClass("x-grid-row-over");
33526         }
33527     },
33528
33529     onRowOut : function(e, t){
33530         var row;
33531         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33532             this.getRowComposite(row).removeClass("x-grid-row-over");
33533         }
33534     },
33535
33536     renderHeaders : function(){
33537         var cm = this.cm;
33538         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33539         var cb = [], lb = [], sb = [], lsb = [], p = {};
33540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33541             p.cellId = "x-grid-hd-0-" + i;
33542             p.splitId = "x-grid-csplit-0-" + i;
33543             p.id = cm.getColumnId(i);
33544             p.title = cm.getColumnTooltip(i) || "";
33545             p.value = cm.getColumnHeader(i) || "";
33546             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33547             if(!cm.isLocked(i)){
33548                 cb[cb.length] = ct.apply(p);
33549                 sb[sb.length] = st.apply(p);
33550             }else{
33551                 lb[lb.length] = ct.apply(p);
33552                 lsb[lsb.length] = st.apply(p);
33553             }
33554         }
33555         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33556                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33557     },
33558
33559     updateHeaders : function(){
33560         var html = this.renderHeaders();
33561         this.lockedHd.update(html[0]);
33562         this.mainHd.update(html[1]);
33563     },
33564
33565     /**
33566      * Focuses the specified row.
33567      * @param {Number} row The row index
33568      */
33569     focusRow : function(row)
33570     {
33571         //Roo.log('GridView.focusRow');
33572         var x = this.scroller.dom.scrollLeft;
33573         this.focusCell(row, 0, false);
33574         this.scroller.dom.scrollLeft = x;
33575     },
33576
33577     /**
33578      * Focuses the specified cell.
33579      * @param {Number} row The row index
33580      * @param {Number} col The column index
33581      * @param {Boolean} hscroll false to disable horizontal scrolling
33582      */
33583     focusCell : function(row, col, hscroll)
33584     {
33585         //Roo.log('GridView.focusCell');
33586         var el = this.ensureVisible(row, col, hscroll);
33587         this.focusEl.alignTo(el, "tl-tl");
33588         if(Roo.isGecko){
33589             this.focusEl.focus();
33590         }else{
33591             this.focusEl.focus.defer(1, this.focusEl);
33592         }
33593     },
33594
33595     /**
33596      * Scrolls the specified cell into view
33597      * @param {Number} row The row index
33598      * @param {Number} col The column index
33599      * @param {Boolean} hscroll false to disable horizontal scrolling
33600      */
33601     ensureVisible : function(row, col, hscroll)
33602     {
33603         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33604         //return null; //disable for testing.
33605         if(typeof row != "number"){
33606             row = row.rowIndex;
33607         }
33608         if(row < 0 && row >= this.ds.getCount()){
33609             return  null;
33610         }
33611         col = (col !== undefined ? col : 0);
33612         var cm = this.grid.colModel;
33613         while(cm.isHidden(col)){
33614             col++;
33615         }
33616
33617         var el = this.getCell(row, col);
33618         if(!el){
33619             return null;
33620         }
33621         var c = this.scroller.dom;
33622
33623         var ctop = parseInt(el.offsetTop, 10);
33624         var cleft = parseInt(el.offsetLeft, 10);
33625         var cbot = ctop + el.offsetHeight;
33626         var cright = cleft + el.offsetWidth;
33627         
33628         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33629         var stop = parseInt(c.scrollTop, 10);
33630         var sleft = parseInt(c.scrollLeft, 10);
33631         var sbot = stop + ch;
33632         var sright = sleft + c.clientWidth;
33633         /*
33634         Roo.log('GridView.ensureVisible:' +
33635                 ' ctop:' + ctop +
33636                 ' c.clientHeight:' + c.clientHeight +
33637                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33638                 ' stop:' + stop +
33639                 ' cbot:' + cbot +
33640                 ' sbot:' + sbot +
33641                 ' ch:' + ch  
33642                 );
33643         */
33644         if(ctop < stop){
33645              c.scrollTop = ctop;
33646             //Roo.log("set scrolltop to ctop DISABLE?");
33647         }else if(cbot > sbot){
33648             //Roo.log("set scrolltop to cbot-ch");
33649             c.scrollTop = cbot-ch;
33650         }
33651         
33652         if(hscroll !== false){
33653             if(cleft < sleft){
33654                 c.scrollLeft = cleft;
33655             }else if(cright > sright){
33656                 c.scrollLeft = cright-c.clientWidth;
33657             }
33658         }
33659          
33660         return el;
33661     },
33662
33663     updateColumns : function(){
33664         this.grid.stopEditing();
33665         var cm = this.grid.colModel, colIds = this.getColumnIds();
33666         //var totalWidth = cm.getTotalWidth();
33667         var pos = 0;
33668         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33669             //if(cm.isHidden(i)) continue;
33670             var w = cm.getColumnWidth(i);
33671             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33672             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33673         }
33674         this.updateSplitters();
33675     },
33676
33677     generateRules : function(cm){
33678         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33679         Roo.util.CSS.removeStyleSheet(rulesId);
33680         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33681             var cid = cm.getColumnId(i);
33682             var align = '';
33683             if(cm.config[i].align){
33684                 align = 'text-align:'+cm.config[i].align+';';
33685             }
33686             var hidden = '';
33687             if(cm.isHidden(i)){
33688                 hidden = 'display:none;';
33689             }
33690             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33691             ruleBuf.push(
33692                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33693                     this.hdSelector, cid, " {\n", align, width, "}\n",
33694                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33695                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33696         }
33697         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33698     },
33699
33700     updateSplitters : function(){
33701         var cm = this.cm, s = this.getSplitters();
33702         if(s){ // splitters not created yet
33703             var pos = 0, locked = true;
33704             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33705                 if(cm.isHidden(i)) continue;
33706                 var w = cm.getColumnWidth(i); // make sure it's a number
33707                 if(!cm.isLocked(i) && locked){
33708                     pos = 0;
33709                     locked = false;
33710                 }
33711                 pos += w;
33712                 s[i].style.left = (pos-this.splitOffset) + "px";
33713             }
33714         }
33715     },
33716
33717     handleHiddenChange : function(colModel, colIndex, hidden){
33718         if(hidden){
33719             this.hideColumn(colIndex);
33720         }else{
33721             this.unhideColumn(colIndex);
33722         }
33723     },
33724
33725     hideColumn : function(colIndex){
33726         var cid = this.getColumnId(colIndex);
33727         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33728         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33729         if(Roo.isSafari){
33730             this.updateHeaders();
33731         }
33732         this.updateSplitters();
33733         this.layout();
33734     },
33735
33736     unhideColumn : function(colIndex){
33737         var cid = this.getColumnId(colIndex);
33738         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33739         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33740
33741         if(Roo.isSafari){
33742             this.updateHeaders();
33743         }
33744         this.updateSplitters();
33745         this.layout();
33746     },
33747
33748     insertRows : function(dm, firstRow, lastRow, isUpdate){
33749         if(firstRow == 0 && lastRow == dm.getCount()-1){
33750             this.refresh();
33751         }else{
33752             if(!isUpdate){
33753                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33754             }
33755             var s = this.getScrollState();
33756             var markup = this.renderRows(firstRow, lastRow);
33757             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33758             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33759             this.restoreScroll(s);
33760             if(!isUpdate){
33761                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33762                 this.syncRowHeights(firstRow, lastRow);
33763                 this.stripeRows(firstRow);
33764                 this.layout();
33765             }
33766         }
33767     },
33768
33769     bufferRows : function(markup, target, index){
33770         var before = null, trows = target.rows, tbody = target.tBodies[0];
33771         if(index < trows.length){
33772             before = trows[index];
33773         }
33774         var b = document.createElement("div");
33775         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33776         var rows = b.firstChild.rows;
33777         for(var i = 0, len = rows.length; i < len; i++){
33778             if(before){
33779                 tbody.insertBefore(rows[0], before);
33780             }else{
33781                 tbody.appendChild(rows[0]);
33782             }
33783         }
33784         b.innerHTML = "";
33785         b = null;
33786     },
33787
33788     deleteRows : function(dm, firstRow, lastRow){
33789         if(dm.getRowCount()<1){
33790             this.fireEvent("beforerefresh", this);
33791             this.mainBody.update("");
33792             this.lockedBody.update("");
33793             this.fireEvent("refresh", this);
33794         }else{
33795             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33796             var bt = this.getBodyTable();
33797             var tbody = bt.firstChild;
33798             var rows = bt.rows;
33799             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33800                 tbody.removeChild(rows[firstRow]);
33801             }
33802             this.stripeRows(firstRow);
33803             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33804         }
33805     },
33806
33807     updateRows : function(dataSource, firstRow, lastRow){
33808         var s = this.getScrollState();
33809         this.refresh();
33810         this.restoreScroll(s);
33811     },
33812
33813     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33814         if(!noRefresh){
33815            this.refresh();
33816         }
33817         this.updateHeaderSortState();
33818     },
33819
33820     getScrollState : function(){
33821         
33822         var sb = this.scroller.dom;
33823         return {left: sb.scrollLeft, top: sb.scrollTop};
33824     },
33825
33826     stripeRows : function(startRow){
33827         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33828             return;
33829         }
33830         startRow = startRow || 0;
33831         var rows = this.getBodyTable().rows;
33832         var lrows = this.getLockedTable().rows;
33833         var cls = ' x-grid-row-alt ';
33834         for(var i = startRow, len = rows.length; i < len; i++){
33835             var row = rows[i], lrow = lrows[i];
33836             var isAlt = ((i+1) % 2 == 0);
33837             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33838             if(isAlt == hasAlt){
33839                 continue;
33840             }
33841             if(isAlt){
33842                 row.className += " x-grid-row-alt";
33843             }else{
33844                 row.className = row.className.replace("x-grid-row-alt", "");
33845             }
33846             if(lrow){
33847                 lrow.className = row.className;
33848             }
33849         }
33850     },
33851
33852     restoreScroll : function(state){
33853         //Roo.log('GridView.restoreScroll');
33854         var sb = this.scroller.dom;
33855         sb.scrollLeft = state.left;
33856         sb.scrollTop = state.top;
33857         this.syncScroll();
33858     },
33859
33860     syncScroll : function(){
33861         //Roo.log('GridView.syncScroll');
33862         var sb = this.scroller.dom;
33863         var sh = this.mainHd.dom;
33864         var bs = this.mainBody.dom;
33865         var lv = this.lockedBody.dom;
33866         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33867         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33868     },
33869
33870     handleScroll : function(e){
33871         this.syncScroll();
33872         var sb = this.scroller.dom;
33873         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33874         e.stopEvent();
33875     },
33876
33877     handleWheel : function(e){
33878         var d = e.getWheelDelta();
33879         this.scroller.dom.scrollTop -= d*22;
33880         // set this here to prevent jumpy scrolling on large tables
33881         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33882         e.stopEvent();
33883     },
33884
33885     renderRows : function(startRow, endRow){
33886         // pull in all the crap needed to render rows
33887         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33888         var colCount = cm.getColumnCount();
33889
33890         if(ds.getCount() < 1){
33891             return ["", ""];
33892         }
33893
33894         // build a map for all the columns
33895         var cs = [];
33896         for(var i = 0; i < colCount; i++){
33897             var name = cm.getDataIndex(i);
33898             cs[i] = {
33899                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33900                 renderer : cm.getRenderer(i),
33901                 id : cm.getColumnId(i),
33902                 locked : cm.isLocked(i)
33903             };
33904         }
33905
33906         startRow = startRow || 0;
33907         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33908
33909         // records to render
33910         var rs = ds.getRange(startRow, endRow);
33911
33912         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33913     },
33914
33915     // As much as I hate to duplicate code, this was branched because FireFox really hates
33916     // [].join("") on strings. The performance difference was substantial enough to
33917     // branch this function
33918     doRender : Roo.isGecko ?
33919             function(cs, rs, ds, startRow, colCount, stripe){
33920                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33921                 // buffers
33922                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33923                 
33924                 var hasListener = this.grid.hasListener('rowclass');
33925                 var rowcfg = {};
33926                 for(var j = 0, len = rs.length; j < len; j++){
33927                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33928                     for(var i = 0; i < colCount; i++){
33929                         c = cs[i];
33930                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33931                         p.id = c.id;
33932                         p.css = p.attr = "";
33933                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33934                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33935                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33936                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33937                         }
33938                         var markup = ct.apply(p);
33939                         if(!c.locked){
33940                             cb+= markup;
33941                         }else{
33942                             lcb+= markup;
33943                         }
33944                     }
33945                     var alt = [];
33946                     if(stripe && ((rowIndex+1) % 2 == 0)){
33947                         alt.push("x-grid-row-alt")
33948                     }
33949                     if(r.dirty){
33950                         alt.push(  " x-grid-dirty-row");
33951                     }
33952                     rp.cells = lcb;
33953                     if(this.getRowClass){
33954                         alt.push(this.getRowClass(r, rowIndex));
33955                     }
33956                     if (hasListener) {
33957                         rowcfg = {
33958                              
33959                             record: r,
33960                             rowIndex : rowIndex,
33961                             rowClass : ''
33962                         }
33963                         this.grid.fireEvent('rowclass', this, rowcfg);
33964                         alt.push(rowcfg.rowClass);
33965                     }
33966                     rp.alt = alt.join(" ");
33967                     lbuf+= rt.apply(rp);
33968                     rp.cells = cb;
33969                     buf+=  rt.apply(rp);
33970                 }
33971                 return [lbuf, buf];
33972             } :
33973             function(cs, rs, ds, startRow, colCount, stripe){
33974                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33975                 // buffers
33976                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33977                 var hasListener = this.grid.hasListener('rowclass');
33978                 var rowcfg = {};
33979                 for(var j = 0, len = rs.length; j < len; j++){
33980                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33981                     for(var i = 0; i < colCount; i++){
33982                         c = cs[i];
33983                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33984                         p.id = c.id;
33985                         p.css = p.attr = "";
33986                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33987                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33988                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33989                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33990                         }
33991                         var markup = ct.apply(p);
33992                         if(!c.locked){
33993                             cb[cb.length] = markup;
33994                         }else{
33995                             lcb[lcb.length] = markup;
33996                         }
33997                     }
33998                     var alt = [];
33999                     if(stripe && ((rowIndex+1) % 2 == 0)){
34000                         alt.push( "x-grid-row-alt");
34001                     }
34002                     if(r.dirty){
34003                         alt.push(" x-grid-dirty-row");
34004                     }
34005                     rp.cells = lcb;
34006                     if(this.getRowClass){
34007                         alt.push( this.getRowClass(r, rowIndex));
34008                     }
34009                     if (hasListener) {
34010                         rowcfg = {
34011                              
34012                             record: r,
34013                             rowIndex : rowIndex,
34014                             rowClass : ''
34015                         }
34016                         this.grid.fireEvent('rowclass', this, rowcfg);
34017                         alt.push(rowcfg.rowClass);
34018                     }
34019                     rp.alt = alt.join(" ");
34020                     rp.cells = lcb.join("");
34021                     lbuf[lbuf.length] = rt.apply(rp);
34022                     rp.cells = cb.join("");
34023                     buf[buf.length] =  rt.apply(rp);
34024                 }
34025                 return [lbuf.join(""), buf.join("")];
34026             },
34027
34028     renderBody : function(){
34029         var markup = this.renderRows();
34030         var bt = this.templates.body;
34031         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34032     },
34033
34034     /**
34035      * Refreshes the grid
34036      * @param {Boolean} headersToo
34037      */
34038     refresh : function(headersToo){
34039         this.fireEvent("beforerefresh", this);
34040         this.grid.stopEditing();
34041         var result = this.renderBody();
34042         this.lockedBody.update(result[0]);
34043         this.mainBody.update(result[1]);
34044         if(headersToo === true){
34045             this.updateHeaders();
34046             this.updateColumns();
34047             this.updateSplitters();
34048             this.updateHeaderSortState();
34049         }
34050         this.syncRowHeights();
34051         this.layout();
34052         this.fireEvent("refresh", this);
34053     },
34054
34055     handleColumnMove : function(cm, oldIndex, newIndex){
34056         this.indexMap = null;
34057         var s = this.getScrollState();
34058         this.refresh(true);
34059         this.restoreScroll(s);
34060         this.afterMove(newIndex);
34061     },
34062
34063     afterMove : function(colIndex){
34064         if(this.enableMoveAnim && Roo.enableFx){
34065             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34066         }
34067         // if multisort - fix sortOrder, and reload..
34068         if (this.grid.dataSource.multiSort) {
34069             // the we can call sort again..
34070             var dm = this.grid.dataSource;
34071             var cm = this.grid.colModel;
34072             var so = [];
34073             for(var i = 0; i < cm.config.length; i++ ) {
34074                 
34075                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34076                     continue; // dont' bother, it's not in sort list or being set.
34077                 }
34078                 
34079                 so.push(cm.config[i].dataIndex);
34080             };
34081             dm.sortOrder = so;
34082             dm.load(dm.lastOptions);
34083             
34084             
34085         }
34086         
34087     },
34088
34089     updateCell : function(dm, rowIndex, dataIndex){
34090         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34091         if(typeof colIndex == "undefined"){ // not present in grid
34092             return;
34093         }
34094         var cm = this.grid.colModel;
34095         var cell = this.getCell(rowIndex, colIndex);
34096         var cellText = this.getCellText(rowIndex, colIndex);
34097
34098         var p = {
34099             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34100             id : cm.getColumnId(colIndex),
34101             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34102         };
34103         var renderer = cm.getRenderer(colIndex);
34104         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34105         if(typeof val == "undefined" || val === "") val = "&#160;";
34106         cellText.innerHTML = val;
34107         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34108         this.syncRowHeights(rowIndex, rowIndex);
34109     },
34110
34111     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34112         var maxWidth = 0;
34113         if(this.grid.autoSizeHeaders){
34114             var h = this.getHeaderCellMeasure(colIndex);
34115             maxWidth = Math.max(maxWidth, h.scrollWidth);
34116         }
34117         var tb, index;
34118         if(this.cm.isLocked(colIndex)){
34119             tb = this.getLockedTable();
34120             index = colIndex;
34121         }else{
34122             tb = this.getBodyTable();
34123             index = colIndex - this.cm.getLockedCount();
34124         }
34125         if(tb && tb.rows){
34126             var rows = tb.rows;
34127             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34128             for(var i = 0; i < stopIndex; i++){
34129                 var cell = rows[i].childNodes[index].firstChild;
34130                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34131             }
34132         }
34133         return maxWidth + /*margin for error in IE*/ 5;
34134     },
34135     /**
34136      * Autofit a column to its content.
34137      * @param {Number} colIndex
34138      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34139      */
34140      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34141          if(this.cm.isHidden(colIndex)){
34142              return; // can't calc a hidden column
34143          }
34144         if(forceMinSize){
34145             var cid = this.cm.getColumnId(colIndex);
34146             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34147            if(this.grid.autoSizeHeaders){
34148                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34149            }
34150         }
34151         var newWidth = this.calcColumnWidth(colIndex);
34152         this.cm.setColumnWidth(colIndex,
34153             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34154         if(!suppressEvent){
34155             this.grid.fireEvent("columnresize", colIndex, newWidth);
34156         }
34157     },
34158
34159     /**
34160      * Autofits all columns to their content and then expands to fit any extra space in the grid
34161      */
34162      autoSizeColumns : function(){
34163         var cm = this.grid.colModel;
34164         var colCount = cm.getColumnCount();
34165         for(var i = 0; i < colCount; i++){
34166             this.autoSizeColumn(i, true, true);
34167         }
34168         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34169             this.fitColumns();
34170         }else{
34171             this.updateColumns();
34172             this.layout();
34173         }
34174     },
34175
34176     /**
34177      * Autofits all columns to the grid's width proportionate with their current size
34178      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34179      */
34180     fitColumns : function(reserveScrollSpace){
34181         var cm = this.grid.colModel;
34182         var colCount = cm.getColumnCount();
34183         var cols = [];
34184         var width = 0;
34185         var i, w;
34186         for (i = 0; i < colCount; i++){
34187             if(!cm.isHidden(i) && !cm.isFixed(i)){
34188                 w = cm.getColumnWidth(i);
34189                 cols.push(i);
34190                 cols.push(w);
34191                 width += w;
34192             }
34193         }
34194         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34195         if(reserveScrollSpace){
34196             avail -= 17;
34197         }
34198         var frac = (avail - cm.getTotalWidth())/width;
34199         while (cols.length){
34200             w = cols.pop();
34201             i = cols.pop();
34202             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34203         }
34204         this.updateColumns();
34205         this.layout();
34206     },
34207
34208     onRowSelect : function(rowIndex){
34209         var row = this.getRowComposite(rowIndex);
34210         row.addClass("x-grid-row-selected");
34211     },
34212
34213     onRowDeselect : function(rowIndex){
34214         var row = this.getRowComposite(rowIndex);
34215         row.removeClass("x-grid-row-selected");
34216     },
34217
34218     onCellSelect : function(row, col){
34219         var cell = this.getCell(row, col);
34220         if(cell){
34221             Roo.fly(cell).addClass("x-grid-cell-selected");
34222         }
34223     },
34224
34225     onCellDeselect : function(row, col){
34226         var cell = this.getCell(row, col);
34227         if(cell){
34228             Roo.fly(cell).removeClass("x-grid-cell-selected");
34229         }
34230     },
34231
34232     updateHeaderSortState : function(){
34233         
34234         // sort state can be single { field: xxx, direction : yyy}
34235         // or   { xxx=>ASC , yyy : DESC ..... }
34236         
34237         var mstate = {};
34238         if (!this.ds.multiSort) { 
34239             var state = this.ds.getSortState();
34240             if(!state){
34241                 return;
34242             }
34243             mstate[state.field] = state.direction;
34244             // FIXME... - this is not used here.. but might be elsewhere..
34245             this.sortState = state;
34246             
34247         } else {
34248             mstate = this.ds.sortToggle;
34249         }
34250         //remove existing sort classes..
34251         
34252         var sc = this.sortClasses;
34253         var hds = this.el.select(this.headerSelector).removeClass(sc);
34254         
34255         for(var f in mstate) {
34256         
34257             var sortColumn = this.cm.findColumnIndex(f);
34258             
34259             if(sortColumn != -1){
34260                 var sortDir = mstate[f];        
34261                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34262             }
34263         }
34264         
34265          
34266         
34267     },
34268
34269
34270     handleHeaderClick : function(g, index){
34271         if(this.headersDisabled){
34272             return;
34273         }
34274         var dm = g.dataSource, cm = g.colModel;
34275         if(!cm.isSortable(index)){
34276             return;
34277         }
34278         g.stopEditing();
34279         
34280         if (dm.multiSort) {
34281             // update the sortOrder
34282             var so = [];
34283             for(var i = 0; i < cm.config.length; i++ ) {
34284                 
34285                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34286                     continue; // dont' bother, it's not in sort list or being set.
34287                 }
34288                 
34289                 so.push(cm.config[i].dataIndex);
34290             };
34291             dm.sortOrder = so;
34292         }
34293         
34294         
34295         dm.sort(cm.getDataIndex(index));
34296     },
34297
34298
34299     destroy : function(){
34300         if(this.colMenu){
34301             this.colMenu.removeAll();
34302             Roo.menu.MenuMgr.unregister(this.colMenu);
34303             this.colMenu.getEl().remove();
34304             delete this.colMenu;
34305         }
34306         if(this.hmenu){
34307             this.hmenu.removeAll();
34308             Roo.menu.MenuMgr.unregister(this.hmenu);
34309             this.hmenu.getEl().remove();
34310             delete this.hmenu;
34311         }
34312         if(this.grid.enableColumnMove){
34313             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34314             if(dds){
34315                 for(var dd in dds){
34316                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34317                         var elid = dds[dd].dragElId;
34318                         dds[dd].unreg();
34319                         Roo.get(elid).remove();
34320                     } else if(dds[dd].config.isTarget){
34321                         dds[dd].proxyTop.remove();
34322                         dds[dd].proxyBottom.remove();
34323                         dds[dd].unreg();
34324                     }
34325                     if(Roo.dd.DDM.locationCache[dd]){
34326                         delete Roo.dd.DDM.locationCache[dd];
34327                     }
34328                 }
34329                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34330             }
34331         }
34332         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34333         this.bind(null, null);
34334         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34335     },
34336
34337     handleLockChange : function(){
34338         this.refresh(true);
34339     },
34340
34341     onDenyColumnLock : function(){
34342
34343     },
34344
34345     onDenyColumnHide : function(){
34346
34347     },
34348
34349     handleHdMenuClick : function(item){
34350         var index = this.hdCtxIndex;
34351         var cm = this.cm, ds = this.ds;
34352         switch(item.id){
34353             case "asc":
34354                 ds.sort(cm.getDataIndex(index), "ASC");
34355                 break;
34356             case "desc":
34357                 ds.sort(cm.getDataIndex(index), "DESC");
34358                 break;
34359             case "lock":
34360                 var lc = cm.getLockedCount();
34361                 if(cm.getColumnCount(true) <= lc+1){
34362                     this.onDenyColumnLock();
34363                     return;
34364                 }
34365                 if(lc != index){
34366                     cm.setLocked(index, true, true);
34367                     cm.moveColumn(index, lc);
34368                     this.grid.fireEvent("columnmove", index, lc);
34369                 }else{
34370                     cm.setLocked(index, true);
34371                 }
34372             break;
34373             case "unlock":
34374                 var lc = cm.getLockedCount();
34375                 if((lc-1) != index){
34376                     cm.setLocked(index, false, true);
34377                     cm.moveColumn(index, lc-1);
34378                     this.grid.fireEvent("columnmove", index, lc-1);
34379                 }else{
34380                     cm.setLocked(index, false);
34381                 }
34382             break;
34383             default:
34384                 index = cm.getIndexById(item.id.substr(4));
34385                 if(index != -1){
34386                     if(item.checked && cm.getColumnCount(true) <= 1){
34387                         this.onDenyColumnHide();
34388                         return false;
34389                     }
34390                     cm.setHidden(index, item.checked);
34391                 }
34392         }
34393         return true;
34394     },
34395
34396     beforeColMenuShow : function(){
34397         var cm = this.cm,  colCount = cm.getColumnCount();
34398         this.colMenu.removeAll();
34399         for(var i = 0; i < colCount; i++){
34400             this.colMenu.add(new Roo.menu.CheckItem({
34401                 id: "col-"+cm.getColumnId(i),
34402                 text: cm.getColumnHeader(i),
34403                 checked: !cm.isHidden(i),
34404                 hideOnClick:false
34405             }));
34406         }
34407     },
34408
34409     handleHdCtx : function(g, index, e){
34410         e.stopEvent();
34411         var hd = this.getHeaderCell(index);
34412         this.hdCtxIndex = index;
34413         var ms = this.hmenu.items, cm = this.cm;
34414         ms.get("asc").setDisabled(!cm.isSortable(index));
34415         ms.get("desc").setDisabled(!cm.isSortable(index));
34416         if(this.grid.enableColLock !== false){
34417             ms.get("lock").setDisabled(cm.isLocked(index));
34418             ms.get("unlock").setDisabled(!cm.isLocked(index));
34419         }
34420         this.hmenu.show(hd, "tl-bl");
34421     },
34422
34423     handleHdOver : function(e){
34424         var hd = this.findHeaderCell(e.getTarget());
34425         if(hd && !this.headersDisabled){
34426             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34427                this.fly(hd).addClass("x-grid-hd-over");
34428             }
34429         }
34430     },
34431
34432     handleHdOut : function(e){
34433         var hd = this.findHeaderCell(e.getTarget());
34434         if(hd){
34435             this.fly(hd).removeClass("x-grid-hd-over");
34436         }
34437     },
34438
34439     handleSplitDblClick : function(e, t){
34440         var i = this.getCellIndex(t);
34441         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34442             this.autoSizeColumn(i, true);
34443             this.layout();
34444         }
34445     },
34446
34447     render : function(){
34448
34449         var cm = this.cm;
34450         var colCount = cm.getColumnCount();
34451
34452         if(this.grid.monitorWindowResize === true){
34453             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34454         }
34455         var header = this.renderHeaders();
34456         var body = this.templates.body.apply({rows:""});
34457         var html = this.templates.master.apply({
34458             lockedBody: body,
34459             body: body,
34460             lockedHeader: header[0],
34461             header: header[1]
34462         });
34463
34464         //this.updateColumns();
34465
34466         this.grid.getGridEl().dom.innerHTML = html;
34467
34468         this.initElements();
34469         
34470         // a kludge to fix the random scolling effect in webkit
34471         this.el.on("scroll", function() {
34472             this.el.dom.scrollTop=0; // hopefully not recursive..
34473         },this);
34474
34475         this.scroller.on("scroll", this.handleScroll, this);
34476         this.lockedBody.on("mousewheel", this.handleWheel, this);
34477         this.mainBody.on("mousewheel", this.handleWheel, this);
34478
34479         this.mainHd.on("mouseover", this.handleHdOver, this);
34480         this.mainHd.on("mouseout", this.handleHdOut, this);
34481         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34482                 {delegate: "."+this.splitClass});
34483
34484         this.lockedHd.on("mouseover", this.handleHdOver, this);
34485         this.lockedHd.on("mouseout", this.handleHdOut, this);
34486         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34487                 {delegate: "."+this.splitClass});
34488
34489         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34490             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34491         }
34492
34493         this.updateSplitters();
34494
34495         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34496             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34497             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34498         }
34499
34500         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34501             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34502             this.hmenu.add(
34503                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34504                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34505             );
34506             if(this.grid.enableColLock !== false){
34507                 this.hmenu.add('-',
34508                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34509                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34510                 );
34511             }
34512             if(this.grid.enableColumnHide !== false){
34513
34514                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34515                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34516                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34517
34518                 this.hmenu.add('-',
34519                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34520                 );
34521             }
34522             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34523
34524             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34525         }
34526
34527         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34528             this.dd = new Roo.grid.GridDragZone(this.grid, {
34529                 ddGroup : this.grid.ddGroup || 'GridDD'
34530             });
34531         }
34532
34533         /*
34534         for(var i = 0; i < colCount; i++){
34535             if(cm.isHidden(i)){
34536                 this.hideColumn(i);
34537             }
34538             if(cm.config[i].align){
34539                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34540                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34541             }
34542         }*/
34543         
34544         this.updateHeaderSortState();
34545
34546         this.beforeInitialResize();
34547         this.layout(true);
34548
34549         // two part rendering gives faster view to the user
34550         this.renderPhase2.defer(1, this);
34551     },
34552
34553     renderPhase2 : function(){
34554         // render the rows now
34555         this.refresh();
34556         if(this.grid.autoSizeColumns){
34557             this.autoSizeColumns();
34558         }
34559     },
34560
34561     beforeInitialResize : function(){
34562
34563     },
34564
34565     onColumnSplitterMoved : function(i, w){
34566         this.userResized = true;
34567         var cm = this.grid.colModel;
34568         cm.setColumnWidth(i, w, true);
34569         var cid = cm.getColumnId(i);
34570         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34571         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34572         this.updateSplitters();
34573         this.layout();
34574         this.grid.fireEvent("columnresize", i, w);
34575     },
34576
34577     syncRowHeights : function(startIndex, endIndex){
34578         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34579             startIndex = startIndex || 0;
34580             var mrows = this.getBodyTable().rows;
34581             var lrows = this.getLockedTable().rows;
34582             var len = mrows.length-1;
34583             endIndex = Math.min(endIndex || len, len);
34584             for(var i = startIndex; i <= endIndex; i++){
34585                 var m = mrows[i], l = lrows[i];
34586                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34587                 m.style.height = l.style.height = h + "px";
34588             }
34589         }
34590     },
34591
34592     layout : function(initialRender, is2ndPass){
34593         var g = this.grid;
34594         var auto = g.autoHeight;
34595         var scrollOffset = 16;
34596         var c = g.getGridEl(), cm = this.cm,
34597                 expandCol = g.autoExpandColumn,
34598                 gv = this;
34599         //c.beginMeasure();
34600
34601         if(!c.dom.offsetWidth){ // display:none?
34602             if(initialRender){
34603                 this.lockedWrap.show();
34604                 this.mainWrap.show();
34605             }
34606             return;
34607         }
34608
34609         var hasLock = this.cm.isLocked(0);
34610
34611         var tbh = this.headerPanel.getHeight();
34612         var bbh = this.footerPanel.getHeight();
34613
34614         if(auto){
34615             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34616             var newHeight = ch + c.getBorderWidth("tb");
34617             if(g.maxHeight){
34618                 newHeight = Math.min(g.maxHeight, newHeight);
34619             }
34620             c.setHeight(newHeight);
34621         }
34622
34623         if(g.autoWidth){
34624             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34625         }
34626
34627         var s = this.scroller;
34628
34629         var csize = c.getSize(true);
34630
34631         this.el.setSize(csize.width, csize.height);
34632
34633         this.headerPanel.setWidth(csize.width);
34634         this.footerPanel.setWidth(csize.width);
34635
34636         var hdHeight = this.mainHd.getHeight();
34637         var vw = csize.width;
34638         var vh = csize.height - (tbh + bbh);
34639
34640         s.setSize(vw, vh);
34641
34642         var bt = this.getBodyTable();
34643         var ltWidth = hasLock ?
34644                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34645
34646         var scrollHeight = bt.offsetHeight;
34647         var scrollWidth = ltWidth + bt.offsetWidth;
34648         var vscroll = false, hscroll = false;
34649
34650         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34651
34652         var lw = this.lockedWrap, mw = this.mainWrap;
34653         var lb = this.lockedBody, mb = this.mainBody;
34654
34655         setTimeout(function(){
34656             var t = s.dom.offsetTop;
34657             var w = s.dom.clientWidth,
34658                 h = s.dom.clientHeight;
34659
34660             lw.setTop(t);
34661             lw.setSize(ltWidth, h);
34662
34663             mw.setLeftTop(ltWidth, t);
34664             mw.setSize(w-ltWidth, h);
34665
34666             lb.setHeight(h-hdHeight);
34667             mb.setHeight(h-hdHeight);
34668
34669             if(is2ndPass !== true && !gv.userResized && expandCol){
34670                 // high speed resize without full column calculation
34671                 
34672                 var ci = cm.getIndexById(expandCol);
34673                 if (ci < 0) {
34674                     ci = cm.findColumnIndex(expandCol);
34675                 }
34676                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34677                 var expandId = cm.getColumnId(ci);
34678                 var  tw = cm.getTotalWidth(false);
34679                 var currentWidth = cm.getColumnWidth(ci);
34680                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34681                 if(currentWidth != cw){
34682                     cm.setColumnWidth(ci, cw, true);
34683                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34684                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34685                     gv.updateSplitters();
34686                     gv.layout(false, true);
34687                 }
34688             }
34689
34690             if(initialRender){
34691                 lw.show();
34692                 mw.show();
34693             }
34694             //c.endMeasure();
34695         }, 10);
34696     },
34697
34698     onWindowResize : function(){
34699         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34700             return;
34701         }
34702         this.layout();
34703     },
34704
34705     appendFooter : function(parentEl){
34706         return null;
34707     },
34708
34709     sortAscText : "Sort Ascending",
34710     sortDescText : "Sort Descending",
34711     lockText : "Lock Column",
34712     unlockText : "Unlock Column",
34713     columnsText : "Columns"
34714 });
34715
34716
34717 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34718     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34719     this.proxy.el.addClass('x-grid3-col-dd');
34720 };
34721
34722 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34723     handleMouseDown : function(e){
34724
34725     },
34726
34727     callHandleMouseDown : function(e){
34728         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34729     }
34730 });
34731 /*
34732  * Based on:
34733  * Ext JS Library 1.1.1
34734  * Copyright(c) 2006-2007, Ext JS, LLC.
34735  *
34736  * Originally Released Under LGPL - original licence link has changed is not relivant.
34737  *
34738  * Fork - LGPL
34739  * <script type="text/javascript">
34740  */
34741  
34742 // private
34743 // This is a support class used internally by the Grid components
34744 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34745     this.grid = grid;
34746     this.view = grid.getView();
34747     this.proxy = this.view.resizeProxy;
34748     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34749         "gridSplitters" + this.grid.getGridEl().id, {
34750         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34751     });
34752     this.setHandleElId(Roo.id(hd));
34753     this.setOuterHandleElId(Roo.id(hd2));
34754     this.scroll = false;
34755 };
34756 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34757     fly: Roo.Element.fly,
34758
34759     b4StartDrag : function(x, y){
34760         this.view.headersDisabled = true;
34761         this.proxy.setHeight(this.view.mainWrap.getHeight());
34762         var w = this.cm.getColumnWidth(this.cellIndex);
34763         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34764         this.resetConstraints();
34765         this.setXConstraint(minw, 1000);
34766         this.setYConstraint(0, 0);
34767         this.minX = x - minw;
34768         this.maxX = x + 1000;
34769         this.startPos = x;
34770         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34771     },
34772
34773
34774     handleMouseDown : function(e){
34775         ev = Roo.EventObject.setEvent(e);
34776         var t = this.fly(ev.getTarget());
34777         if(t.hasClass("x-grid-split")){
34778             this.cellIndex = this.view.getCellIndex(t.dom);
34779             this.split = t.dom;
34780             this.cm = this.grid.colModel;
34781             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34782                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34783             }
34784         }
34785     },
34786
34787     endDrag : function(e){
34788         this.view.headersDisabled = false;
34789         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34790         var diff = endX - this.startPos;
34791         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34792     },
34793
34794     autoOffset : function(){
34795         this.setDelta(0,0);
34796     }
34797 });/*
34798  * Based on:
34799  * Ext JS Library 1.1.1
34800  * Copyright(c) 2006-2007, Ext JS, LLC.
34801  *
34802  * Originally Released Under LGPL - original licence link has changed is not relivant.
34803  *
34804  * Fork - LGPL
34805  * <script type="text/javascript">
34806  */
34807  
34808 // private
34809 // This is a support class used internally by the Grid components
34810 Roo.grid.GridDragZone = function(grid, config){
34811     this.view = grid.getView();
34812     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34813     if(this.view.lockedBody){
34814         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34815         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34816     }
34817     this.scroll = false;
34818     this.grid = grid;
34819     this.ddel = document.createElement('div');
34820     this.ddel.className = 'x-grid-dd-wrap';
34821 };
34822
34823 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34824     ddGroup : "GridDD",
34825
34826     getDragData : function(e){
34827         var t = Roo.lib.Event.getTarget(e);
34828         var rowIndex = this.view.findRowIndex(t);
34829         if(rowIndex !== false){
34830             var sm = this.grid.selModel;
34831             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34832               //  sm.mouseDown(e, t);
34833             //}
34834             if (e.hasModifier()){
34835                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34836             }
34837             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34838         }
34839         return false;
34840     },
34841
34842     onInitDrag : function(e){
34843         var data = this.dragData;
34844         this.ddel.innerHTML = this.grid.getDragDropText();
34845         this.proxy.update(this.ddel);
34846         // fire start drag?
34847     },
34848
34849     afterRepair : function(){
34850         this.dragging = false;
34851     },
34852
34853     getRepairXY : function(e, data){
34854         return false;
34855     },
34856
34857     onEndDrag : function(data, e){
34858         // fire end drag?
34859     },
34860
34861     onValidDrop : function(dd, e, id){
34862         // fire drag drop?
34863         this.hideProxy();
34864     },
34865
34866     beforeInvalidDrop : function(e, id){
34867
34868     }
34869 });/*
34870  * Based on:
34871  * Ext JS Library 1.1.1
34872  * Copyright(c) 2006-2007, Ext JS, LLC.
34873  *
34874  * Originally Released Under LGPL - original licence link has changed is not relivant.
34875  *
34876  * Fork - LGPL
34877  * <script type="text/javascript">
34878  */
34879  
34880
34881 /**
34882  * @class Roo.grid.ColumnModel
34883  * @extends Roo.util.Observable
34884  * This is the default implementation of a ColumnModel used by the Grid. It defines
34885  * the columns in the grid.
34886  * <br>Usage:<br>
34887  <pre><code>
34888  var colModel = new Roo.grid.ColumnModel([
34889         {header: "Ticker", width: 60, sortable: true, locked: true},
34890         {header: "Company Name", width: 150, sortable: true},
34891         {header: "Market Cap.", width: 100, sortable: true},
34892         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34893         {header: "Employees", width: 100, sortable: true, resizable: false}
34894  ]);
34895  </code></pre>
34896  * <p>
34897  
34898  * The config options listed for this class are options which may appear in each
34899  * individual column definition.
34900  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34901  * @constructor
34902  * @param {Object} config An Array of column config objects. See this class's
34903  * config objects for details.
34904 */
34905 Roo.grid.ColumnModel = function(config){
34906         /**
34907      * The config passed into the constructor
34908      */
34909     this.config = config;
34910     this.lookup = {};
34911
34912     // if no id, create one
34913     // if the column does not have a dataIndex mapping,
34914     // map it to the order it is in the config
34915     for(var i = 0, len = config.length; i < len; i++){
34916         var c = config[i];
34917         if(typeof c.dataIndex == "undefined"){
34918             c.dataIndex = i;
34919         }
34920         if(typeof c.renderer == "string"){
34921             c.renderer = Roo.util.Format[c.renderer];
34922         }
34923         if(typeof c.id == "undefined"){
34924             c.id = Roo.id();
34925         }
34926         if(c.editor && c.editor.xtype){
34927             c.editor  = Roo.factory(c.editor, Roo.grid);
34928         }
34929         if(c.editor && c.editor.isFormField){
34930             c.editor = new Roo.grid.GridEditor(c.editor);
34931         }
34932         this.lookup[c.id] = c;
34933     }
34934
34935     /**
34936      * The width of columns which have no width specified (defaults to 100)
34937      * @type Number
34938      */
34939     this.defaultWidth = 100;
34940
34941     /**
34942      * Default sortable of columns which have no sortable specified (defaults to false)
34943      * @type Boolean
34944      */
34945     this.defaultSortable = false;
34946
34947     this.addEvents({
34948         /**
34949              * @event widthchange
34950              * Fires when the width of a column changes.
34951              * @param {ColumnModel} this
34952              * @param {Number} columnIndex The column index
34953              * @param {Number} newWidth The new width
34954              */
34955             "widthchange": true,
34956         /**
34957              * @event headerchange
34958              * Fires when the text of a header changes.
34959              * @param {ColumnModel} this
34960              * @param {Number} columnIndex The column index
34961              * @param {Number} newText The new header text
34962              */
34963             "headerchange": true,
34964         /**
34965              * @event hiddenchange
34966              * Fires when a column is hidden or "unhidden".
34967              * @param {ColumnModel} this
34968              * @param {Number} columnIndex The column index
34969              * @param {Boolean} hidden true if hidden, false otherwise
34970              */
34971             "hiddenchange": true,
34972             /**
34973          * @event columnmoved
34974          * Fires when a column is moved.
34975          * @param {ColumnModel} this
34976          * @param {Number} oldIndex
34977          * @param {Number} newIndex
34978          */
34979         "columnmoved" : true,
34980         /**
34981          * @event columlockchange
34982          * Fires when a column's locked state is changed
34983          * @param {ColumnModel} this
34984          * @param {Number} colIndex
34985          * @param {Boolean} locked true if locked
34986          */
34987         "columnlockchange" : true
34988     });
34989     Roo.grid.ColumnModel.superclass.constructor.call(this);
34990 };
34991 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34992     /**
34993      * @cfg {String} header The header text to display in the Grid view.
34994      */
34995     /**
34996      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34997      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34998      * specified, the column's index is used as an index into the Record's data Array.
34999      */
35000     /**
35001      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35002      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35003      */
35004     /**
35005      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35006      * Defaults to the value of the {@link #defaultSortable} property.
35007      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35008      */
35009     /**
35010      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35011      */
35012     /**
35013      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35014      */
35015     /**
35016      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35017      */
35018     /**
35019      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35020      */
35021     /**
35022      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35023      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35024      * default renderer uses the raw data value.
35025      */
35026        /**
35027      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35028      */
35029     /**
35030      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35031      */
35032
35033     /**
35034      * Returns the id of the column at the specified index.
35035      * @param {Number} index The column index
35036      * @return {String} the id
35037      */
35038     getColumnId : function(index){
35039         return this.config[index].id;
35040     },
35041
35042     /**
35043      * Returns the column for a specified id.
35044      * @param {String} id The column id
35045      * @return {Object} the column
35046      */
35047     getColumnById : function(id){
35048         return this.lookup[id];
35049     },
35050
35051     
35052     /**
35053      * Returns the column for a specified dataIndex.
35054      * @param {String} dataIndex The column dataIndex
35055      * @return {Object|Boolean} the column or false if not found
35056      */
35057     getColumnByDataIndex: function(dataIndex){
35058         var index = this.findColumnIndex(dataIndex);
35059         return index > -1 ? this.config[index] : false;
35060     },
35061     
35062     /**
35063      * Returns the index for a specified column id.
35064      * @param {String} id The column id
35065      * @return {Number} the index, or -1 if not found
35066      */
35067     getIndexById : function(id){
35068         for(var i = 0, len = this.config.length; i < len; i++){
35069             if(this.config[i].id == id){
35070                 return i;
35071             }
35072         }
35073         return -1;
35074     },
35075     
35076     /**
35077      * Returns the index for a specified column dataIndex.
35078      * @param {String} dataIndex The column dataIndex
35079      * @return {Number} the index, or -1 if not found
35080      */
35081     
35082     findColumnIndex : function(dataIndex){
35083         for(var i = 0, len = this.config.length; i < len; i++){
35084             if(this.config[i].dataIndex == dataIndex){
35085                 return i;
35086             }
35087         }
35088         return -1;
35089     },
35090     
35091     
35092     moveColumn : function(oldIndex, newIndex){
35093         var c = this.config[oldIndex];
35094         this.config.splice(oldIndex, 1);
35095         this.config.splice(newIndex, 0, c);
35096         this.dataMap = null;
35097         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35098     },
35099
35100     isLocked : function(colIndex){
35101         return this.config[colIndex].locked === true;
35102     },
35103
35104     setLocked : function(colIndex, value, suppressEvent){
35105         if(this.isLocked(colIndex) == value){
35106             return;
35107         }
35108         this.config[colIndex].locked = value;
35109         if(!suppressEvent){
35110             this.fireEvent("columnlockchange", this, colIndex, value);
35111         }
35112     },
35113
35114     getTotalLockedWidth : function(){
35115         var totalWidth = 0;
35116         for(var i = 0; i < this.config.length; i++){
35117             if(this.isLocked(i) && !this.isHidden(i)){
35118                 this.totalWidth += this.getColumnWidth(i);
35119             }
35120         }
35121         return totalWidth;
35122     },
35123
35124     getLockedCount : function(){
35125         for(var i = 0, len = this.config.length; i < len; i++){
35126             if(!this.isLocked(i)){
35127                 return i;
35128             }
35129         }
35130     },
35131
35132     /**
35133      * Returns the number of columns.
35134      * @return {Number}
35135      */
35136     getColumnCount : function(visibleOnly){
35137         if(visibleOnly === true){
35138             var c = 0;
35139             for(var i = 0, len = this.config.length; i < len; i++){
35140                 if(!this.isHidden(i)){
35141                     c++;
35142                 }
35143             }
35144             return c;
35145         }
35146         return this.config.length;
35147     },
35148
35149     /**
35150      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35151      * @param {Function} fn
35152      * @param {Object} scope (optional)
35153      * @return {Array} result
35154      */
35155     getColumnsBy : function(fn, scope){
35156         var r = [];
35157         for(var i = 0, len = this.config.length; i < len; i++){
35158             var c = this.config[i];
35159             if(fn.call(scope||this, c, i) === true){
35160                 r[r.length] = c;
35161             }
35162         }
35163         return r;
35164     },
35165
35166     /**
35167      * Returns true if the specified column is sortable.
35168      * @param {Number} col The column index
35169      * @return {Boolean}
35170      */
35171     isSortable : function(col){
35172         if(typeof this.config[col].sortable == "undefined"){
35173             return this.defaultSortable;
35174         }
35175         return this.config[col].sortable;
35176     },
35177
35178     /**
35179      * Returns the rendering (formatting) function defined for the column.
35180      * @param {Number} col The column index.
35181      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35182      */
35183     getRenderer : function(col){
35184         if(!this.config[col].renderer){
35185             return Roo.grid.ColumnModel.defaultRenderer;
35186         }
35187         return this.config[col].renderer;
35188     },
35189
35190     /**
35191      * Sets the rendering (formatting) function for a column.
35192      * @param {Number} col The column index
35193      * @param {Function} fn The function to use to process the cell's raw data
35194      * to return HTML markup for the grid view. The render function is called with
35195      * the following parameters:<ul>
35196      * <li>Data value.</li>
35197      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35198      * <li>css A CSS style string to apply to the table cell.</li>
35199      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35200      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35201      * <li>Row index</li>
35202      * <li>Column index</li>
35203      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35204      */
35205     setRenderer : function(col, fn){
35206         this.config[col].renderer = fn;
35207     },
35208
35209     /**
35210      * Returns the width for the specified column.
35211      * @param {Number} col The column index
35212      * @return {Number}
35213      */
35214     getColumnWidth : function(col){
35215         return this.config[col].width * 1 || this.defaultWidth;
35216     },
35217
35218     /**
35219      * Sets the width for a column.
35220      * @param {Number} col The column index
35221      * @param {Number} width The new width
35222      */
35223     setColumnWidth : function(col, width, suppressEvent){
35224         this.config[col].width = width;
35225         this.totalWidth = null;
35226         if(!suppressEvent){
35227              this.fireEvent("widthchange", this, col, width);
35228         }
35229     },
35230
35231     /**
35232      * Returns the total width of all columns.
35233      * @param {Boolean} includeHidden True to include hidden column widths
35234      * @return {Number}
35235      */
35236     getTotalWidth : function(includeHidden){
35237         if(!this.totalWidth){
35238             this.totalWidth = 0;
35239             for(var i = 0, len = this.config.length; i < len; i++){
35240                 if(includeHidden || !this.isHidden(i)){
35241                     this.totalWidth += this.getColumnWidth(i);
35242                 }
35243             }
35244         }
35245         return this.totalWidth;
35246     },
35247
35248     /**
35249      * Returns the header for the specified column.
35250      * @param {Number} col The column index
35251      * @return {String}
35252      */
35253     getColumnHeader : function(col){
35254         return this.config[col].header;
35255     },
35256
35257     /**
35258      * Sets the header for a column.
35259      * @param {Number} col The column index
35260      * @param {String} header The new header
35261      */
35262     setColumnHeader : function(col, header){
35263         this.config[col].header = header;
35264         this.fireEvent("headerchange", this, col, header);
35265     },
35266
35267     /**
35268      * Returns the tooltip for the specified column.
35269      * @param {Number} col The column index
35270      * @return {String}
35271      */
35272     getColumnTooltip : function(col){
35273             return this.config[col].tooltip;
35274     },
35275     /**
35276      * Sets the tooltip for a column.
35277      * @param {Number} col The column index
35278      * @param {String} tooltip The new tooltip
35279      */
35280     setColumnTooltip : function(col, tooltip){
35281             this.config[col].tooltip = tooltip;
35282     },
35283
35284     /**
35285      * Returns the dataIndex for the specified column.
35286      * @param {Number} col The column index
35287      * @return {Number}
35288      */
35289     getDataIndex : function(col){
35290         return this.config[col].dataIndex;
35291     },
35292
35293     /**
35294      * Sets the dataIndex for a column.
35295      * @param {Number} col The column index
35296      * @param {Number} dataIndex The new dataIndex
35297      */
35298     setDataIndex : function(col, dataIndex){
35299         this.config[col].dataIndex = dataIndex;
35300     },
35301
35302     
35303     
35304     /**
35305      * Returns true if the cell is editable.
35306      * @param {Number} colIndex The column index
35307      * @param {Number} rowIndex The row index
35308      * @return {Boolean}
35309      */
35310     isCellEditable : function(colIndex, rowIndex){
35311         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35312     },
35313
35314     /**
35315      * Returns the editor defined for the cell/column.
35316      * return false or null to disable editing.
35317      * @param {Number} colIndex The column index
35318      * @param {Number} rowIndex The row index
35319      * @return {Object}
35320      */
35321     getCellEditor : function(colIndex, rowIndex){
35322         return this.config[colIndex].editor;
35323     },
35324
35325     /**
35326      * Sets if a column is editable.
35327      * @param {Number} col The column index
35328      * @param {Boolean} editable True if the column is editable
35329      */
35330     setEditable : function(col, editable){
35331         this.config[col].editable = editable;
35332     },
35333
35334
35335     /**
35336      * Returns true if the column is hidden.
35337      * @param {Number} colIndex The column index
35338      * @return {Boolean}
35339      */
35340     isHidden : function(colIndex){
35341         return this.config[colIndex].hidden;
35342     },
35343
35344
35345     /**
35346      * Returns true if the column width cannot be changed
35347      */
35348     isFixed : function(colIndex){
35349         return this.config[colIndex].fixed;
35350     },
35351
35352     /**
35353      * Returns true if the column can be resized
35354      * @return {Boolean}
35355      */
35356     isResizable : function(colIndex){
35357         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35358     },
35359     /**
35360      * Sets if a column is hidden.
35361      * @param {Number} colIndex The column index
35362      * @param {Boolean} hidden True if the column is hidden
35363      */
35364     setHidden : function(colIndex, hidden){
35365         this.config[colIndex].hidden = hidden;
35366         this.totalWidth = null;
35367         this.fireEvent("hiddenchange", this, colIndex, hidden);
35368     },
35369
35370     /**
35371      * Sets the editor for a column.
35372      * @param {Number} col The column index
35373      * @param {Object} editor The editor object
35374      */
35375     setEditor : function(col, editor){
35376         this.config[col].editor = editor;
35377     }
35378 });
35379
35380 Roo.grid.ColumnModel.defaultRenderer = function(value){
35381         if(typeof value == "string" && value.length < 1){
35382             return "&#160;";
35383         }
35384         return value;
35385 };
35386
35387 // Alias for backwards compatibility
35388 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35389 /*
35390  * Based on:
35391  * Ext JS Library 1.1.1
35392  * Copyright(c) 2006-2007, Ext JS, LLC.
35393  *
35394  * Originally Released Under LGPL - original licence link has changed is not relivant.
35395  *
35396  * Fork - LGPL
35397  * <script type="text/javascript">
35398  */
35399
35400 /**
35401  * @class Roo.grid.AbstractSelectionModel
35402  * @extends Roo.util.Observable
35403  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35404  * implemented by descendant classes.  This class should not be directly instantiated.
35405  * @constructor
35406  */
35407 Roo.grid.AbstractSelectionModel = function(){
35408     this.locked = false;
35409     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35410 };
35411
35412 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35413     /** @ignore Called by the grid automatically. Do not call directly. */
35414     init : function(grid){
35415         this.grid = grid;
35416         this.initEvents();
35417     },
35418
35419     /**
35420      * Locks the selections.
35421      */
35422     lock : function(){
35423         this.locked = true;
35424     },
35425
35426     /**
35427      * Unlocks the selections.
35428      */
35429     unlock : function(){
35430         this.locked = false;
35431     },
35432
35433     /**
35434      * Returns true if the selections are locked.
35435      * @return {Boolean}
35436      */
35437     isLocked : function(){
35438         return this.locked;
35439     }
35440 });/*
35441  * Based on:
35442  * Ext JS Library 1.1.1
35443  * Copyright(c) 2006-2007, Ext JS, LLC.
35444  *
35445  * Originally Released Under LGPL - original licence link has changed is not relivant.
35446  *
35447  * Fork - LGPL
35448  * <script type="text/javascript">
35449  */
35450 /**
35451  * @extends Roo.grid.AbstractSelectionModel
35452  * @class Roo.grid.RowSelectionModel
35453  * The default SelectionModel used by {@link Roo.grid.Grid}.
35454  * It supports multiple selections and keyboard selection/navigation. 
35455  * @constructor
35456  * @param {Object} config
35457  */
35458 Roo.grid.RowSelectionModel = function(config){
35459     Roo.apply(this, config);
35460     this.selections = new Roo.util.MixedCollection(false, function(o){
35461         return o.id;
35462     });
35463
35464     this.last = false;
35465     this.lastActive = false;
35466
35467     this.addEvents({
35468         /**
35469              * @event selectionchange
35470              * Fires when the selection changes
35471              * @param {SelectionModel} this
35472              */
35473             "selectionchange" : true,
35474         /**
35475              * @event afterselectionchange
35476              * Fires after the selection changes (eg. by key press or clicking)
35477              * @param {SelectionModel} this
35478              */
35479             "afterselectionchange" : true,
35480         /**
35481              * @event beforerowselect
35482              * Fires when a row is selected being selected, return false to cancel.
35483              * @param {SelectionModel} this
35484              * @param {Number} rowIndex The selected index
35485              * @param {Boolean} keepExisting False if other selections will be cleared
35486              */
35487             "beforerowselect" : true,
35488         /**
35489              * @event rowselect
35490              * Fires when a row is selected.
35491              * @param {SelectionModel} this
35492              * @param {Number} rowIndex The selected index
35493              * @param {Roo.data.Record} r The record
35494              */
35495             "rowselect" : true,
35496         /**
35497              * @event rowdeselect
35498              * Fires when a row is deselected.
35499              * @param {SelectionModel} this
35500              * @param {Number} rowIndex The selected index
35501              */
35502         "rowdeselect" : true
35503     });
35504     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35505     this.locked = false;
35506 };
35507
35508 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35509     /**
35510      * @cfg {Boolean} singleSelect
35511      * True to allow selection of only one row at a time (defaults to false)
35512      */
35513     singleSelect : false,
35514
35515     // private
35516     initEvents : function(){
35517
35518         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35519             this.grid.on("mousedown", this.handleMouseDown, this);
35520         }else{ // allow click to work like normal
35521             this.grid.on("rowclick", this.handleDragableRowClick, this);
35522         }
35523
35524         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35525             "up" : function(e){
35526                 if(!e.shiftKey){
35527                     this.selectPrevious(e.shiftKey);
35528                 }else if(this.last !== false && this.lastActive !== false){
35529                     var last = this.last;
35530                     this.selectRange(this.last,  this.lastActive-1);
35531                     this.grid.getView().focusRow(this.lastActive);
35532                     if(last !== false){
35533                         this.last = last;
35534                     }
35535                 }else{
35536                     this.selectFirstRow();
35537                 }
35538                 this.fireEvent("afterselectionchange", this);
35539             },
35540             "down" : function(e){
35541                 if(!e.shiftKey){
35542                     this.selectNext(e.shiftKey);
35543                 }else if(this.last !== false && this.lastActive !== false){
35544                     var last = this.last;
35545                     this.selectRange(this.last,  this.lastActive+1);
35546                     this.grid.getView().focusRow(this.lastActive);
35547                     if(last !== false){
35548                         this.last = last;
35549                     }
35550                 }else{
35551                     this.selectFirstRow();
35552                 }
35553                 this.fireEvent("afterselectionchange", this);
35554             },
35555             scope: this
35556         });
35557
35558         var view = this.grid.view;
35559         view.on("refresh", this.onRefresh, this);
35560         view.on("rowupdated", this.onRowUpdated, this);
35561         view.on("rowremoved", this.onRemove, this);
35562     },
35563
35564     // private
35565     onRefresh : function(){
35566         var ds = this.grid.dataSource, i, v = this.grid.view;
35567         var s = this.selections;
35568         s.each(function(r){
35569             if((i = ds.indexOfId(r.id)) != -1){
35570                 v.onRowSelect(i);
35571             }else{
35572                 s.remove(r);
35573             }
35574         });
35575     },
35576
35577     // private
35578     onRemove : function(v, index, r){
35579         this.selections.remove(r);
35580     },
35581
35582     // private
35583     onRowUpdated : function(v, index, r){
35584         if(this.isSelected(r)){
35585             v.onRowSelect(index);
35586         }
35587     },
35588
35589     /**
35590      * Select records.
35591      * @param {Array} records The records to select
35592      * @param {Boolean} keepExisting (optional) True to keep existing selections
35593      */
35594     selectRecords : function(records, keepExisting){
35595         if(!keepExisting){
35596             this.clearSelections();
35597         }
35598         var ds = this.grid.dataSource;
35599         for(var i = 0, len = records.length; i < len; i++){
35600             this.selectRow(ds.indexOf(records[i]), true);
35601         }
35602     },
35603
35604     /**
35605      * Gets the number of selected rows.
35606      * @return {Number}
35607      */
35608     getCount : function(){
35609         return this.selections.length;
35610     },
35611
35612     /**
35613      * Selects the first row in the grid.
35614      */
35615     selectFirstRow : function(){
35616         this.selectRow(0);
35617     },
35618
35619     /**
35620      * Select the last row.
35621      * @param {Boolean} keepExisting (optional) True to keep existing selections
35622      */
35623     selectLastRow : function(keepExisting){
35624         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35625     },
35626
35627     /**
35628      * Selects the row immediately following the last selected row.
35629      * @param {Boolean} keepExisting (optional) True to keep existing selections
35630      */
35631     selectNext : function(keepExisting){
35632         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35633             this.selectRow(this.last+1, keepExisting);
35634             this.grid.getView().focusRow(this.last);
35635         }
35636     },
35637
35638     /**
35639      * Selects the row that precedes the last selected row.
35640      * @param {Boolean} keepExisting (optional) True to keep existing selections
35641      */
35642     selectPrevious : function(keepExisting){
35643         if(this.last){
35644             this.selectRow(this.last-1, keepExisting);
35645             this.grid.getView().focusRow(this.last);
35646         }
35647     },
35648
35649     /**
35650      * Returns the selected records
35651      * @return {Array} Array of selected records
35652      */
35653     getSelections : function(){
35654         return [].concat(this.selections.items);
35655     },
35656
35657     /**
35658      * Returns the first selected record.
35659      * @return {Record}
35660      */
35661     getSelected : function(){
35662         return this.selections.itemAt(0);
35663     },
35664
35665
35666     /**
35667      * Clears all selections.
35668      */
35669     clearSelections : function(fast){
35670         if(this.locked) return;
35671         if(fast !== true){
35672             var ds = this.grid.dataSource;
35673             var s = this.selections;
35674             s.each(function(r){
35675                 this.deselectRow(ds.indexOfId(r.id));
35676             }, this);
35677             s.clear();
35678         }else{
35679             this.selections.clear();
35680         }
35681         this.last = false;
35682     },
35683
35684
35685     /**
35686      * Selects all rows.
35687      */
35688     selectAll : function(){
35689         if(this.locked) return;
35690         this.selections.clear();
35691         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35692             this.selectRow(i, true);
35693         }
35694     },
35695
35696     /**
35697      * Returns True if there is a selection.
35698      * @return {Boolean}
35699      */
35700     hasSelection : function(){
35701         return this.selections.length > 0;
35702     },
35703
35704     /**
35705      * Returns True if the specified row is selected.
35706      * @param {Number/Record} record The record or index of the record to check
35707      * @return {Boolean}
35708      */
35709     isSelected : function(index){
35710         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35711         return (r && this.selections.key(r.id) ? true : false);
35712     },
35713
35714     /**
35715      * Returns True if the specified record id is selected.
35716      * @param {String} id The id of record to check
35717      * @return {Boolean}
35718      */
35719     isIdSelected : function(id){
35720         return (this.selections.key(id) ? true : false);
35721     },
35722
35723     // private
35724     handleMouseDown : function(e, t){
35725         var view = this.grid.getView(), rowIndex;
35726         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35727             return;
35728         };
35729         if(e.shiftKey && this.last !== false){
35730             var last = this.last;
35731             this.selectRange(last, rowIndex, e.ctrlKey);
35732             this.last = last; // reset the last
35733             view.focusRow(rowIndex);
35734         }else{
35735             var isSelected = this.isSelected(rowIndex);
35736             if(e.button !== 0 && isSelected){
35737                 view.focusRow(rowIndex);
35738             }else if(e.ctrlKey && isSelected){
35739                 this.deselectRow(rowIndex);
35740             }else if(!isSelected){
35741                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35742                 view.focusRow(rowIndex);
35743             }
35744         }
35745         this.fireEvent("afterselectionchange", this);
35746     },
35747     // private
35748     handleDragableRowClick :  function(grid, rowIndex, e) 
35749     {
35750         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35751             this.selectRow(rowIndex, false);
35752             grid.view.focusRow(rowIndex);
35753              this.fireEvent("afterselectionchange", this);
35754         }
35755     },
35756     
35757     /**
35758      * Selects multiple rows.
35759      * @param {Array} rows Array of the indexes of the row to select
35760      * @param {Boolean} keepExisting (optional) True to keep existing selections
35761      */
35762     selectRows : function(rows, keepExisting){
35763         if(!keepExisting){
35764             this.clearSelections();
35765         }
35766         for(var i = 0, len = rows.length; i < len; i++){
35767             this.selectRow(rows[i], true);
35768         }
35769     },
35770
35771     /**
35772      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35773      * @param {Number} startRow The index of the first row in the range
35774      * @param {Number} endRow The index of the last row in the range
35775      * @param {Boolean} keepExisting (optional) True to retain existing selections
35776      */
35777     selectRange : function(startRow, endRow, keepExisting){
35778         if(this.locked) return;
35779         if(!keepExisting){
35780             this.clearSelections();
35781         }
35782         if(startRow <= endRow){
35783             for(var i = startRow; i <= endRow; i++){
35784                 this.selectRow(i, true);
35785             }
35786         }else{
35787             for(var i = startRow; i >= endRow; i--){
35788                 this.selectRow(i, true);
35789             }
35790         }
35791     },
35792
35793     /**
35794      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35795      * @param {Number} startRow The index of the first row in the range
35796      * @param {Number} endRow The index of the last row in the range
35797      */
35798     deselectRange : function(startRow, endRow, preventViewNotify){
35799         if(this.locked) return;
35800         for(var i = startRow; i <= endRow; i++){
35801             this.deselectRow(i, preventViewNotify);
35802         }
35803     },
35804
35805     /**
35806      * Selects a row.
35807      * @param {Number} row The index of the row to select
35808      * @param {Boolean} keepExisting (optional) True to keep existing selections
35809      */
35810     selectRow : function(index, keepExisting, preventViewNotify){
35811         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35812         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35813             if(!keepExisting || this.singleSelect){
35814                 this.clearSelections();
35815             }
35816             var r = this.grid.dataSource.getAt(index);
35817             this.selections.add(r);
35818             this.last = this.lastActive = index;
35819             if(!preventViewNotify){
35820                 this.grid.getView().onRowSelect(index);
35821             }
35822             this.fireEvent("rowselect", this, index, r);
35823             this.fireEvent("selectionchange", this);
35824         }
35825     },
35826
35827     /**
35828      * Deselects a row.
35829      * @param {Number} row The index of the row to deselect
35830      */
35831     deselectRow : function(index, preventViewNotify){
35832         if(this.locked) return;
35833         if(this.last == index){
35834             this.last = false;
35835         }
35836         if(this.lastActive == index){
35837             this.lastActive = false;
35838         }
35839         var r = this.grid.dataSource.getAt(index);
35840         this.selections.remove(r);
35841         if(!preventViewNotify){
35842             this.grid.getView().onRowDeselect(index);
35843         }
35844         this.fireEvent("rowdeselect", this, index);
35845         this.fireEvent("selectionchange", this);
35846     },
35847
35848     // private
35849     restoreLast : function(){
35850         if(this._last){
35851             this.last = this._last;
35852         }
35853     },
35854
35855     // private
35856     acceptsNav : function(row, col, cm){
35857         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35858     },
35859
35860     // private
35861     onEditorKey : function(field, e){
35862         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35863         if(k == e.TAB){
35864             e.stopEvent();
35865             ed.completeEdit();
35866             if(e.shiftKey){
35867                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35868             }else{
35869                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35870             }
35871         }else if(k == e.ENTER && !e.ctrlKey){
35872             e.stopEvent();
35873             ed.completeEdit();
35874             if(e.shiftKey){
35875                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35876             }else{
35877                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35878             }
35879         }else if(k == e.ESC){
35880             ed.cancelEdit();
35881         }
35882         if(newCell){
35883             g.startEditing(newCell[0], newCell[1]);
35884         }
35885     }
35886 });/*
35887  * Based on:
35888  * Ext JS Library 1.1.1
35889  * Copyright(c) 2006-2007, Ext JS, LLC.
35890  *
35891  * Originally Released Under LGPL - original licence link has changed is not relivant.
35892  *
35893  * Fork - LGPL
35894  * <script type="text/javascript">
35895  */
35896 /**
35897  * @class Roo.grid.CellSelectionModel
35898  * @extends Roo.grid.AbstractSelectionModel
35899  * This class provides the basic implementation for cell selection in a grid.
35900  * @constructor
35901  * @param {Object} config The object containing the configuration of this model.
35902  */
35903 Roo.grid.CellSelectionModel = function(config){
35904     Roo.apply(this, config);
35905
35906     this.selection = null;
35907
35908     this.addEvents({
35909         /**
35910              * @event beforerowselect
35911              * Fires before a cell is selected.
35912              * @param {SelectionModel} this
35913              * @param {Number} rowIndex The selected row index
35914              * @param {Number} colIndex The selected cell index
35915              */
35916             "beforecellselect" : true,
35917         /**
35918              * @event cellselect
35919              * Fires when a cell is selected.
35920              * @param {SelectionModel} this
35921              * @param {Number} rowIndex The selected row index
35922              * @param {Number} colIndex The selected cell index
35923              */
35924             "cellselect" : true,
35925         /**
35926              * @event selectionchange
35927              * Fires when the active selection changes.
35928              * @param {SelectionModel} this
35929              * @param {Object} selection null for no selection or an object (o) with two properties
35930                 <ul>
35931                 <li>o.record: the record object for the row the selection is in</li>
35932                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35933                 </ul>
35934              */
35935             "selectionchange" : true
35936     });
35937     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35938 };
35939
35940 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35941
35942     /** @ignore */
35943     initEvents : function(){
35944         this.grid.on("mousedown", this.handleMouseDown, this);
35945         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35946         var view = this.grid.view;
35947         view.on("refresh", this.onViewChange, this);
35948         view.on("rowupdated", this.onRowUpdated, this);
35949         view.on("beforerowremoved", this.clearSelections, this);
35950         view.on("beforerowsinserted", this.clearSelections, this);
35951         if(this.grid.isEditor){
35952             this.grid.on("beforeedit", this.beforeEdit,  this);
35953         }
35954     },
35955
35956         //private
35957     beforeEdit : function(e){
35958         this.select(e.row, e.column, false, true, e.record);
35959     },
35960
35961         //private
35962     onRowUpdated : function(v, index, r){
35963         if(this.selection && this.selection.record == r){
35964             v.onCellSelect(index, this.selection.cell[1]);
35965         }
35966     },
35967
35968         //private
35969     onViewChange : function(){
35970         this.clearSelections(true);
35971     },
35972
35973         /**
35974          * Returns the currently selected cell,.
35975          * @return {Array} The selected cell (row, column) or null if none selected.
35976          */
35977     getSelectedCell : function(){
35978         return this.selection ? this.selection.cell : null;
35979     },
35980
35981     /**
35982      * Clears all selections.
35983      * @param {Boolean} true to prevent the gridview from being notified about the change.
35984      */
35985     clearSelections : function(preventNotify){
35986         var s = this.selection;
35987         if(s){
35988             if(preventNotify !== true){
35989                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35990             }
35991             this.selection = null;
35992             this.fireEvent("selectionchange", this, null);
35993         }
35994     },
35995
35996     /**
35997      * Returns true if there is a selection.
35998      * @return {Boolean}
35999      */
36000     hasSelection : function(){
36001         return this.selection ? true : false;
36002     },
36003
36004     /** @ignore */
36005     handleMouseDown : function(e, t){
36006         var v = this.grid.getView();
36007         if(this.isLocked()){
36008             return;
36009         };
36010         var row = v.findRowIndex(t);
36011         var cell = v.findCellIndex(t);
36012         if(row !== false && cell !== false){
36013             this.select(row, cell);
36014         }
36015     },
36016
36017     /**
36018      * Selects a cell.
36019      * @param {Number} rowIndex
36020      * @param {Number} collIndex
36021      */
36022     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36023         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36024             this.clearSelections();
36025             r = r || this.grid.dataSource.getAt(rowIndex);
36026             this.selection = {
36027                 record : r,
36028                 cell : [rowIndex, colIndex]
36029             };
36030             if(!preventViewNotify){
36031                 var v = this.grid.getView();
36032                 v.onCellSelect(rowIndex, colIndex);
36033                 if(preventFocus !== true){
36034                     v.focusCell(rowIndex, colIndex);
36035                 }
36036             }
36037             this.fireEvent("cellselect", this, rowIndex, colIndex);
36038             this.fireEvent("selectionchange", this, this.selection);
36039         }
36040     },
36041
36042         //private
36043     isSelectable : function(rowIndex, colIndex, cm){
36044         return !cm.isHidden(colIndex);
36045     },
36046
36047     /** @ignore */
36048     handleKeyDown : function(e){
36049         Roo.log('Cell Sel Model handleKeyDown');
36050         if(!e.isNavKeyPress()){
36051             return;
36052         }
36053         var g = this.grid, s = this.selection;
36054         if(!s){
36055             e.stopEvent();
36056             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36057             if(cell){
36058                 this.select(cell[0], cell[1]);
36059             }
36060             return;
36061         }
36062         var sm = this;
36063         var walk = function(row, col, step){
36064             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36065         };
36066         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36067         var newCell;
36068
36069         switch(k){
36070             case e.TAB:
36071                 // handled by onEditorKey
36072                 if (g.isEditor && g.editing) {
36073                     return;
36074                 }
36075                 if(e.shiftKey){
36076                      newCell = walk(r, c-1, -1);
36077                 }else{
36078                      newCell = walk(r, c+1, 1);
36079                 }
36080              break;
36081              case e.DOWN:
36082                  newCell = walk(r+1, c, 1);
36083              break;
36084              case e.UP:
36085                  newCell = walk(r-1, c, -1);
36086              break;
36087              case e.RIGHT:
36088                  newCell = walk(r, c+1, 1);
36089              break;
36090              case e.LEFT:
36091                  newCell = walk(r, c-1, -1);
36092              break;
36093              case e.ENTER:
36094                  if(g.isEditor && !g.editing){
36095                     g.startEditing(r, c);
36096                     e.stopEvent();
36097                     return;
36098                 }
36099              break;
36100         };
36101         if(newCell){
36102             this.select(newCell[0], newCell[1]);
36103             e.stopEvent();
36104         }
36105     },
36106
36107     acceptsNav : function(row, col, cm){
36108         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36109     },
36110
36111     onEditorKey : function(field, e){
36112         
36113         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36114         ///Roo.log('onEditorKey' + k);
36115         
36116         if(k == e.TAB){
36117             if(e.shiftKey){
36118                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36119             }else{
36120                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36121             }
36122             e.stopEvent();
36123         }else if(k == e.ENTER && !e.ctrlKey){
36124             ed.completeEdit();
36125             e.stopEvent();
36126             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36127         }else if(k == e.ESC){
36128             ed.cancelEdit();
36129         }
36130         
36131         
36132         if(newCell){
36133             //Roo.log('next cell after edit');
36134             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36135         }
36136     }
36137 });/*
36138  * Based on:
36139  * Ext JS Library 1.1.1
36140  * Copyright(c) 2006-2007, Ext JS, LLC.
36141  *
36142  * Originally Released Under LGPL - original licence link has changed is not relivant.
36143  *
36144  * Fork - LGPL
36145  * <script type="text/javascript">
36146  */
36147  
36148 /**
36149  * @class Roo.grid.EditorGrid
36150  * @extends Roo.grid.Grid
36151  * Class for creating and editable grid.
36152  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36153  * The container MUST have some type of size defined for the grid to fill. The container will be 
36154  * automatically set to position relative if it isn't already.
36155  * @param {Object} dataSource The data model to bind to
36156  * @param {Object} colModel The column model with info about this grid's columns
36157  */
36158 Roo.grid.EditorGrid = function(container, config){
36159     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36160     this.getGridEl().addClass("xedit-grid");
36161
36162     if(!this.selModel){
36163         this.selModel = new Roo.grid.CellSelectionModel();
36164     }
36165
36166     this.activeEditor = null;
36167
36168         this.addEvents({
36169             /**
36170              * @event beforeedit
36171              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36172              * <ul style="padding:5px;padding-left:16px;">
36173              * <li>grid - This grid</li>
36174              * <li>record - The record being edited</li>
36175              * <li>field - The field name being edited</li>
36176              * <li>value - The value for the field being edited.</li>
36177              * <li>row - The grid row index</li>
36178              * <li>column - The grid column index</li>
36179              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36180              * </ul>
36181              * @param {Object} e An edit event (see above for description)
36182              */
36183             "beforeedit" : true,
36184             /**
36185              * @event afteredit
36186              * Fires after a cell is edited. <br />
36187              * <ul style="padding:5px;padding-left:16px;">
36188              * <li>grid - This grid</li>
36189              * <li>record - The record being edited</li>
36190              * <li>field - The field name being edited</li>
36191              * <li>value - The value being set</li>
36192              * <li>originalValue - The original value for the field, before the edit.</li>
36193              * <li>row - The grid row index</li>
36194              * <li>column - The grid column index</li>
36195              * </ul>
36196              * @param {Object} e An edit event (see above for description)
36197              */
36198             "afteredit" : true,
36199             /**
36200              * @event validateedit
36201              * Fires after a cell is edited, but before the value is set in the record. 
36202          * You can use this to modify the value being set in the field, Return false
36203              * to cancel the change. The edit event object has the following properties <br />
36204              * <ul style="padding:5px;padding-left:16px;">
36205          * <li>editor - This editor</li>
36206              * <li>grid - This grid</li>
36207              * <li>record - The record being edited</li>
36208              * <li>field - The field name being edited</li>
36209              * <li>value - The value being set</li>
36210              * <li>originalValue - The original value for the field, before the edit.</li>
36211              * <li>row - The grid row index</li>
36212              * <li>column - The grid column index</li>
36213              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36214              * </ul>
36215              * @param {Object} e An edit event (see above for description)
36216              */
36217             "validateedit" : true
36218         });
36219     this.on("bodyscroll", this.stopEditing,  this);
36220     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36221 };
36222
36223 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36224     /**
36225      * @cfg {Number} clicksToEdit
36226      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36227      */
36228     clicksToEdit: 2,
36229
36230     // private
36231     isEditor : true,
36232     // private
36233     trackMouseOver: false, // causes very odd FF errors
36234
36235     onCellDblClick : function(g, row, col){
36236         this.startEditing(row, col);
36237     },
36238
36239     onEditComplete : function(ed, value, startValue){
36240         this.editing = false;
36241         this.activeEditor = null;
36242         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36243         var r = ed.record;
36244         var field = this.colModel.getDataIndex(ed.col);
36245         var e = {
36246             grid: this,
36247             record: r,
36248             field: field,
36249             originalValue: startValue,
36250             value: value,
36251             row: ed.row,
36252             column: ed.col,
36253             cancel:false,
36254             editor: ed
36255         };
36256         if(String(value) !== String(startValue)){
36257             
36258             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36259                 r.set(field, e.value);
36260                 // if we are dealing with a combo box..
36261                 // then we also set the 'name' colum to be the displayField
36262                 if (ed.field.displayField && ed.field.name) {
36263                     r.set(ed.field.name, ed.field.el.dom.value);
36264                 }
36265                 
36266                 delete e.cancel; //?? why!!!
36267                 this.fireEvent("afteredit", e);
36268             }
36269         } else {
36270             this.fireEvent("afteredit", e); // always fire it!
36271         }
36272         this.view.focusCell(ed.row, ed.col);
36273     },
36274
36275     /**
36276      * Starts editing the specified for the specified row/column
36277      * @param {Number} rowIndex
36278      * @param {Number} colIndex
36279      */
36280     startEditing : function(row, col){
36281         this.stopEditing();
36282         if(this.colModel.isCellEditable(col, row)){
36283             this.view.ensureVisible(row, col, true);
36284             var r = this.dataSource.getAt(row);
36285             var field = this.colModel.getDataIndex(col);
36286             var e = {
36287                 grid: this,
36288                 record: r,
36289                 field: field,
36290                 value: r.data[field],
36291                 row: row,
36292                 column: col,
36293                 cancel:false
36294             };
36295             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36296                 this.editing = true;
36297                 var ed = this.colModel.getCellEditor(col, row);
36298                 
36299                 if (!ed) {
36300                     return;
36301                 }
36302                 if(!ed.rendered){
36303                     ed.render(ed.parentEl || document.body);
36304                 }
36305                 ed.field.reset();
36306                 (function(){ // complex but required for focus issues in safari, ie and opera
36307                     ed.row = row;
36308                     ed.col = col;
36309                     ed.record = r;
36310                     ed.on("complete", this.onEditComplete, this, {single: true});
36311                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36312                     this.activeEditor = ed;
36313                     var v = r.data[field];
36314                     ed.startEdit(this.view.getCell(row, col), v);
36315                     // combo's with 'displayField and name set
36316                     if (ed.field.displayField && ed.field.name) {
36317                         ed.field.el.dom.value = r.data[ed.field.name];
36318                     }
36319                     
36320                     
36321                 }).defer(50, this);
36322             }
36323         }
36324     },
36325         
36326     /**
36327      * Stops any active editing
36328      */
36329     stopEditing : function(){
36330         if(this.activeEditor){
36331             this.activeEditor.completeEdit();
36332         }
36333         this.activeEditor = null;
36334     }
36335 });/*
36336  * Based on:
36337  * Ext JS Library 1.1.1
36338  * Copyright(c) 2006-2007, Ext JS, LLC.
36339  *
36340  * Originally Released Under LGPL - original licence link has changed is not relivant.
36341  *
36342  * Fork - LGPL
36343  * <script type="text/javascript">
36344  */
36345
36346 // private - not really -- you end up using it !
36347 // This is a support class used internally by the Grid components
36348
36349 /**
36350  * @class Roo.grid.GridEditor
36351  * @extends Roo.Editor
36352  * Class for creating and editable grid elements.
36353  * @param {Object} config any settings (must include field)
36354  */
36355 Roo.grid.GridEditor = function(field, config){
36356     if (!config && field.field) {
36357         config = field;
36358         field = Roo.factory(config.field, Roo.form);
36359     }
36360     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36361     field.monitorTab = false;
36362 };
36363
36364 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36365     
36366     /**
36367      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36368      */
36369     
36370     alignment: "tl-tl",
36371     autoSize: "width",
36372     hideEl : false,
36373     cls: "x-small-editor x-grid-editor",
36374     shim:false,
36375     shadow:"frame"
36376 });/*
36377  * Based on:
36378  * Ext JS Library 1.1.1
36379  * Copyright(c) 2006-2007, Ext JS, LLC.
36380  *
36381  * Originally Released Under LGPL - original licence link has changed is not relivant.
36382  *
36383  * Fork - LGPL
36384  * <script type="text/javascript">
36385  */
36386   
36387
36388   
36389 Roo.grid.PropertyRecord = Roo.data.Record.create([
36390     {name:'name',type:'string'},  'value'
36391 ]);
36392
36393
36394 Roo.grid.PropertyStore = function(grid, source){
36395     this.grid = grid;
36396     this.store = new Roo.data.Store({
36397         recordType : Roo.grid.PropertyRecord
36398     });
36399     this.store.on('update', this.onUpdate,  this);
36400     if(source){
36401         this.setSource(source);
36402     }
36403     Roo.grid.PropertyStore.superclass.constructor.call(this);
36404 };
36405
36406
36407
36408 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36409     setSource : function(o){
36410         this.source = o;
36411         this.store.removeAll();
36412         var data = [];
36413         for(var k in o){
36414             if(this.isEditableValue(o[k])){
36415                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36416             }
36417         }
36418         this.store.loadRecords({records: data}, {}, true);
36419     },
36420
36421     onUpdate : function(ds, record, type){
36422         if(type == Roo.data.Record.EDIT){
36423             var v = record.data['value'];
36424             var oldValue = record.modified['value'];
36425             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36426                 this.source[record.id] = v;
36427                 record.commit();
36428                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36429             }else{
36430                 record.reject();
36431             }
36432         }
36433     },
36434
36435     getProperty : function(row){
36436        return this.store.getAt(row);
36437     },
36438
36439     isEditableValue: function(val){
36440         if(val && val instanceof Date){
36441             return true;
36442         }else if(typeof val == 'object' || typeof val == 'function'){
36443             return false;
36444         }
36445         return true;
36446     },
36447
36448     setValue : function(prop, value){
36449         this.source[prop] = value;
36450         this.store.getById(prop).set('value', value);
36451     },
36452
36453     getSource : function(){
36454         return this.source;
36455     }
36456 });
36457
36458 Roo.grid.PropertyColumnModel = function(grid, store){
36459     this.grid = grid;
36460     var g = Roo.grid;
36461     g.PropertyColumnModel.superclass.constructor.call(this, [
36462         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36463         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36464     ]);
36465     this.store = store;
36466     this.bselect = Roo.DomHelper.append(document.body, {
36467         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36468             {tag: 'option', value: 'true', html: 'true'},
36469             {tag: 'option', value: 'false', html: 'false'}
36470         ]
36471     });
36472     Roo.id(this.bselect);
36473     var f = Roo.form;
36474     this.editors = {
36475         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36476         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36477         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36478         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36479         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36480     };
36481     this.renderCellDelegate = this.renderCell.createDelegate(this);
36482     this.renderPropDelegate = this.renderProp.createDelegate(this);
36483 };
36484
36485 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36486     
36487     
36488     nameText : 'Name',
36489     valueText : 'Value',
36490     
36491     dateFormat : 'm/j/Y',
36492     
36493     
36494     renderDate : function(dateVal){
36495         return dateVal.dateFormat(this.dateFormat);
36496     },
36497
36498     renderBool : function(bVal){
36499         return bVal ? 'true' : 'false';
36500     },
36501
36502     isCellEditable : function(colIndex, rowIndex){
36503         return colIndex == 1;
36504     },
36505
36506     getRenderer : function(col){
36507         return col == 1 ?
36508             this.renderCellDelegate : this.renderPropDelegate;
36509     },
36510
36511     renderProp : function(v){
36512         return this.getPropertyName(v);
36513     },
36514
36515     renderCell : function(val){
36516         var rv = val;
36517         if(val instanceof Date){
36518             rv = this.renderDate(val);
36519         }else if(typeof val == 'boolean'){
36520             rv = this.renderBool(val);
36521         }
36522         return Roo.util.Format.htmlEncode(rv);
36523     },
36524
36525     getPropertyName : function(name){
36526         var pn = this.grid.propertyNames;
36527         return pn && pn[name] ? pn[name] : name;
36528     },
36529
36530     getCellEditor : function(colIndex, rowIndex){
36531         var p = this.store.getProperty(rowIndex);
36532         var n = p.data['name'], val = p.data['value'];
36533         
36534         if(typeof(this.grid.customEditors[n]) == 'string'){
36535             return this.editors[this.grid.customEditors[n]];
36536         }
36537         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36538             return this.grid.customEditors[n];
36539         }
36540         if(val instanceof Date){
36541             return this.editors['date'];
36542         }else if(typeof val == 'number'){
36543             return this.editors['number'];
36544         }else if(typeof val == 'boolean'){
36545             return this.editors['boolean'];
36546         }else{
36547             return this.editors['string'];
36548         }
36549     }
36550 });
36551
36552 /**
36553  * @class Roo.grid.PropertyGrid
36554  * @extends Roo.grid.EditorGrid
36555  * This class represents the  interface of a component based property grid control.
36556  * <br><br>Usage:<pre><code>
36557  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36558       
36559  });
36560  // set any options
36561  grid.render();
36562  * </code></pre>
36563   
36564  * @constructor
36565  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36566  * The container MUST have some type of size defined for the grid to fill. The container will be
36567  * automatically set to position relative if it isn't already.
36568  * @param {Object} config A config object that sets properties on this grid.
36569  */
36570 Roo.grid.PropertyGrid = function(container, config){
36571     config = config || {};
36572     var store = new Roo.grid.PropertyStore(this);
36573     this.store = store;
36574     var cm = new Roo.grid.PropertyColumnModel(this, store);
36575     store.store.sort('name', 'ASC');
36576     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36577         ds: store.store,
36578         cm: cm,
36579         enableColLock:false,
36580         enableColumnMove:false,
36581         stripeRows:false,
36582         trackMouseOver: false,
36583         clicksToEdit:1
36584     }, config));
36585     this.getGridEl().addClass('x-props-grid');
36586     this.lastEditRow = null;
36587     this.on('columnresize', this.onColumnResize, this);
36588     this.addEvents({
36589          /**
36590              * @event beforepropertychange
36591              * Fires before a property changes (return false to stop?)
36592              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36593              * @param {String} id Record Id
36594              * @param {String} newval New Value
36595          * @param {String} oldval Old Value
36596              */
36597         "beforepropertychange": true,
36598         /**
36599              * @event propertychange
36600              * Fires after a property changes
36601              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36602              * @param {String} id Record Id
36603              * @param {String} newval New Value
36604          * @param {String} oldval Old Value
36605              */
36606         "propertychange": true
36607     });
36608     this.customEditors = this.customEditors || {};
36609 };
36610 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36611     
36612      /**
36613      * @cfg {Object} customEditors map of colnames=> custom editors.
36614      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36615      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36616      * false disables editing of the field.
36617          */
36618     
36619       /**
36620      * @cfg {Object} propertyNames map of property Names to their displayed value
36621          */
36622     
36623     render : function(){
36624         Roo.grid.PropertyGrid.superclass.render.call(this);
36625         this.autoSize.defer(100, this);
36626     },
36627
36628     autoSize : function(){
36629         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36630         if(this.view){
36631             this.view.fitColumns();
36632         }
36633     },
36634
36635     onColumnResize : function(){
36636         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36637         this.autoSize();
36638     },
36639     /**
36640      * Sets the data for the Grid
36641      * accepts a Key => Value object of all the elements avaiable.
36642      * @param {Object} data  to appear in grid.
36643      */
36644     setSource : function(source){
36645         this.store.setSource(source);
36646         //this.autoSize();
36647     },
36648     /**
36649      * Gets all the data from the grid.
36650      * @return {Object} data  data stored in grid
36651      */
36652     getSource : function(){
36653         return this.store.getSource();
36654     }
36655 });/*
36656  * Based on:
36657  * Ext JS Library 1.1.1
36658  * Copyright(c) 2006-2007, Ext JS, LLC.
36659  *
36660  * Originally Released Under LGPL - original licence link has changed is not relivant.
36661  *
36662  * Fork - LGPL
36663  * <script type="text/javascript">
36664  */
36665  
36666 /**
36667  * @class Roo.LoadMask
36668  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36669  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36670  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36671  * element's UpdateManager load indicator and will be destroyed after the initial load.
36672  * @constructor
36673  * Create a new LoadMask
36674  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36675  * @param {Object} config The config object
36676  */
36677 Roo.LoadMask = function(el, config){
36678     this.el = Roo.get(el);
36679     Roo.apply(this, config);
36680     if(this.store){
36681         this.store.on('beforeload', this.onBeforeLoad, this);
36682         this.store.on('load', this.onLoad, this);
36683         this.store.on('loadexception', this.onLoad, this);
36684         this.removeMask = false;
36685     }else{
36686         var um = this.el.getUpdateManager();
36687         um.showLoadIndicator = false; // disable the default indicator
36688         um.on('beforeupdate', this.onBeforeLoad, this);
36689         um.on('update', this.onLoad, this);
36690         um.on('failure', this.onLoad, this);
36691         this.removeMask = true;
36692     }
36693 };
36694
36695 Roo.LoadMask.prototype = {
36696     /**
36697      * @cfg {Boolean} removeMask
36698      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36699      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36700      */
36701     /**
36702      * @cfg {String} msg
36703      * The text to display in a centered loading message box (defaults to 'Loading...')
36704      */
36705     msg : 'Loading...',
36706     /**
36707      * @cfg {String} msgCls
36708      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36709      */
36710     msgCls : 'x-mask-loading',
36711
36712     /**
36713      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36714      * @type Boolean
36715      */
36716     disabled: false,
36717
36718     /**
36719      * Disables the mask to prevent it from being displayed
36720      */
36721     disable : function(){
36722        this.disabled = true;
36723     },
36724
36725     /**
36726      * Enables the mask so that it can be displayed
36727      */
36728     enable : function(){
36729         this.disabled = false;
36730     },
36731
36732     // private
36733     onLoad : function(){
36734         this.el.unmask(this.removeMask);
36735     },
36736
36737     // private
36738     onBeforeLoad : function(){
36739         if(!this.disabled){
36740             this.el.mask(this.msg, this.msgCls);
36741         }
36742     },
36743
36744     // private
36745     destroy : function(){
36746         if(this.store){
36747             this.store.un('beforeload', this.onBeforeLoad, this);
36748             this.store.un('load', this.onLoad, this);
36749             this.store.un('loadexception', this.onLoad, this);
36750         }else{
36751             var um = this.el.getUpdateManager();
36752             um.un('beforeupdate', this.onBeforeLoad, this);
36753             um.un('update', this.onLoad, this);
36754             um.un('failure', this.onLoad, this);
36755         }
36756     }
36757 };/*
36758  * Based on:
36759  * Ext JS Library 1.1.1
36760  * Copyright(c) 2006-2007, Ext JS, LLC.
36761  *
36762  * Originally Released Under LGPL - original licence link has changed is not relivant.
36763  *
36764  * Fork - LGPL
36765  * <script type="text/javascript">
36766  */
36767 Roo.XTemplate = function(){
36768     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36769     var s = this.html;
36770
36771     s = ['<tpl>', s, '</tpl>'].join('');
36772
36773     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36774
36775     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36776     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36777     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36778     var m, id = 0;
36779     var tpls = [];
36780
36781     while(m = s.match(re)){
36782        var m2 = m[0].match(nameRe);
36783        var m3 = m[0].match(ifRe);
36784        var m4 = m[0].match(execRe);
36785        var exp = null, fn = null, exec = null;
36786        var name = m2 && m2[1] ? m2[1] : '';
36787        if(m3){
36788            exp = m3 && m3[1] ? m3[1] : null;
36789            if(exp){
36790                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36791            }
36792        }
36793        if(m4){
36794            exp = m4 && m4[1] ? m4[1] : null;
36795            if(exp){
36796                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36797            }
36798        }
36799        if(name){
36800            switch(name){
36801                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36802                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36803                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36804            }
36805        }
36806        tpls.push({
36807             id: id,
36808             target: name,
36809             exec: exec,
36810             test: fn,
36811             body: m[1]||''
36812         });
36813        s = s.replace(m[0], '{xtpl'+ id + '}');
36814        ++id;
36815     }
36816     for(var i = tpls.length-1; i >= 0; --i){
36817         this.compileTpl(tpls[i]);
36818     }
36819     this.master = tpls[tpls.length-1];
36820     this.tpls = tpls;
36821 };
36822 Roo.extend(Roo.XTemplate, Roo.Template, {
36823
36824     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36825
36826     applySubTemplate : function(id, values, parent){
36827         var t = this.tpls[id];
36828         if(t.test && !t.test.call(this, values, parent)){
36829             return '';
36830         }
36831         if(t.exec && t.exec.call(this, values, parent)){
36832             return '';
36833         }
36834         var vs = t.target ? t.target.call(this, values, parent) : values;
36835         parent = t.target ? values : parent;
36836         if(t.target && vs instanceof Array){
36837             var buf = [];
36838             for(var i = 0, len = vs.length; i < len; i++){
36839                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36840             }
36841             return buf.join('');
36842         }
36843         return t.compiled.call(this, vs, parent);
36844     },
36845
36846     compileTpl : function(tpl){
36847         var fm = Roo.util.Format;
36848         var useF = this.disableFormats !== true;
36849         var sep = Roo.isGecko ? "+" : ",";
36850         var fn = function(m, name, format, args){
36851             if(name.substr(0, 4) == 'xtpl'){
36852                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36853             }
36854             var v;
36855             if(name.indexOf('.') != -1){
36856                 v = name;
36857             }else{
36858                 v = "values['" + name + "']";
36859             }
36860             if(format && useF){
36861                 args = args ? ',' + args : "";
36862                 if(format.substr(0, 5) != "this."){
36863                     format = "fm." + format + '(';
36864                 }else{
36865                     format = 'this.call("'+ format.substr(5) + '", ';
36866                     args = ", values";
36867                 }
36868             }else{
36869                 args= ''; format = "("+v+" === undefined ? '' : ";
36870             }
36871             return "'"+ sep + format + v + args + ")"+sep+"'";
36872         };
36873         var body;
36874         // branched to use + in gecko and [].join() in others
36875         if(Roo.isGecko){
36876             body = "tpl.compiled = function(values, parent){ return '" +
36877                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36878                     "';};";
36879         }else{
36880             body = ["tpl.compiled = function(values, parent){ return ['"];
36881             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36882             body.push("'].join('');};");
36883             body = body.join('');
36884         }
36885         /** eval:var:zzzzzzz */
36886         eval(body);
36887         return this;
36888     },
36889
36890     applyTemplate : function(values){
36891         return this.master.compiled.call(this, values, {});
36892         var s = this.subs;
36893     },
36894
36895     apply : function(){
36896         return this.applyTemplate.apply(this, arguments);
36897     },
36898
36899     compile : function(){return this;}
36900 });
36901
36902 Roo.XTemplate.from = function(el){
36903     el = Roo.getDom(el);
36904     return new Roo.XTemplate(el.value || el.innerHTML);
36905 };/*
36906  * Original code for Roojs - LGPL
36907  * <script type="text/javascript">
36908  */
36909  
36910 /**
36911  * @class Roo.XComponent
36912  * A delayed Element creator...
36913  * Or a way to group chunks of interface together.
36914  * 
36915  * Mypart.xyx = new Roo.XComponent({
36916
36917     parent : 'Mypart.xyz', // empty == document.element.!!
36918     order : '001',
36919     name : 'xxxx'
36920     region : 'xxxx'
36921     disabled : function() {} 
36922      
36923     tree : function() { // return an tree of xtype declared components
36924         var MODULE = this;
36925         return 
36926         {
36927             xtype : 'NestedLayoutPanel',
36928             // technicall
36929         }
36930      ]
36931  *})
36932  *
36933  *
36934  * It can be used to build a big heiracy, with parent etc.
36935  * or you can just use this to render a single compoent to a dom element
36936  * MYPART.render(Roo.Element | String(id) | dom_element )
36937  * 
36938  * @extends Roo.util.Observable
36939  * @constructor
36940  * @param cfg {Object} configuration of component
36941  * 
36942  */
36943 Roo.XComponent = function(cfg) {
36944     Roo.apply(this, cfg);
36945     this.addEvents({ 
36946         /**
36947              * @event built
36948              * Fires when this the componnt is built
36949              * @param {Roo.XComponent} c the component
36950              */
36951         'built' : true,
36952         /**
36953              * @event buildcomplete
36954              * Fires on the top level element when all elements have been built
36955              * @param {Roo.XComponent} c the top level component.
36956          */
36957         'buildcomplete' : true
36958         
36959     });
36960     this.region = this.region || 'center'; // default..
36961     Roo.XComponent.register(this);
36962     this.modules = false;
36963     this.el = false; // where the layout goes..
36964     
36965     
36966 }
36967 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36968     /**
36969      * @property el
36970      * The created element (with Roo.factory())
36971      * @type {Roo.Layout}
36972      */
36973     el  : false,
36974     
36975     /**
36976      * @property el
36977      * for BC  - use el in new code
36978      * @type {Roo.Layout}
36979      */
36980     panel : false,
36981     
36982     /**
36983      * @property layout
36984      * for BC  - use el in new code
36985      * @type {Roo.Layout}
36986      */
36987     layout : false,
36988     
36989      /**
36990      * @cfg {Function|boolean} disabled
36991      * If this module is disabled by some rule, return true from the funtion
36992      */
36993     disabled : false,
36994     
36995     /**
36996      * @cfg {String} parent 
36997      * Name of parent element which it get xtype added to..
36998      */
36999     parent: false,
37000     
37001     /**
37002      * @cfg {String} order
37003      * Used to set the order in which elements are created (usefull for multiple tabs)
37004      */
37005     
37006     order : false,
37007     /**
37008      * @cfg {String} name
37009      * String to display while loading.
37010      */
37011     name : false,
37012     /**
37013      * @cfg {String} region
37014      * Region to render component to (defaults to center)
37015      */
37016     region : 'center',
37017     
37018     /**
37019      * @cfg {Array} items
37020      * A single item array - the first element is the root of the tree..
37021      * It's done this way to stay compatible with the Xtype system...
37022      */
37023     items : false,
37024     
37025     
37026      /**
37027      * render
37028      * render element to dom or tree
37029      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
37030      */
37031     
37032     render : function(el)
37033     {
37034         
37035         el = el || false;
37036         var hp = this.parent ? 1 : 0;
37037         
37038         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
37039             // if parent is a '#.....' string, then let's use that..
37040             var ename = this.parent.substr(1)
37041             this.parent = false;
37042             el = Roo.get(ename);
37043             if (!el) {
37044                 Roo.log("Warning - element can not be found :#" + ename );
37045                 return;
37046             }
37047         }
37048         
37049         
37050         if (!this.parent) {
37051             
37052             el = el ? Roo.get(el) : false;
37053             
37054             // it's a top level one..
37055             this.parent =  {
37056                 el : new Roo.BorderLayout(el || document.body, {
37057                 
37058                      center: {
37059                          titlebar: false,
37060                          autoScroll:false,
37061                          closeOnTab: true,
37062                          tabPosition: 'top',
37063                           //resizeTabs: true,
37064                          alwaysShowTabs: el && hp? false :  true,
37065                          hideTabs: el || !hp ? true :  false,
37066                          minTabWidth: 140
37067                      }
37068                  })
37069             }
37070         }
37071         
37072         
37073             
37074         var tree = this.tree();
37075         tree.region = tree.region || this.region;
37076         this.el = this.parent.el.addxtype(tree);
37077         this.fireEvent('built', this);
37078         
37079         this.panel = this.el;
37080         this.layout = this.panel.layout;    
37081          
37082     }
37083     
37084 });
37085
37086 Roo.apply(Roo.XComponent, {
37087     
37088     /**
37089      * @property  buildCompleted
37090      * True when the builder has completed building the interface.
37091      * @type Boolean
37092      */
37093     buildCompleted : false,
37094      
37095     /**
37096      * @property  topModule
37097      * the upper most module - uses document.element as it's constructor.
37098      * @type Object
37099      */
37100      
37101     topModule  : false,
37102       
37103     /**
37104      * @property  modules
37105      * array of modules to be created by registration system.
37106      * @type {Array} of Roo.XComponent
37107      */
37108     
37109     modules : [],
37110     /**
37111      * @property  elmodules
37112      * array of modules to be created by which use #ID 
37113      * @type {Array} of Roo.XComponent
37114      */
37115      
37116     elmodules : [],
37117
37118     
37119     /**
37120      * Register components to be built later.
37121      *
37122      * This solves the following issues
37123      * - Building is not done on page load, but after an authentication process has occured.
37124      * - Interface elements are registered on page load
37125      * - Parent Interface elements may not be loaded before child, so this handles that..
37126      * 
37127      *
37128      * example:
37129      * 
37130      * MyApp.register({
37131           order : '000001',
37132           module : 'Pman.Tab.projectMgr',
37133           region : 'center',
37134           parent : 'Pman.layout',
37135           disabled : false,  // or use a function..
37136         })
37137      
37138      * * @param {Object} details about module
37139      */
37140     register : function(obj) {
37141         this.modules.push(obj);
37142          
37143     },
37144     /**
37145      * convert a string to an object..
37146      * eg. 'AAA.BBB' -> finds AAA.BBB
37147
37148      */
37149     
37150     toObject : function(str)
37151     {
37152         if (!str || typeof(str) == 'object') {
37153             return str;
37154         }
37155         if (str.substring(0,1) == '#') {
37156             return str;
37157         }
37158
37159         var ar = str.split('.');
37160         var rt, o;
37161         rt = ar.shift();
37162             /** eval:var:o */
37163         try {
37164             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
37165         } catch (e) {
37166             throw "Module not found : " + str;
37167         }
37168         
37169         if (o === false) {
37170             throw "Module not found : " + str;
37171         }
37172         Roo.each(ar, function(e) {
37173             if (typeof(o[e]) == 'undefined') {
37174                 throw "Module not found : " + str;
37175             }
37176             o = o[e];
37177         });
37178         
37179         return o;
37180         
37181     },
37182     
37183     
37184     /**
37185      * move modules into their correct place in the tree..
37186      * 
37187      */
37188     preBuild : function ()
37189     {
37190         var _t = this;
37191         Roo.each(this.modules , function (obj)
37192         {
37193             var opar = obj.parent;
37194             try { 
37195                 obj.parent = this.toObject(opar);
37196             } catch(e) {
37197                 Roo.log(e.toString());
37198                 return;
37199             }
37200             
37201             if (!obj.parent) {
37202                 this.topModule = obj;
37203                 return;
37204             }
37205             if (typeof(obj.parent) == 'string') {
37206                 this.elmodules.push(obj);
37207                 return;
37208             }
37209             if (obj.parent.constructor != Roo.XComponent) {
37210                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
37211             }
37212             if (!obj.parent.modules) {
37213                 obj.parent.modules = new Roo.util.MixedCollection(false, 
37214                     function(o) { return o.order + '' }
37215                 );
37216             }
37217             
37218             obj.parent.modules.add(obj);
37219         }, this);
37220     },
37221     
37222      /**
37223      * make a list of modules to build.
37224      * @return {Array} list of modules. 
37225      */ 
37226     
37227     buildOrder : function()
37228     {
37229         var _this = this;
37230         var cmp = function(a,b) {   
37231             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
37232         };
37233         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
37234             throw "No top level modules to build";
37235         }
37236         
37237         // make a flat list in order of modules to build.
37238         var mods = this.topModule ? [ this.topModule ] : [];
37239         Roo.each(this.elmodules,function(e) { mods.push(e) });
37240
37241         
37242         // add modules to their parents..
37243         var addMod = function(m) {
37244            // Roo.debug && Roo.log(m.modKey);
37245             
37246             mods.push(m);
37247             if (m.modules) {
37248                 m.modules.keySort('ASC',  cmp );
37249                 m.modules.each(addMod);
37250             }
37251             // not sure if this is used any more..
37252             if (m.finalize) {
37253                 m.finalize.name = m.name + " (clean up) ";
37254                 mods.push(m.finalize);
37255             }
37256             
37257         }
37258         if (this.topModule) { 
37259             this.topModule.modules.keySort('ASC',  cmp );
37260             this.topModule.modules.each(addMod);
37261         }
37262         return mods;
37263     },
37264     
37265      /**
37266      * Build the registered modules.
37267      * @param {Object} parent element.
37268      * @param {Function} optional method to call after module has been added.
37269      * 
37270      */ 
37271    
37272     build : function() 
37273     {
37274         
37275         this.preBuild();
37276         var mods = this.buildOrder();
37277       
37278         //this.allmods = mods;
37279         //Roo.debug && Roo.log(mods);
37280         //return;
37281         if (!mods.length) { // should not happen
37282             throw "NO modules!!!";
37283         }
37284         
37285         
37286         
37287         // flash it up as modal - so we store the mask!?
37288         Roo.MessageBox.show({ title: 'loading' });
37289         Roo.MessageBox.show({
37290            title: "Please wait...",
37291            msg: "Building Interface...",
37292            width:450,
37293            progress:true,
37294            closable:false,
37295            modal: false
37296           
37297         });
37298         var total = mods.length;
37299         
37300         var _this = this;
37301         var progressRun = function() {
37302             if (!mods.length) {
37303                 Roo.debug && Roo.log('hide?');
37304                 Roo.MessageBox.hide();
37305                 if (_this.topModule) { 
37306                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
37307                 }
37308                 // THE END...
37309                 return false;   
37310             }
37311             
37312             var m = mods.shift();
37313             
37314             
37315             Roo.debug && Roo.log(m);
37316             // not sure if this is supported any more.. - modules that are are just function
37317             if (typeof(m) == 'function') { 
37318                 m.call(this);
37319                 return progressRun.defer(10, _this);
37320             } 
37321             
37322             
37323             
37324             Roo.MessageBox.updateProgress(
37325                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
37326                     " of " + total + 
37327                     (m.name ? (' - ' + m.name) : '')
37328                     );
37329             
37330          
37331             // is the module disabled?
37332             var disabled = (typeof(m.disabled) == 'function') ?
37333                 m.disabled.call(m.module.disabled) : m.disabled;    
37334             
37335             
37336             if (disabled) {
37337                 return progressRun(); // we do not update the display!
37338             }
37339             
37340             // now build 
37341             
37342             m.render();
37343             // it's 10 on top level, and 1 on others??? why...
37344             return progressRun.defer(10, _this);
37345              
37346         }
37347         progressRun.defer(1, _this);
37348      
37349         
37350         
37351     }
37352     
37353      
37354    
37355     
37356     
37357 });
37358  //<script type="text/javascript">
37359
37360
37361 /**
37362  * @class Roo.Login
37363  * @extends Roo.LayoutDialog
37364  * A generic Login Dialog..... - only one needed in theory!?!?
37365  *
37366  * Fires XComponent builder on success...
37367  * 
37368  * Sends 
37369  *    username,password, lang = for login actions.
37370  *    check = 1 for periodic checking that sesion is valid.
37371  *    passwordRequest = email request password
37372  *    logout = 1 = to logout
37373  * 
37374  * Affects: (this id="????" elements)
37375  *   loading  (removed) (used to indicate application is loading)
37376  *   loading-mask (hides) (used to hide application when it's building loading)
37377  *   
37378  * 
37379  * Usage: 
37380  *    
37381  * 
37382  * Myapp.login = Roo.Login({
37383      url: xxxx,
37384    
37385      realm : 'Myapp', 
37386      
37387      
37388      method : 'POST',
37389      
37390      
37391      * 
37392  })
37393  * 
37394  * 
37395  * 
37396  **/
37397  
37398 Roo.Login = function(cfg)
37399 {
37400     this.addEvents({
37401         'refreshed' : true
37402     });
37403     
37404     Roo.apply(this,cfg);
37405     
37406     Roo.onReady(function() {
37407         this.onLoad();
37408     }, this);
37409     // call parent..
37410     
37411    
37412     Roo.Login.superclass.constructor.call(this, this);
37413     //this.addxtype(this.items[0]);
37414     
37415     
37416 }
37417
37418
37419 Roo.extend(Roo.Login, Roo.LayoutDialog, {
37420     
37421     /**
37422      * @cfg {String} method
37423      * Method used to query for login details.
37424      */
37425     
37426     method : 'POST',
37427     /**
37428      * @cfg {String} url
37429      * URL to query login data. - eg. baseURL + '/Login.php'
37430      */
37431     url : '',
37432     
37433     /**
37434      * @property user
37435      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
37436      * @type {Object} 
37437      */
37438     user : false,
37439     /**
37440      * @property checkFails
37441      * Number of times we have attempted to get authentication check, and failed.
37442      * @type {Number} 
37443      */
37444     checkFails : 0,
37445       /**
37446      * @property intervalID
37447      * The window interval that does the constant login checking.
37448      * @type {Number} 
37449      */
37450     intervalID : 0,
37451     
37452     
37453     onLoad : function() // called on page load...
37454     {
37455         // load 
37456          
37457         if (Roo.get('loading')) { // clear any loading indicator..
37458             Roo.get('loading').remove();
37459         }
37460         
37461         //this.switchLang('en'); // set the language to english..
37462        
37463         this.check({
37464             success:  function(response, opts)  {  // check successfull...
37465             
37466                 var res = this.processResponse(response);
37467                 this.checkFails =0;
37468                 if (!res.success) { // error!
37469                     this.checkFails = 5;
37470                     //console.log('call failure');
37471                     return this.failure(response,opts);
37472                 }
37473                 
37474                 if (!res.data.id) { // id=0 == login failure.
37475                     return this.show();
37476                 }
37477                 
37478                               
37479                         //console.log(success);
37480                 this.fillAuth(res.data);   
37481                 this.checkFails =0;
37482                 Roo.XComponent.build();
37483             },
37484             failure : this.show
37485         });
37486         
37487     }, 
37488     
37489     
37490     check: function(cfg) // called every so often to refresh cookie etc..
37491     {
37492         if (cfg.again) { // could be undefined..
37493             this.checkFails++;
37494         } else {
37495             this.checkFails = 0;
37496         }
37497         var _this = this;
37498         if (this.sending) {
37499             if ( this.checkFails > 4) {
37500                 Roo.MessageBox.alert("Error",  
37501                     "Error getting authentication status. - try reloading, or wait a while", function() {
37502                         _this.sending = false;
37503                     }); 
37504                 return;
37505             }
37506             cfg.again = true;
37507             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
37508             return;
37509         }
37510         this.sending = true;
37511         
37512         Roo.Ajax.request({  
37513             url: this.url,
37514             params: {
37515                 getAuthUser: true
37516             },  
37517             method: this.method,
37518             success:  cfg.success || this.success,
37519             failure : cfg.failure || this.failure,
37520             scope : this,
37521             callCfg : cfg
37522               
37523         });  
37524     }, 
37525     
37526     
37527     logout: function()
37528     {
37529         window.onbeforeunload = function() { }; // false does not work for IE..
37530         this.user = false;
37531         var _this = this;
37532         
37533         Roo.Ajax.request({  
37534             url: this.url,
37535             params: {
37536                 logout: 1
37537             },  
37538             method: 'GET',
37539             failure : function() {
37540                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
37541                     document.location = document.location.toString() + '?ts=' + Math.random();
37542                 });
37543                 
37544             },
37545             success : function() {
37546                 _this.user = false;
37547                 this.checkFails =0;
37548                 // fixme..
37549                 document.location = document.location.toString() + '?ts=' + Math.random();
37550             }
37551               
37552               
37553         }); 
37554     },
37555     
37556     processResponse : function (response)
37557     {
37558         var res = '';
37559         try {
37560             res = Roo.decode(response.responseText);
37561             // oops...
37562             if (typeof(res) != 'object') {
37563                 res = { success : false, errorMsg : res, errors : true };
37564             }
37565             if (typeof(res.success) == 'undefined') {
37566                 res.success = false;
37567             }
37568             
37569         } catch(e) {
37570             res = { success : false,  errorMsg : response.responseText, errors : true };
37571         }
37572         return res;
37573     },
37574     
37575     success : function(response, opts)  // check successfull...
37576     {  
37577         this.sending = false;
37578         var res = this.processResponse(response);
37579         if (!res.success) {
37580             return this.failure(response, opts);
37581         }
37582         if (!res.data || !res.data.id) {
37583             return this.failure(response,opts);
37584         }
37585         //console.log(res);
37586         this.fillAuth(res.data);
37587         
37588         this.checkFails =0;
37589         
37590     },
37591     
37592     
37593     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37594     {
37595         this.authUser = -1;
37596         this.sending = false;
37597         var res = this.processResponse(response);
37598         //console.log(res);
37599         if ( this.checkFails > 2) {
37600         
37601             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37602                 "Error getting authentication status. - try reloading"); 
37603             return;
37604         }
37605         opts.callCfg.again = true;
37606         this.check.defer(1000, this, [ opts.callCfg ]);
37607         return;  
37608     },
37609     
37610     
37611     
37612     fillAuth: function(au) {
37613         this.startAuthCheck();
37614         this.authUserId = au.id;
37615         this.authUser = au;
37616         this.lastChecked = new Date();
37617         this.fireEvent('refreshed', au);
37618         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37619         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37620         au.lang = au.lang || 'en';
37621         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37622         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37623         this.switchLang(au.lang );
37624         
37625      
37626         // open system... - -on setyp..
37627         if (this.authUserId  < 0) {
37628             Roo.MessageBox.alert("Warning", 
37629                 "This is an open system - please set up a admin user with a password.");  
37630         }
37631          
37632         //Pman.onload(); // which should do nothing if it's a re-auth result...
37633         
37634              
37635     },
37636     
37637     startAuthCheck : function() // starter for timeout checking..
37638     {
37639         if (this.intervalID) { // timer already in place...
37640             return false;
37641         }
37642         var _this = this;
37643         this.intervalID =  window.setInterval(function() {
37644               _this.check(false);
37645             }, 120000); // every 120 secs = 2mins..
37646         
37647         
37648     },
37649          
37650     
37651     switchLang : function (lang) 
37652     {
37653         _T = typeof(_T) == 'undefined' ? false : _T;
37654           if (!_T || !lang.length) {
37655             return;
37656         }
37657         
37658         if (!_T && lang != 'en') {
37659             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37660             return;
37661         }
37662         
37663         if (typeof(_T.en) == 'undefined') {
37664             _T.en = {};
37665             Roo.apply(_T.en, _T);
37666         }
37667         
37668         if (typeof(_T[lang]) == 'undefined') {
37669             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37670             return;
37671         }
37672         
37673         
37674         Roo.apply(_T, _T[lang]);
37675         // just need to set the text values for everything...
37676         var _this = this;
37677         /* this will not work ...
37678         if (this.form) { 
37679             
37680                
37681             function formLabel(name, val) {
37682                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37683             }
37684             
37685             formLabel('password', "Password"+':');
37686             formLabel('username', "Email Address"+':');
37687             formLabel('lang', "Language"+':');
37688             this.dialog.setTitle("Login");
37689             this.dialog.buttons[0].setText("Forgot Password");
37690             this.dialog.buttons[1].setText("Login");
37691         }
37692         */
37693         
37694         
37695     },
37696     
37697     
37698     title: "Login",
37699     modal: true,
37700     width:  350,
37701     //height: 230,
37702     height: 180,
37703     shadow: true,
37704     minWidth:200,
37705     minHeight:180,
37706     //proxyDrag: true,
37707     closable: false,
37708     draggable: false,
37709     collapsible: false,
37710     resizable: false,
37711     center: {  // needed??
37712         autoScroll:false,
37713         titlebar: false,
37714        // tabPosition: 'top',
37715         hideTabs: true,
37716         closeOnTab: true,
37717         alwaysShowTabs: false
37718     } ,
37719     listeners : {
37720         
37721         show  : function(dlg)
37722         {
37723             //console.log(this);
37724             this.form = this.layout.getRegion('center').activePanel.form;
37725             this.form.dialog = dlg;
37726             this.buttons[0].form = this.form;
37727             this.buttons[0].dialog = dlg;
37728             this.buttons[1].form = this.form;
37729             this.buttons[1].dialog = dlg;
37730            
37731            //this.resizeToLogo.defer(1000,this);
37732             // this is all related to resizing for logos..
37733             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37734            //// if (!sz) {
37735              //   this.resizeToLogo.defer(1000,this);
37736              //   return;
37737            // }
37738             //var w = Ext.lib.Dom.getViewWidth() - 100;
37739             //var h = Ext.lib.Dom.getViewHeight() - 100;
37740             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37741             //this.center();
37742             if (this.disabled) {
37743                 this.hide();
37744                 return;
37745             }
37746             
37747             if (this.user.id < 0) { // used for inital setup situations.
37748                 return;
37749             }
37750             
37751             if (this.intervalID) {
37752                 // remove the timer
37753                 window.clearInterval(this.intervalID);
37754                 this.intervalID = false;
37755             }
37756             
37757             
37758             if (Roo.get('loading')) {
37759                 Roo.get('loading').remove();
37760             }
37761             if (Roo.get('loading-mask')) {
37762                 Roo.get('loading-mask').hide();
37763             }
37764             
37765             //incomming._node = tnode;
37766             this.form.reset();
37767             //this.dialog.modal = !modal;
37768             //this.dialog.show();
37769             this.el.unmask(); 
37770             
37771             
37772             this.form.setValues({
37773                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37774                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37775             });
37776             
37777             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37778             if (this.form.findField('username').getValue().length > 0 ){
37779                 this.form.findField('password').focus();
37780             } else {
37781                this.form.findField('username').focus();
37782             }
37783     
37784         }
37785     },
37786     items : [
37787          {
37788        
37789             xtype : 'ContentPanel',
37790             xns : Roo,
37791             region: 'center',
37792             fitToFrame : true,
37793             
37794             items : [
37795     
37796                 {
37797                
37798                     xtype : 'Form',
37799                     xns : Roo.form,
37800                     labelWidth: 100,
37801                     style : 'margin: 10px;',
37802                     
37803                     listeners : {
37804                         actionfailed : function(f, act) {
37805                             // form can return { errors: .... }
37806                                 
37807                             //act.result.errors // invalid form element list...
37808                             //act.result.errorMsg// invalid form element list...
37809                             
37810                             this.dialog.el.unmask();
37811                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37812                                         "Login failed - communication error - try again.");
37813                                       
37814                         },
37815                         actioncomplete: function(re, act) {
37816                              
37817                             Roo.state.Manager.set(
37818                                 this.dialog.realm + '.username',  
37819                                     this.findField('username').getValue()
37820                             );
37821                             Roo.state.Manager.set(
37822                                 this.dialog.realm + '.lang',  
37823                                 this.findField('lang').getValue() 
37824                             );
37825                             
37826                             this.dialog.fillAuth(act.result.data);
37827                               
37828                             this.dialog.hide();
37829                             
37830                             if (Roo.get('loading-mask')) {
37831                                 Roo.get('loading-mask').show();
37832                             }
37833                             Roo.XComponent.build();
37834                             
37835                              
37836                             
37837                         }
37838                     },
37839                     items : [
37840                         {
37841                             xtype : 'TextField',
37842                             xns : Roo.form,
37843                             fieldLabel: "Email Address",
37844                             name: 'username',
37845                             width:200,
37846                             autoCreate : {tag: "input", type: "text", size: "20"}
37847                         },
37848                         {
37849                             xtype : 'TextField',
37850                             xns : Roo.form,
37851                             fieldLabel: "Password",
37852                             inputType: 'password',
37853                             name: 'password',
37854                             width:200,
37855                             autoCreate : {tag: "input", type: "text", size: "20"},
37856                             listeners : {
37857                                 specialkey : function(e,ev) {
37858                                     if (ev.keyCode == 13) {
37859                                         this.form.dialog.el.mask("Logging in");
37860                                         this.form.doAction('submit', {
37861                                             url: this.form.dialog.url,
37862                                             method: this.form.dialog.method
37863                                         });
37864                                     }
37865                                 }
37866                             }  
37867                         },
37868                         {
37869                             xtype : 'ComboBox',
37870                             xns : Roo.form,
37871                             fieldLabel: "Language",
37872                             name : 'langdisp',
37873                             store: {
37874                                 xtype : 'SimpleStore',
37875                                 fields: ['lang', 'ldisp'],
37876                                 data : [
37877                                     [ 'en', 'English' ],
37878                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37879                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37880                                 ]
37881                             },
37882                             
37883                             valueField : 'lang',
37884                             hiddenName:  'lang',
37885                             width: 200,
37886                             displayField:'ldisp',
37887                             typeAhead: false,
37888                             editable: false,
37889                             mode: 'local',
37890                             triggerAction: 'all',
37891                             emptyText:'Select a Language...',
37892                             selectOnFocus:true,
37893                             listeners : {
37894                                 select :  function(cb, rec, ix) {
37895                                     this.form.switchLang(rec.data.lang);
37896                                 }
37897                             }
37898                         
37899                         }
37900                     ]
37901                 }
37902                   
37903                 
37904             ]
37905         }
37906     ],
37907     buttons : [
37908         {
37909             xtype : 'Button',
37910             xns : 'Roo',
37911             text : "Forgot Password",
37912             listeners : {
37913                 click : function() {
37914                     //console.log(this);
37915                     var n = this.form.findField('username').getValue();
37916                     if (!n.length) {
37917                         Roo.MessageBox.alert("Error", "Fill in your email address");
37918                         return;
37919                     }
37920                     Roo.Ajax.request({
37921                         url: this.dialog.url,
37922                         params: {
37923                             passwordRequest: n
37924                         },
37925                         method: this.dialog.method,
37926                         success:  function(response, opts)  {  // check successfull...
37927                         
37928                             var res = this.dialog.processResponse(response);
37929                             if (!res.success) { // error!
37930                                Roo.MessageBox.alert("Error" ,
37931                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37932                                return;
37933                             }
37934                             Roo.MessageBox.alert("Notice" ,
37935                                 "Please check you email for the Password Reset message");
37936                         },
37937                         failure : function() {
37938                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37939                         }
37940                         
37941                     });
37942                 }
37943             }
37944         },
37945         {
37946             xtype : 'Button',
37947             xns : 'Roo',
37948             text : "Login",
37949             listeners : {
37950                 
37951                 click : function () {
37952                         
37953                     this.dialog.el.mask("Logging in");
37954                     this.form.doAction('submit', {
37955                             url: this.dialog.url,
37956                             method: this.dialog.method
37957                     });
37958                 }
37959             }
37960         }
37961     ]
37962   
37963   
37964 })
37965  
37966
37967
37968