Roo/form/ComboBoxArray.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing && this.store){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             // show a message if no listener is registered.
5064             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5065                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5066             }
5067             // loadmask wil be hooked into this..
5068             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5069             return;
5070         }
5071         var r = o.records, t = o.totalRecords || r.length;
5072         if(!options || options.add !== true){
5073             if(this.pruneModifiedRecords){
5074                 this.modified = [];
5075             }
5076             for(var i = 0, len = r.length; i < len; i++){
5077                 r[i].join(this);
5078             }
5079             if(this.snapshot){
5080                 this.data = this.snapshot;
5081                 delete this.snapshot;
5082             }
5083             this.data.clear();
5084             this.data.addAll(r);
5085             this.totalLength = t;
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.totalLength = Math.max(t, this.data.length+r.length);
5090             this.add(r);
5091         }
5092         this.fireEvent("load", this, r, options);
5093         if(options.callback){
5094             options.callback.call(options.scope || this, r, options, true);
5095         }
5096     },
5097
5098
5099     /**
5100      * Loads data from a passed data block. A Reader which understands the format of the data
5101      * must have been configured in the constructor.
5102      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5103      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5104      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5105      */
5106     loadData : function(o, append){
5107         var r = this.reader.readRecords(o);
5108         this.loadRecords(r, {add: append}, true);
5109     },
5110
5111     /**
5112      * Gets the number of cached records.
5113      * <p>
5114      * <em>If using paging, this may not be the total size of the dataset. If the data object
5115      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5116      * the data set size</em>
5117      */
5118     getCount : function(){
5119         return this.data.length || 0;
5120     },
5121
5122     /**
5123      * Gets the total number of records in the dataset as returned by the server.
5124      * <p>
5125      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5126      * the dataset size</em>
5127      */
5128     getTotalCount : function(){
5129         return this.totalLength || 0;
5130     },
5131
5132     /**
5133      * Returns the sort state of the Store as an object with two properties:
5134      * <pre><code>
5135  field {String} The name of the field by which the Records are sorted
5136  direction {String} The sort order, "ASC" or "DESC"
5137      * </code></pre>
5138      */
5139     getSortState : function(){
5140         return this.sortInfo;
5141     },
5142
5143     // private
5144     applySort : function(){
5145         if(this.sortInfo && !this.remoteSort){
5146             var s = this.sortInfo, f = s.field;
5147             var st = this.fields.get(f).sortType;
5148             var fn = function(r1, r2){
5149                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5150                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5151             };
5152             this.data.sort(s.direction, fn);
5153             if(this.snapshot && this.snapshot != this.data){
5154                 this.snapshot.sort(s.direction, fn);
5155             }
5156         }
5157     },
5158
5159     /**
5160      * Sets the default sort column and order to be used by the next load operation.
5161      * @param {String} fieldName The name of the field to sort by.
5162      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5163      */
5164     setDefaultSort : function(field, dir){
5165         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5166     },
5167
5168     /**
5169      * Sort the Records.
5170      * If remote sorting is used, the sort is performed on the server, and the cache is
5171      * reloaded. If local sorting is used, the cache is sorted internally.
5172      * @param {String} fieldName The name of the field to sort by.
5173      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5174      */
5175     sort : function(fieldName, dir){
5176         var f = this.fields.get(fieldName);
5177         if(!dir){
5178             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5179             
5180             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5181                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5182             }else{
5183                 dir = f.sortDir;
5184             }
5185         }
5186         this.sortToggle[f.name] = dir;
5187         this.sortInfo = {field: f.name, direction: dir};
5188         if(!this.remoteSort){
5189             this.applySort();
5190             this.fireEvent("datachanged", this);
5191         }else{
5192             this.load(this.lastOptions);
5193         }
5194     },
5195
5196     /**
5197      * Calls the specified function for each of the Records in the cache.
5198      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5199      * Returning <em>false</em> aborts and exits the iteration.
5200      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5201      */
5202     each : function(fn, scope){
5203         this.data.each(fn, scope);
5204     },
5205
5206     /**
5207      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5208      * (e.g., during paging).
5209      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5210      */
5211     getModifiedRecords : function(){
5212         return this.modified;
5213     },
5214
5215     // private
5216     createFilterFn : function(property, value, anyMatch){
5217         if(!value.exec){ // not a regex
5218             value = String(value);
5219             if(value.length == 0){
5220                 return false;
5221             }
5222             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5223         }
5224         return function(r){
5225             return value.test(r.data[property]);
5226         };
5227     },
5228
5229     /**
5230      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5231      * @param {String} property A field on your records
5232      * @param {Number} start The record index to start at (defaults to 0)
5233      * @param {Number} end The last record index to include (defaults to length - 1)
5234      * @return {Number} The sum
5235      */
5236     sum : function(property, start, end){
5237         var rs = this.data.items, v = 0;
5238         start = start || 0;
5239         end = (end || end === 0) ? end : rs.length-1;
5240
5241         for(var i = start; i <= end; i++){
5242             v += (rs[i].data[property] || 0);
5243         }
5244         return v;
5245     },
5246
5247     /**
5248      * Filter the records by a specified property.
5249      * @param {String} field A field on your records
5250      * @param {String/RegExp} value Either a string that the field
5251      * should start with or a RegExp to test against the field
5252      * @param {Boolean} anyMatch True to match any part not just the beginning
5253      */
5254     filter : function(property, value, anyMatch){
5255         var fn = this.createFilterFn(property, value, anyMatch);
5256         return fn ? this.filterBy(fn) : this.clearFilter();
5257     },
5258
5259     /**
5260      * Filter by a function. The specified function will be called with each
5261      * record in this data source. If the function returns true the record is included,
5262      * otherwise it is filtered.
5263      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5264      * @param {Object} scope (optional) The scope of the function (defaults to this)
5265      */
5266     filterBy : function(fn, scope){
5267         this.snapshot = this.snapshot || this.data;
5268         this.data = this.queryBy(fn, scope||this);
5269         this.fireEvent("datachanged", this);
5270     },
5271
5272     /**
5273      * Query the records by a specified property.
5274      * @param {String} field A field on your records
5275      * @param {String/RegExp} value Either a string that the field
5276      * should start with or a RegExp to test against the field
5277      * @param {Boolean} anyMatch True to match any part not just the beginning
5278      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5279      */
5280     query : function(property, value, anyMatch){
5281         var fn = this.createFilterFn(property, value, anyMatch);
5282         return fn ? this.queryBy(fn) : this.data.clone();
5283     },
5284
5285     /**
5286      * Query by a function. The specified function will be called with each
5287      * record in this data source. If the function returns true the record is included
5288      * in the results.
5289      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5290      * @param {Object} scope (optional) The scope of the function (defaults to this)
5291       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5292      **/
5293     queryBy : function(fn, scope){
5294         var data = this.snapshot || this.data;
5295         return data.filterBy(fn, scope||this);
5296     },
5297
5298     /**
5299      * Collects unique values for a particular dataIndex from this store.
5300      * @param {String} dataIndex The property to collect
5301      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5302      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5303      * @return {Array} An array of the unique values
5304      **/
5305     collect : function(dataIndex, allowNull, bypassFilter){
5306         var d = (bypassFilter === true && this.snapshot) ?
5307                 this.snapshot.items : this.data.items;
5308         var v, sv, r = [], l = {};
5309         for(var i = 0, len = d.length; i < len; i++){
5310             v = d[i].data[dataIndex];
5311             sv = String(v);
5312             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5313                 l[sv] = true;
5314                 r[r.length] = v;
5315             }
5316         }
5317         return r;
5318     },
5319
5320     /**
5321      * Revert to a view of the Record cache with no filtering applied.
5322      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5323      */
5324     clearFilter : function(suppressEvent){
5325         if(this.snapshot && this.snapshot != this.data){
5326             this.data = this.snapshot;
5327             delete this.snapshot;
5328             if(suppressEvent !== true){
5329                 this.fireEvent("datachanged", this);
5330             }
5331         }
5332     },
5333
5334     // private
5335     afterEdit : function(record){
5336         if(this.modified.indexOf(record) == -1){
5337             this.modified.push(record);
5338         }
5339         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5340     },
5341     
5342     // private
5343     afterReject : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5346     },
5347
5348     // private
5349     afterCommit : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5352     },
5353
5354     /**
5355      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5356      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5357      */
5358     commitChanges : function(){
5359         var m = this.modified.slice(0);
5360         this.modified = [];
5361         for(var i = 0, len = m.length; i < len; i++){
5362             m[i].commit();
5363         }
5364     },
5365
5366     /**
5367      * Cancel outstanding changes on all changed records.
5368      */
5369     rejectChanges : function(){
5370         var m = this.modified.slice(0);
5371         this.modified = [];
5372         for(var i = 0, len = m.length; i < len; i++){
5373             m[i].reject();
5374         }
5375     },
5376
5377     onMetaChange : function(meta, rtype, o){
5378         this.recordType = rtype;
5379         this.fields = rtype.prototype.fields;
5380         delete this.snapshot;
5381         this.sortInfo = meta.sortInfo || this.sortInfo;
5382         this.modified = [];
5383         this.fireEvent('metachange', this, this.reader.meta);
5384     }
5385 });/*
5386  * Based on:
5387  * Ext JS Library 1.1.1
5388  * Copyright(c) 2006-2007, Ext JS, LLC.
5389  *
5390  * Originally Released Under LGPL - original licence link has changed is not relivant.
5391  *
5392  * Fork - LGPL
5393  * <script type="text/javascript">
5394  */
5395
5396 /**
5397  * @class Roo.data.SimpleStore
5398  * @extends Roo.data.Store
5399  * Small helper class to make creating Stores from Array data easier.
5400  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5401  * @cfg {Array} fields An array of field definition objects, or field name strings.
5402  * @cfg {Array} data The multi-dimensional array of data
5403  * @constructor
5404  * @param {Object} config
5405  */
5406 Roo.data.SimpleStore = function(config){
5407     Roo.data.SimpleStore.superclass.constructor.call(this, {
5408         isLocal : true,
5409         reader: new Roo.data.ArrayReader({
5410                 id: config.id
5411             },
5412             Roo.data.Record.create(config.fields)
5413         ),
5414         proxy : new Roo.data.MemoryProxy(config.data)
5415     });
5416     this.load();
5417 };
5418 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429 /**
5430 /**
5431  * @extends Roo.data.Store
5432  * @class Roo.data.JsonStore
5433  * Small helper class to make creating Stores for JSON data easier. <br/>
5434 <pre><code>
5435 var store = new Roo.data.JsonStore({
5436     url: 'get-images.php',
5437     root: 'images',
5438     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5439 });
5440 </code></pre>
5441  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5442  * JsonReader and HttpProxy (unless inline data is provided).</b>
5443  * @cfg {Array} fields An array of field definition objects, or field name strings.
5444  * @constructor
5445  * @param {Object} config
5446  */
5447 Roo.data.JsonStore = function(c){
5448     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5449         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5450         reader: new Roo.data.JsonReader(c, c.fields)
5451     }));
5452 };
5453 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5454  * Based on:
5455  * Ext JS Library 1.1.1
5456  * Copyright(c) 2006-2007, Ext JS, LLC.
5457  *
5458  * Originally Released Under LGPL - original licence link has changed is not relivant.
5459  *
5460  * Fork - LGPL
5461  * <script type="text/javascript">
5462  */
5463
5464  
5465 Roo.data.Field = function(config){
5466     if(typeof config == "string"){
5467         config = {name: config};
5468     }
5469     Roo.apply(this, config);
5470     
5471     if(!this.type){
5472         this.type = "auto";
5473     }
5474     
5475     var st = Roo.data.SortTypes;
5476     // named sortTypes are supported, here we look them up
5477     if(typeof this.sortType == "string"){
5478         this.sortType = st[this.sortType];
5479     }
5480     
5481     // set default sortType for strings and dates
5482     if(!this.sortType){
5483         switch(this.type){
5484             case "string":
5485                 this.sortType = st.asUCString;
5486                 break;
5487             case "date":
5488                 this.sortType = st.asDate;
5489                 break;
5490             default:
5491                 this.sortType = st.none;
5492         }
5493     }
5494
5495     // define once
5496     var stripRe = /[\$,%]/g;
5497
5498     // prebuilt conversion function for this field, instead of
5499     // switching every time we're reading a value
5500     if(!this.convert){
5501         var cv, dateFormat = this.dateFormat;
5502         switch(this.type){
5503             case "":
5504             case "auto":
5505             case undefined:
5506                 cv = function(v){ return v; };
5507                 break;
5508             case "string":
5509                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5510                 break;
5511             case "int":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5515                     };
5516                 break;
5517             case "float":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5521                     };
5522                 break;
5523             case "bool":
5524             case "boolean":
5525                 cv = function(v){ return v === true || v === "true" || v == 1; };
5526                 break;
5527             case "date":
5528                 cv = function(v){
5529                     if(!v){
5530                         return '';
5531                     }
5532                     if(v instanceof Date){
5533                         return v;
5534                     }
5535                     if(dateFormat){
5536                         if(dateFormat == "timestamp"){
5537                             return new Date(v*1000);
5538                         }
5539                         return Date.parseDate(v, dateFormat);
5540                     }
5541                     var parsed = Date.parse(v);
5542                     return parsed ? new Date(parsed) : null;
5543                 };
5544              break;
5545             
5546         }
5547         this.convert = cv;
5548     }
5549 };
5550
5551 Roo.data.Field.prototype = {
5552     dateFormat: null,
5553     defaultValue: "",
5554     mapping: null,
5555     sortType : null,
5556     sortDir : "ASC"
5557 };/*
5558  * Based on:
5559  * Ext JS Library 1.1.1
5560  * Copyright(c) 2006-2007, Ext JS, LLC.
5561  *
5562  * Originally Released Under LGPL - original licence link has changed is not relivant.
5563  *
5564  * Fork - LGPL
5565  * <script type="text/javascript">
5566  */
5567  
5568 // Base class for reading structured data from a data source.  This class is intended to be
5569 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5570
5571 /**
5572  * @class Roo.data.DataReader
5573  * Base class for reading structured data from a data source.  This class is intended to be
5574  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5575  */
5576
5577 Roo.data.DataReader = function(meta, recordType){
5578     
5579     this.meta = meta;
5580     
5581     this.recordType = recordType instanceof Array ? 
5582         Roo.data.Record.create(recordType) : recordType;
5583 };
5584
5585 Roo.data.DataReader.prototype = {
5586      /**
5587      * Create an empty record
5588      * @param {Object} data (optional) - overlay some values
5589      * @return {Roo.data.Record} record created.
5590      */
5591     newRow :  function(d) {
5592         var da =  {};
5593         this.recordType.prototype.fields.each(function(c) {
5594             switch( c.type) {
5595                 case 'int' : da[c.name] = 0; break;
5596                 case 'date' : da[c.name] = new Date(); break;
5597                 case 'float' : da[c.name] = 0.0; break;
5598                 case 'boolean' : da[c.name] = false; break;
5599                 default : da[c.name] = ""; break;
5600             }
5601             
5602         });
5603         return new this.recordType(Roo.apply(da, d));
5604     }
5605     
5606 };/*
5607  * Based on:
5608  * Ext JS Library 1.1.1
5609  * Copyright(c) 2006-2007, Ext JS, LLC.
5610  *
5611  * Originally Released Under LGPL - original licence link has changed is not relivant.
5612  *
5613  * Fork - LGPL
5614  * <script type="text/javascript">
5615  */
5616
5617 /**
5618  * @class Roo.data.DataProxy
5619  * @extends Roo.data.Observable
5620  * This class is an abstract base class for implementations which provide retrieval of
5621  * unformatted data objects.<br>
5622  * <p>
5623  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5624  * (of the appropriate type which knows how to parse the data object) to provide a block of
5625  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5626  * <p>
5627  * Custom implementations must implement the load method as described in
5628  * {@link Roo.data.HttpProxy#load}.
5629  */
5630 Roo.data.DataProxy = function(){
5631     this.addEvents({
5632         /**
5633          * @event beforeload
5634          * Fires before a network request is made to retrieve a data object.
5635          * @param {Object} This DataProxy object.
5636          * @param {Object} params The params parameter to the load function.
5637          */
5638         beforeload : true,
5639         /**
5640          * @event load
5641          * Fires before the load method's callback is called.
5642          * @param {Object} This DataProxy object.
5643          * @param {Object} o The data object.
5644          * @param {Object} arg The callback argument object passed to the load function.
5645          */
5646         load : true,
5647         /**
5648          * @event loadexception
5649          * Fires if an Exception occurs during data retrieval.
5650          * @param {Object} This DataProxy object.
5651          * @param {Object} o The data object.
5652          * @param {Object} arg The callback argument object passed to the load function.
5653          * @param {Object} e The Exception.
5654          */
5655         loadexception : true
5656     });
5657     Roo.data.DataProxy.superclass.constructor.call(this);
5658 };
5659
5660 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5661
5662     /**
5663      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5664      */
5665 /*
5666  * Based on:
5667  * Ext JS Library 1.1.1
5668  * Copyright(c) 2006-2007, Ext JS, LLC.
5669  *
5670  * Originally Released Under LGPL - original licence link has changed is not relivant.
5671  *
5672  * Fork - LGPL
5673  * <script type="text/javascript">
5674  */
5675 /**
5676  * @class Roo.data.MemoryProxy
5677  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5678  * to the Reader when its load method is called.
5679  * @constructor
5680  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5681  */
5682 Roo.data.MemoryProxy = function(data){
5683     if (data.data) {
5684         data = data.data;
5685     }
5686     Roo.data.MemoryProxy.superclass.constructor.call(this);
5687     this.data = data;
5688 };
5689
5690 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5691     /**
5692      * Load data from the requested source (in this case an in-memory
5693      * data object passed to the constructor), read the data object into
5694      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5695      * process that block using the passed callback.
5696      * @param {Object} params This parameter is not used by the MemoryProxy class.
5697      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5698      * object into a block of Roo.data.Records.
5699      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5700      * The function must be passed <ul>
5701      * <li>The Record block object</li>
5702      * <li>The "arg" argument from the load function</li>
5703      * <li>A boolean success indicator</li>
5704      * </ul>
5705      * @param {Object} scope The scope in which to call the callback
5706      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5707      */
5708     load : function(params, reader, callback, scope, arg){
5709         params = params || {};
5710         var result;
5711         try {
5712             result = reader.readRecords(this.data);
5713         }catch(e){
5714             this.fireEvent("loadexception", this, arg, null, e);
5715             callback.call(scope, null, arg, false);
5716             return;
5717         }
5718         callback.call(scope, result, arg, true);
5719     },
5720     
5721     // private
5722     update : function(params, records){
5723         
5724     }
5725 });/*
5726  * Based on:
5727  * Ext JS Library 1.1.1
5728  * Copyright(c) 2006-2007, Ext JS, LLC.
5729  *
5730  * Originally Released Under LGPL - original licence link has changed is not relivant.
5731  *
5732  * Fork - LGPL
5733  * <script type="text/javascript">
5734  */
5735 /**
5736  * @class Roo.data.HttpProxy
5737  * @extends Roo.data.DataProxy
5738  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5739  * configured to reference a certain URL.<br><br>
5740  * <p>
5741  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5742  * from which the running page was served.<br><br>
5743  * <p>
5744  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5745  * <p>
5746  * Be aware that to enable the browser to parse an XML document, the server must set
5747  * the Content-Type header in the HTTP response to "text/xml".
5748  * @constructor
5749  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5750  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5751  * will be used to make the request.
5752  */
5753 Roo.data.HttpProxy = function(conn){
5754     Roo.data.HttpProxy.superclass.constructor.call(this);
5755     // is conn a conn config or a real conn?
5756     this.conn = conn;
5757     this.useAjax = !conn || !conn.events;
5758   
5759 };
5760
5761 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5762     // thse are take from connection...
5763     
5764     /**
5765      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5766      */
5767     /**
5768      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5769      * extra parameters to each request made by this object. (defaults to undefined)
5770      */
5771     /**
5772      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5773      *  to each request made by this object. (defaults to undefined)
5774      */
5775     /**
5776      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5777      */
5778     /**
5779      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5780      */
5781      /**
5782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5783      * @type Boolean
5784      */
5785   
5786
5787     /**
5788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5789      * @type Boolean
5790      */
5791     /**
5792      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5793      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5794      * a finer-grained basis than the DataProxy events.
5795      */
5796     getConnection : function(){
5797         return this.useAjax ? Roo.Ajax : this.conn;
5798     },
5799
5800     /**
5801      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5802      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5803      * process that block using the passed callback.
5804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5805      * for the request to the remote server.
5806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5807      * object into a block of Roo.data.Records.
5808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5809      * The function must be passed <ul>
5810      * <li>The Record block object</li>
5811      * <li>The "arg" argument from the load function</li>
5812      * <li>A boolean success indicator</li>
5813      * </ul>
5814      * @param {Object} scope The scope in which to call the callback
5815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5816      */
5817     load : function(params, reader, callback, scope, arg){
5818         if(this.fireEvent("beforeload", this, params) !== false){
5819             var  o = {
5820                 params : params || {},
5821                 request: {
5822                     callback : callback,
5823                     scope : scope,
5824                     arg : arg
5825                 },
5826                 reader: reader,
5827                 callback : this.loadResponse,
5828                 scope: this
5829             };
5830             if(this.useAjax){
5831                 Roo.applyIf(o, this.conn);
5832                 if(this.activeRequest){
5833                     Roo.Ajax.abort(this.activeRequest);
5834                 }
5835                 this.activeRequest = Roo.Ajax.request(o);
5836             }else{
5837                 this.conn.request(o);
5838             }
5839         }else{
5840             callback.call(scope||this, null, arg, false);
5841         }
5842     },
5843
5844     // private
5845     loadResponse : function(o, success, response){
5846         delete this.activeRequest;
5847         if(!success){
5848             this.fireEvent("loadexception", this, o, response);
5849             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5850             return;
5851         }
5852         var result;
5853         try {
5854             result = o.reader.read(response);
5855         }catch(e){
5856             this.fireEvent("loadexception", this, o, response, e);
5857             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5858             return;
5859         }
5860         
5861         this.fireEvent("load", this, o, o.request.arg);
5862         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5863     },
5864
5865     // private
5866     update : function(dataSet){
5867
5868     },
5869
5870     // private
5871     updateResponse : function(dataSet){
5872
5873     }
5874 });/*
5875  * Based on:
5876  * Ext JS Library 1.1.1
5877  * Copyright(c) 2006-2007, Ext JS, LLC.
5878  *
5879  * Originally Released Under LGPL - original licence link has changed is not relivant.
5880  *
5881  * Fork - LGPL
5882  * <script type="text/javascript">
5883  */
5884
5885 /**
5886  * @class Roo.data.ScriptTagProxy
5887  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5888  * other than the originating domain of the running page.<br><br>
5889  * <p>
5890  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5891  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5892  * <p>
5893  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5894  * source code that is used as the source inside a &lt;script> tag.<br><br>
5895  * <p>
5896  * In order for the browser to process the returned data, the server must wrap the data object
5897  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5898  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5899  * depending on whether the callback name was passed:
5900  * <p>
5901  * <pre><code>
5902 boolean scriptTag = false;
5903 String cb = request.getParameter("callback");
5904 if (cb != null) {
5905     scriptTag = true;
5906     response.setContentType("text/javascript");
5907 } else {
5908     response.setContentType("application/x-json");
5909 }
5910 Writer out = response.getWriter();
5911 if (scriptTag) {
5912     out.write(cb + "(");
5913 }
5914 out.print(dataBlock.toJsonString());
5915 if (scriptTag) {
5916     out.write(");");
5917 }
5918 </pre></code>
5919  *
5920  * @constructor
5921  * @param {Object} config A configuration object.
5922  */
5923 Roo.data.ScriptTagProxy = function(config){
5924     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5925     Roo.apply(this, config);
5926     this.head = document.getElementsByTagName("head")[0];
5927 };
5928
5929 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5930
5931 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5932     /**
5933      * @cfg {String} url The URL from which to request the data object.
5934      */
5935     /**
5936      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5937      */
5938     timeout : 30000,
5939     /**
5940      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5941      * the server the name of the callback function set up by the load call to process the returned data object.
5942      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5943      * javascript output which calls this named function passing the data object as its only parameter.
5944      */
5945     callbackParam : "callback",
5946     /**
5947      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5948      * name to the request.
5949      */
5950     nocache : true,
5951
5952     /**
5953      * Load data from the configured URL, read the data object into
5954      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5955      * process that block using the passed callback.
5956      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5957      * for the request to the remote server.
5958      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5959      * object into a block of Roo.data.Records.
5960      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5961      * The function must be passed <ul>
5962      * <li>The Record block object</li>
5963      * <li>The "arg" argument from the load function</li>
5964      * <li>A boolean success indicator</li>
5965      * </ul>
5966      * @param {Object} scope The scope in which to call the callback
5967      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5968      */
5969     load : function(params, reader, callback, scope, arg){
5970         if(this.fireEvent("beforeload", this, params) !== false){
5971
5972             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5973
5974             var url = this.url;
5975             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5976             if(this.nocache){
5977                 url += "&_dc=" + (new Date().getTime());
5978             }
5979             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5980             var trans = {
5981                 id : transId,
5982                 cb : "stcCallback"+transId,
5983                 scriptId : "stcScript"+transId,
5984                 params : params,
5985                 arg : arg,
5986                 url : url,
5987                 callback : callback,
5988                 scope : scope,
5989                 reader : reader
5990             };
5991             var conn = this;
5992
5993             window[trans.cb] = function(o){
5994                 conn.handleResponse(o, trans);
5995             };
5996
5997             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5998
5999             if(this.autoAbort !== false){
6000                 this.abort();
6001             }
6002
6003             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6004
6005             var script = document.createElement("script");
6006             script.setAttribute("src", url);
6007             script.setAttribute("type", "text/javascript");
6008             script.setAttribute("id", trans.scriptId);
6009             this.head.appendChild(script);
6010
6011             this.trans = trans;
6012         }else{
6013             callback.call(scope||this, null, arg, false);
6014         }
6015     },
6016
6017     // private
6018     isLoading : function(){
6019         return this.trans ? true : false;
6020     },
6021
6022     /**
6023      * Abort the current server request.
6024      */
6025     abort : function(){
6026         if(this.isLoading()){
6027             this.destroyTrans(this.trans);
6028         }
6029     },
6030
6031     // private
6032     destroyTrans : function(trans, isLoaded){
6033         this.head.removeChild(document.getElementById(trans.scriptId));
6034         clearTimeout(trans.timeoutId);
6035         if(isLoaded){
6036             window[trans.cb] = undefined;
6037             try{
6038                 delete window[trans.cb];
6039             }catch(e){}
6040         }else{
6041             // if hasn't been loaded, wait for load to remove it to prevent script error
6042             window[trans.cb] = function(){
6043                 window[trans.cb] = undefined;
6044                 try{
6045                     delete window[trans.cb];
6046                 }catch(e){}
6047             };
6048         }
6049     },
6050
6051     // private
6052     handleResponse : function(o, trans){
6053         this.trans = false;
6054         this.destroyTrans(trans, true);
6055         var result;
6056         try {
6057             result = trans.reader.readRecords(o);
6058         }catch(e){
6059             this.fireEvent("loadexception", this, o, trans.arg, e);
6060             trans.callback.call(trans.scope||window, null, trans.arg, false);
6061             return;
6062         }
6063         this.fireEvent("load", this, o, trans.arg);
6064         trans.callback.call(trans.scope||window, result, trans.arg, true);
6065     },
6066
6067     // private
6068     handleFailure : function(trans){
6069         this.trans = false;
6070         this.destroyTrans(trans, false);
6071         this.fireEvent("loadexception", this, null, trans.arg);
6072         trans.callback.call(trans.scope||window, null, trans.arg, false);
6073     }
6074 });/*
6075  * Based on:
6076  * Ext JS Library 1.1.1
6077  * Copyright(c) 2006-2007, Ext JS, LLC.
6078  *
6079  * Originally Released Under LGPL - original licence link has changed is not relivant.
6080  *
6081  * Fork - LGPL
6082  * <script type="text/javascript">
6083  */
6084
6085 /**
6086  * @class Roo.data.JsonReader
6087  * @extends Roo.data.DataReader
6088  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6089  * based on mappings in a provided Roo.data.Record constructor.
6090  * 
6091  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6092  * in the reply previously. 
6093  * 
6094  * <p>
6095  * Example code:
6096  * <pre><code>
6097 var RecordDef = Roo.data.Record.create([
6098     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6099     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6100 ]);
6101 var myReader = new Roo.data.JsonReader({
6102     totalProperty: "results",    // The property which contains the total dataset size (optional)
6103     root: "rows",                // The property which contains an Array of row objects
6104     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6105 }, RecordDef);
6106 </code></pre>
6107  * <p>
6108  * This would consume a JSON file like this:
6109  * <pre><code>
6110 { 'results': 2, 'rows': [
6111     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6112     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6113 }
6114 </code></pre>
6115  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6116  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6117  * paged from the remote server.
6118  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6119  * @cfg {String} root name of the property which contains the Array of row objects.
6120  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6121  * @constructor
6122  * Create a new JsonReader
6123  * @param {Object} meta Metadata configuration options
6124  * @param {Object} recordType Either an Array of field definition objects,
6125  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6126  */
6127 Roo.data.JsonReader = function(meta, recordType){
6128     
6129     meta = meta || {};
6130     // set some defaults:
6131     Roo.applyIf(meta, {
6132         totalProperty: 'total',
6133         successProperty : 'success',
6134         root : 'data',
6135         id : 'id'
6136     });
6137     
6138     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6139 };
6140 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6141     
6142     /**
6143      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6144      * Used by Store query builder to append _requestMeta to params.
6145      * 
6146      */
6147     metaFromRemote : false,
6148     /**
6149      * This method is only used by a DataProxy which has retrieved data from a remote server.
6150      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6151      * @return {Object} data A data block which is used by an Roo.data.Store object as
6152      * a cache of Roo.data.Records.
6153      */
6154     read : function(response){
6155         var json = response.responseText;
6156        
6157         var o = /* eval:var:o */ eval("("+json+")");
6158         if(!o) {
6159             throw {message: "JsonReader.read: Json object not found"};
6160         }
6161         
6162         if(o.metaData){
6163             
6164             delete this.ef;
6165             this.metaFromRemote = true;
6166             this.meta = o.metaData;
6167             this.recordType = Roo.data.Record.create(o.metaData.fields);
6168             this.onMetaChange(this.meta, this.recordType, o);
6169         }
6170         return this.readRecords(o);
6171     },
6172
6173     // private function a store will implement
6174     onMetaChange : function(meta, recordType, o){
6175
6176     },
6177
6178     /**
6179          * @ignore
6180          */
6181     simpleAccess: function(obj, subsc) {
6182         return obj[subsc];
6183     },
6184
6185         /**
6186          * @ignore
6187          */
6188     getJsonAccessor: function(){
6189         var re = /[\[\.]/;
6190         return function(expr) {
6191             try {
6192                 return(re.test(expr))
6193                     ? new Function("obj", "return obj." + expr)
6194                     : function(obj){
6195                         return obj[expr];
6196                     };
6197             } catch(e){}
6198             return Roo.emptyFn;
6199         };
6200     }(),
6201
6202     /**
6203      * Create a data block containing Roo.data.Records from an XML document.
6204      * @param {Object} o An object which contains an Array of row objects in the property specified
6205      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6206      * which contains the total size of the dataset.
6207      * @return {Object} data A data block which is used by an Roo.data.Store object as
6208      * a cache of Roo.data.Records.
6209      */
6210     readRecords : function(o){
6211         /**
6212          * After any data loads, the raw JSON data is available for further custom processing.
6213          * @type Object
6214          */
6215         this.jsonData = o;
6216         var s = this.meta, Record = this.recordType,
6217             f = Record.prototype.fields, fi = f.items, fl = f.length;
6218
6219 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6220         if (!this.ef) {
6221             if(s.totalProperty) {
6222                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6223                 }
6224                 if(s.successProperty) {
6225                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6226                 }
6227                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6228                 if (s.id) {
6229                         var g = this.getJsonAccessor(s.id);
6230                         this.getId = function(rec) {
6231                                 var r = g(rec);
6232                                 return (r === undefined || r === "") ? null : r;
6233                         };
6234                 } else {
6235                         this.getId = function(){return null;};
6236                 }
6237             this.ef = [];
6238             for(var jj = 0; jj < fl; jj++){
6239                 f = fi[jj];
6240                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6241                 this.ef[jj] = this.getJsonAccessor(map);
6242             }
6243         }
6244
6245         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6246         if(s.totalProperty){
6247             var vt = parseInt(this.getTotal(o), 10);
6248             if(!isNaN(vt)){
6249                 totalRecords = vt;
6250             }
6251         }
6252         if(s.successProperty){
6253             var vs = this.getSuccess(o);
6254             if(vs === false || vs === 'false'){
6255                 success = false;
6256             }
6257         }
6258         var records = [];
6259             for(var i = 0; i < c; i++){
6260                     var n = root[i];
6261                 var values = {};
6262                 var id = this.getId(n);
6263                 for(var j = 0; j < fl; j++){
6264                     f = fi[j];
6265                 var v = this.ef[j](n);
6266                 if (!f.convert) {
6267                     Roo.log('missing convert for ' + f.name);
6268                     Roo.log(f);
6269                     continue;
6270                 }
6271                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6272                 }
6273                 var record = new Record(values, id);
6274                 record.json = n;
6275                 records[i] = record;
6276             }
6277             return {
6278                 success : success,
6279                 records : records,
6280                 totalRecords : totalRecords
6281             };
6282     }
6283 });/*
6284  * Based on:
6285  * Ext JS Library 1.1.1
6286  * Copyright(c) 2006-2007, Ext JS, LLC.
6287  *
6288  * Originally Released Under LGPL - original licence link has changed is not relivant.
6289  *
6290  * Fork - LGPL
6291  * <script type="text/javascript">
6292  */
6293
6294 /**
6295  * @class Roo.data.XmlReader
6296  * @extends Roo.data.DataReader
6297  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6298  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6299  * <p>
6300  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6301  * header in the HTTP response must be set to "text/xml".</em>
6302  * <p>
6303  * Example code:
6304  * <pre><code>
6305 var RecordDef = Roo.data.Record.create([
6306    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6307    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6308 ]);
6309 var myReader = new Roo.data.XmlReader({
6310    totalRecords: "results", // The element which contains the total dataset size (optional)
6311    record: "row",           // The repeated element which contains row information
6312    id: "id"                 // The element within the row that provides an ID for the record (optional)
6313 }, RecordDef);
6314 </code></pre>
6315  * <p>
6316  * This would consume an XML file like this:
6317  * <pre><code>
6318 &lt;?xml?>
6319 &lt;dataset>
6320  &lt;results>2&lt;/results>
6321  &lt;row>
6322    &lt;id>1&lt;/id>
6323    &lt;name>Bill&lt;/name>
6324    &lt;occupation>Gardener&lt;/occupation>
6325  &lt;/row>
6326  &lt;row>
6327    &lt;id>2&lt;/id>
6328    &lt;name>Ben&lt;/name>
6329    &lt;occupation>Horticulturalist&lt;/occupation>
6330  &lt;/row>
6331 &lt;/dataset>
6332 </code></pre>
6333  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6334  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6335  * paged from the remote server.
6336  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6337  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6338  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6339  * a record identifier value.
6340  * @constructor
6341  * Create a new XmlReader
6342  * @param {Object} meta Metadata configuration options
6343  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6344  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6345  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6346  */
6347 Roo.data.XmlReader = function(meta, recordType){
6348     meta = meta || {};
6349     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6350 };
6351 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6352     /**
6353      * This method is only used by a DataProxy which has retrieved data from a remote server.
6354          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6355          * to contain a method called 'responseXML' that returns an XML document object.
6356      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6357      * a cache of Roo.data.Records.
6358      */
6359     read : function(response){
6360         var doc = response.responseXML;
6361         if(!doc) {
6362             throw {message: "XmlReader.read: XML Document not available"};
6363         }
6364         return this.readRecords(doc);
6365     },
6366
6367     /**
6368      * Create a data block containing Roo.data.Records from an XML document.
6369          * @param {Object} doc A parsed XML document.
6370      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6371      * a cache of Roo.data.Records.
6372      */
6373     readRecords : function(doc){
6374         /**
6375          * After any data loads/reads, the raw XML Document is available for further custom processing.
6376          * @type XMLDocument
6377          */
6378         this.xmlData = doc;
6379         var root = doc.documentElement || doc;
6380         var q = Roo.DomQuery;
6381         var recordType = this.recordType, fields = recordType.prototype.fields;
6382         var sid = this.meta.id;
6383         var totalRecords = 0, success = true;
6384         if(this.meta.totalRecords){
6385             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6386         }
6387         
6388         if(this.meta.success){
6389             var sv = q.selectValue(this.meta.success, root, true);
6390             success = sv !== false && sv !== 'false';
6391         }
6392         var records = [];
6393         var ns = q.select(this.meta.record, root);
6394         for(var i = 0, len = ns.length; i < len; i++) {
6395                 var n = ns[i];
6396                 var values = {};
6397                 var id = sid ? q.selectValue(sid, n) : undefined;
6398                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6399                     var f = fields.items[j];
6400                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6401                     v = f.convert(v);
6402                     values[f.name] = v;
6403                 }
6404                 var record = new recordType(values, id);
6405                 record.node = n;
6406                 records[records.length] = record;
6407             }
6408
6409             return {
6410                 success : success,
6411                 records : records,
6412                 totalRecords : totalRecords || records.length
6413             };
6414     }
6415 });/*
6416  * Based on:
6417  * Ext JS Library 1.1.1
6418  * Copyright(c) 2006-2007, Ext JS, LLC.
6419  *
6420  * Originally Released Under LGPL - original licence link has changed is not relivant.
6421  *
6422  * Fork - LGPL
6423  * <script type="text/javascript">
6424  */
6425
6426 /**
6427  * @class Roo.data.ArrayReader
6428  * @extends Roo.data.DataReader
6429  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6430  * Each element of that Array represents a row of data fields. The
6431  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6432  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6433  * <p>
6434  * Example code:.
6435  * <pre><code>
6436 var RecordDef = Roo.data.Record.create([
6437     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6438     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6439 ]);
6440 var myReader = new Roo.data.ArrayReader({
6441     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6442 }, RecordDef);
6443 </code></pre>
6444  * <p>
6445  * This would consume an Array like this:
6446  * <pre><code>
6447 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6448   </code></pre>
6449  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6450  * @constructor
6451  * Create a new JsonReader
6452  * @param {Object} meta Metadata configuration options.
6453  * @param {Object} recordType Either an Array of field definition objects
6454  * as specified to {@link Roo.data.Record#create},
6455  * or an {@link Roo.data.Record} object
6456  * created using {@link Roo.data.Record#create}.
6457  */
6458 Roo.data.ArrayReader = function(meta, recordType){
6459     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6460 };
6461
6462 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6463     /**
6464      * Create a data block containing Roo.data.Records from an XML document.
6465      * @param {Object} o An Array of row objects which represents the dataset.
6466      * @return {Object} data A data block which is used by an Roo.data.Store object as
6467      * a cache of Roo.data.Records.
6468      */
6469     readRecords : function(o){
6470         var sid = this.meta ? this.meta.id : null;
6471         var recordType = this.recordType, fields = recordType.prototype.fields;
6472         var records = [];
6473         var root = o;
6474             for(var i = 0; i < root.length; i++){
6475                     var n = root[i];
6476                 var values = {};
6477                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6478                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6479                 var f = fields.items[j];
6480                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6481                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6482                 v = f.convert(v);
6483                 values[f.name] = v;
6484             }
6485                 var record = new recordType(values, id);
6486                 record.json = n;
6487                 records[records.length] = record;
6488             }
6489             return {
6490                 records : records,
6491                 totalRecords : records.length
6492             };
6493     }
6494 });/*
6495  * Based on:
6496  * Ext JS Library 1.1.1
6497  * Copyright(c) 2006-2007, Ext JS, LLC.
6498  *
6499  * Originally Released Under LGPL - original licence link has changed is not relivant.
6500  *
6501  * Fork - LGPL
6502  * <script type="text/javascript">
6503  */
6504
6505
6506 /**
6507  * @class Roo.data.Tree
6508  * @extends Roo.util.Observable
6509  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6510  * in the tree have most standard DOM functionality.
6511  * @constructor
6512  * @param {Node} root (optional) The root node
6513  */
6514 Roo.data.Tree = function(root){
6515    this.nodeHash = {};
6516    /**
6517     * The root node for this tree
6518     * @type Node
6519     */
6520    this.root = null;
6521    if(root){
6522        this.setRootNode(root);
6523    }
6524    this.addEvents({
6525        /**
6526         * @event append
6527         * Fires when a new child node is appended to a node in this tree.
6528         * @param {Tree} tree The owner tree
6529         * @param {Node} parent The parent node
6530         * @param {Node} node The newly appended node
6531         * @param {Number} index The index of the newly appended node
6532         */
6533        "append" : true,
6534        /**
6535         * @event remove
6536         * Fires when a child node is removed from a node in this tree.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node removed
6540         */
6541        "remove" : true,
6542        /**
6543         * @event move
6544         * Fires when a node is moved to a new location in the tree
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node moved
6547         * @param {Node} oldParent The old parent of this node
6548         * @param {Node} newParent The new parent of this node
6549         * @param {Number} index The index it was moved to
6550         */
6551        "move" : true,
6552        /**
6553         * @event insert
6554         * Fires when a new child node is inserted in a node in this tree.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node inserted
6558         * @param {Node} refNode The child node the node was inserted before
6559         */
6560        "insert" : true,
6561        /**
6562         * @event beforeappend
6563         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6564         * @param {Tree} tree The owner tree
6565         * @param {Node} parent The parent node
6566         * @param {Node} node The child node to be appended
6567         */
6568        "beforeappend" : true,
6569        /**
6570         * @event beforeremove
6571         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6572         * @param {Tree} tree The owner tree
6573         * @param {Node} parent The parent node
6574         * @param {Node} node The child node to be removed
6575         */
6576        "beforeremove" : true,
6577        /**
6578         * @event beforemove
6579         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} node The node being moved
6582         * @param {Node} oldParent The parent of the node
6583         * @param {Node} newParent The new parent the node is moving to
6584         * @param {Number} index The index it is being moved to
6585         */
6586        "beforemove" : true,
6587        /**
6588         * @event beforeinsert
6589         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6590         * @param {Tree} tree The owner tree
6591         * @param {Node} parent The parent node
6592         * @param {Node} node The child node to be inserted
6593         * @param {Node} refNode The child node the node is being inserted before
6594         */
6595        "beforeinsert" : true
6596    });
6597
6598     Roo.data.Tree.superclass.constructor.call(this);
6599 };
6600
6601 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6602     pathSeparator: "/",
6603
6604     proxyNodeEvent : function(){
6605         return this.fireEvent.apply(this, arguments);
6606     },
6607
6608     /**
6609      * Returns the root node for this tree.
6610      * @return {Node}
6611      */
6612     getRootNode : function(){
6613         return this.root;
6614     },
6615
6616     /**
6617      * Sets the root node for this tree.
6618      * @param {Node} node
6619      * @return {Node}
6620      */
6621     setRootNode : function(node){
6622         this.root = node;
6623         node.ownerTree = this;
6624         node.isRoot = true;
6625         this.registerNode(node);
6626         return node;
6627     },
6628
6629     /**
6630      * Gets a node in this tree by its id.
6631      * @param {String} id
6632      * @return {Node}
6633      */
6634     getNodeById : function(id){
6635         return this.nodeHash[id];
6636     },
6637
6638     registerNode : function(node){
6639         this.nodeHash[node.id] = node;
6640     },
6641
6642     unregisterNode : function(node){
6643         delete this.nodeHash[node.id];
6644     },
6645
6646     toString : function(){
6647         return "[Tree"+(this.id?" "+this.id:"")+"]";
6648     }
6649 });
6650
6651 /**
6652  * @class Roo.data.Node
6653  * @extends Roo.util.Observable
6654  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6655  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6656  * @constructor
6657  * @param {Object} attributes The attributes/config for the node
6658  */
6659 Roo.data.Node = function(attributes){
6660     /**
6661      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6662      * @type {Object}
6663      */
6664     this.attributes = attributes || {};
6665     this.leaf = this.attributes.leaf;
6666     /**
6667      * The node id. @type String
6668      */
6669     this.id = this.attributes.id;
6670     if(!this.id){
6671         this.id = Roo.id(null, "ynode-");
6672         this.attributes.id = this.id;
6673     }
6674     /**
6675      * All child nodes of this node. @type Array
6676      */
6677     this.childNodes = [];
6678     if(!this.childNodes.indexOf){ // indexOf is a must
6679         this.childNodes.indexOf = function(o){
6680             for(var i = 0, len = this.length; i < len; i++){
6681                 if(this[i] == o) {
6682                     return i;
6683                 }
6684             }
6685             return -1;
6686         };
6687     }
6688     /**
6689      * The parent node for this node. @type Node
6690      */
6691     this.parentNode = null;
6692     /**
6693      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6694      */
6695     this.firstChild = null;
6696     /**
6697      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6698      */
6699     this.lastChild = null;
6700     /**
6701      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6702      */
6703     this.previousSibling = null;
6704     /**
6705      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6706      */
6707     this.nextSibling = null;
6708
6709     this.addEvents({
6710        /**
6711         * @event append
6712         * Fires when a new child node is appended
6713         * @param {Tree} tree The owner tree
6714         * @param {Node} this This node
6715         * @param {Node} node The newly appended node
6716         * @param {Number} index The index of the newly appended node
6717         */
6718        "append" : true,
6719        /**
6720         * @event remove
6721         * Fires when a child node is removed
6722         * @param {Tree} tree The owner tree
6723         * @param {Node} this This node
6724         * @param {Node} node The removed node
6725         */
6726        "remove" : true,
6727        /**
6728         * @event move
6729         * Fires when this node is moved to a new location in the tree
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} oldParent The old parent of this node
6733         * @param {Node} newParent The new parent of this node
6734         * @param {Number} index The index it was moved to
6735         */
6736        "move" : true,
6737        /**
6738         * @event insert
6739         * Fires when a new child node is inserted.
6740         * @param {Tree} tree The owner tree
6741         * @param {Node} this This node
6742         * @param {Node} node The child node inserted
6743         * @param {Node} refNode The child node the node was inserted before
6744         */
6745        "insert" : true,
6746        /**
6747         * @event beforeappend
6748         * Fires before a new child is appended, return false to cancel the append.
6749         * @param {Tree} tree The owner tree
6750         * @param {Node} this This node
6751         * @param {Node} node The child node to be appended
6752         */
6753        "beforeappend" : true,
6754        /**
6755         * @event beforeremove
6756         * Fires before a child is removed, return false to cancel the remove.
6757         * @param {Tree} tree The owner tree
6758         * @param {Node} this This node
6759         * @param {Node} node The child node to be removed
6760         */
6761        "beforeremove" : true,
6762        /**
6763         * @event beforemove
6764         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6765         * @param {Tree} tree The owner tree
6766         * @param {Node} this This node
6767         * @param {Node} oldParent The parent of this node
6768         * @param {Node} newParent The new parent this node is moving to
6769         * @param {Number} index The index it is being moved to
6770         */
6771        "beforemove" : true,
6772        /**
6773         * @event beforeinsert
6774         * Fires before a new child is inserted, return false to cancel the insert.
6775         * @param {Tree} tree The owner tree
6776         * @param {Node} this This node
6777         * @param {Node} node The child node to be inserted
6778         * @param {Node} refNode The child node the node is being inserted before
6779         */
6780        "beforeinsert" : true
6781    });
6782     this.listeners = this.attributes.listeners;
6783     Roo.data.Node.superclass.constructor.call(this);
6784 };
6785
6786 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6787     fireEvent : function(evtName){
6788         // first do standard event for this node
6789         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6790             return false;
6791         }
6792         // then bubble it up to the tree if the event wasn't cancelled
6793         var ot = this.getOwnerTree();
6794         if(ot){
6795             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6796                 return false;
6797             }
6798         }
6799         return true;
6800     },
6801
6802     /**
6803      * Returns true if this node is a leaf
6804      * @return {Boolean}
6805      */
6806     isLeaf : function(){
6807         return this.leaf === true;
6808     },
6809
6810     // private
6811     setFirstChild : function(node){
6812         this.firstChild = node;
6813     },
6814
6815     //private
6816     setLastChild : function(node){
6817         this.lastChild = node;
6818     },
6819
6820
6821     /**
6822      * Returns true if this node is the last child of its parent
6823      * @return {Boolean}
6824      */
6825     isLast : function(){
6826        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6827     },
6828
6829     /**
6830      * Returns true if this node is the first child of its parent
6831      * @return {Boolean}
6832      */
6833     isFirst : function(){
6834        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6835     },
6836
6837     hasChildNodes : function(){
6838         return !this.isLeaf() && this.childNodes.length > 0;
6839     },
6840
6841     /**
6842      * Insert node(s) as the last child node of this node.
6843      * @param {Node/Array} node The node or Array of nodes to append
6844      * @return {Node} The appended node if single append, or null if an array was passed
6845      */
6846     appendChild : function(node){
6847         var multi = false;
6848         if(node instanceof Array){
6849             multi = node;
6850         }else if(arguments.length > 1){
6851             multi = arguments;
6852         }
6853         // if passed an array or multiple args do them one by one
6854         if(multi){
6855             for(var i = 0, len = multi.length; i < len; i++) {
6856                 this.appendChild(multi[i]);
6857             }
6858         }else{
6859             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6860                 return false;
6861             }
6862             var index = this.childNodes.length;
6863             var oldParent = node.parentNode;
6864             // it's a move, make sure we move it cleanly
6865             if(oldParent){
6866                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6867                     return false;
6868                 }
6869                 oldParent.removeChild(node);
6870             }
6871             index = this.childNodes.length;
6872             if(index == 0){
6873                 this.setFirstChild(node);
6874             }
6875             this.childNodes.push(node);
6876             node.parentNode = this;
6877             var ps = this.childNodes[index-1];
6878             if(ps){
6879                 node.previousSibling = ps;
6880                 ps.nextSibling = node;
6881             }else{
6882                 node.previousSibling = null;
6883             }
6884             node.nextSibling = null;
6885             this.setLastChild(node);
6886             node.setOwnerTree(this.getOwnerTree());
6887             this.fireEvent("append", this.ownerTree, this, node, index);
6888             if(oldParent){
6889                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6890             }
6891             return node;
6892         }
6893     },
6894
6895     /**
6896      * Removes a child node from this node.
6897      * @param {Node} node The node to remove
6898      * @return {Node} The removed node
6899      */
6900     removeChild : function(node){
6901         var index = this.childNodes.indexOf(node);
6902         if(index == -1){
6903             return false;
6904         }
6905         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6906             return false;
6907         }
6908
6909         // remove it from childNodes collection
6910         this.childNodes.splice(index, 1);
6911
6912         // update siblings
6913         if(node.previousSibling){
6914             node.previousSibling.nextSibling = node.nextSibling;
6915         }
6916         if(node.nextSibling){
6917             node.nextSibling.previousSibling = node.previousSibling;
6918         }
6919
6920         // update child refs
6921         if(this.firstChild == node){
6922             this.setFirstChild(node.nextSibling);
6923         }
6924         if(this.lastChild == node){
6925             this.setLastChild(node.previousSibling);
6926         }
6927
6928         node.setOwnerTree(null);
6929         // clear any references from the node
6930         node.parentNode = null;
6931         node.previousSibling = null;
6932         node.nextSibling = null;
6933         this.fireEvent("remove", this.ownerTree, this, node);
6934         return node;
6935     },
6936
6937     /**
6938      * Inserts the first node before the second node in this nodes childNodes collection.
6939      * @param {Node} node The node to insert
6940      * @param {Node} refNode The node to insert before (if null the node is appended)
6941      * @return {Node} The inserted node
6942      */
6943     insertBefore : function(node, refNode){
6944         if(!refNode){ // like standard Dom, refNode can be null for append
6945             return this.appendChild(node);
6946         }
6947         // nothing to do
6948         if(node == refNode){
6949             return false;
6950         }
6951
6952         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6953             return false;
6954         }
6955         var index = this.childNodes.indexOf(refNode);
6956         var oldParent = node.parentNode;
6957         var refIndex = index;
6958
6959         // when moving internally, indexes will change after remove
6960         if(oldParent == this && this.childNodes.indexOf(node) < index){
6961             refIndex--;
6962         }
6963
6964         // it's a move, make sure we move it cleanly
6965         if(oldParent){
6966             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6967                 return false;
6968             }
6969             oldParent.removeChild(node);
6970         }
6971         if(refIndex == 0){
6972             this.setFirstChild(node);
6973         }
6974         this.childNodes.splice(refIndex, 0, node);
6975         node.parentNode = this;
6976         var ps = this.childNodes[refIndex-1];
6977         if(ps){
6978             node.previousSibling = ps;
6979             ps.nextSibling = node;
6980         }else{
6981             node.previousSibling = null;
6982         }
6983         node.nextSibling = refNode;
6984         refNode.previousSibling = node;
6985         node.setOwnerTree(this.getOwnerTree());
6986         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6987         if(oldParent){
6988             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6989         }
6990         return node;
6991     },
6992
6993     /**
6994      * Returns the child node at the specified index.
6995      * @param {Number} index
6996      * @return {Node}
6997      */
6998     item : function(index){
6999         return this.childNodes[index];
7000     },
7001
7002     /**
7003      * Replaces one child node in this node with another.
7004      * @param {Node} newChild The replacement node
7005      * @param {Node} oldChild The node to replace
7006      * @return {Node} The replaced node
7007      */
7008     replaceChild : function(newChild, oldChild){
7009         this.insertBefore(newChild, oldChild);
7010         this.removeChild(oldChild);
7011         return oldChild;
7012     },
7013
7014     /**
7015      * Returns the index of a child node
7016      * @param {Node} node
7017      * @return {Number} The index of the node or -1 if it was not found
7018      */
7019     indexOf : function(child){
7020         return this.childNodes.indexOf(child);
7021     },
7022
7023     /**
7024      * Returns the tree this node is in.
7025      * @return {Tree}
7026      */
7027     getOwnerTree : function(){
7028         // if it doesn't have one, look for one
7029         if(!this.ownerTree){
7030             var p = this;
7031             while(p){
7032                 if(p.ownerTree){
7033                     this.ownerTree = p.ownerTree;
7034                     break;
7035                 }
7036                 p = p.parentNode;
7037             }
7038         }
7039         return this.ownerTree;
7040     },
7041
7042     /**
7043      * Returns depth of this node (the root node has a depth of 0)
7044      * @return {Number}
7045      */
7046     getDepth : function(){
7047         var depth = 0;
7048         var p = this;
7049         while(p.parentNode){
7050             ++depth;
7051             p = p.parentNode;
7052         }
7053         return depth;
7054     },
7055
7056     // private
7057     setOwnerTree : function(tree){
7058         // if it's move, we need to update everyone
7059         if(tree != this.ownerTree){
7060             if(this.ownerTree){
7061                 this.ownerTree.unregisterNode(this);
7062             }
7063             this.ownerTree = tree;
7064             var cs = this.childNodes;
7065             for(var i = 0, len = cs.length; i < len; i++) {
7066                 cs[i].setOwnerTree(tree);
7067             }
7068             if(tree){
7069                 tree.registerNode(this);
7070             }
7071         }
7072     },
7073
7074     /**
7075      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7076      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7077      * @return {String} The path
7078      */
7079     getPath : function(attr){
7080         attr = attr || "id";
7081         var p = this.parentNode;
7082         var b = [this.attributes[attr]];
7083         while(p){
7084             b.unshift(p.attributes[attr]);
7085             p = p.parentNode;
7086         }
7087         var sep = this.getOwnerTree().pathSeparator;
7088         return sep + b.join(sep);
7089     },
7090
7091     /**
7092      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7093      * function call will be the scope provided or the current node. The arguments to the function
7094      * will be the args provided or the current node. If the function returns false at any point,
7095      * the bubble is stopped.
7096      * @param {Function} fn The function to call
7097      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7098      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7099      */
7100     bubble : function(fn, scope, args){
7101         var p = this;
7102         while(p){
7103             if(fn.call(scope || p, args || p) === false){
7104                 break;
7105             }
7106             p = p.parentNode;
7107         }
7108     },
7109
7110     /**
7111      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7112      * function call will be the scope provided or the current node. The arguments to the function
7113      * will be the args provided or the current node. If the function returns false at any point,
7114      * the cascade is stopped on that branch.
7115      * @param {Function} fn The function to call
7116      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7117      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7118      */
7119     cascade : function(fn, scope, args){
7120         if(fn.call(scope || this, args || this) !== false){
7121             var cs = this.childNodes;
7122             for(var i = 0, len = cs.length; i < len; i++) {
7123                 cs[i].cascade(fn, scope, args);
7124             }
7125         }
7126     },
7127
7128     /**
7129      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7130      * function call will be the scope provided or the current node. The arguments to the function
7131      * will be the args provided or the current node. If the function returns false at any point,
7132      * the iteration stops.
7133      * @param {Function} fn The function to call
7134      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7135      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7136      */
7137     eachChild : function(fn, scope, args){
7138         var cs = this.childNodes;
7139         for(var i = 0, len = cs.length; i < len; i++) {
7140                 if(fn.call(scope || this, args || cs[i]) === false){
7141                     break;
7142                 }
7143         }
7144     },
7145
7146     /**
7147      * Finds the first child that has the attribute with the specified value.
7148      * @param {String} attribute The attribute name
7149      * @param {Mixed} value The value to search for
7150      * @return {Node} The found child or null if none was found
7151      */
7152     findChild : function(attribute, value){
7153         var cs = this.childNodes;
7154         for(var i = 0, len = cs.length; i < len; i++) {
7155                 if(cs[i].attributes[attribute] == value){
7156                     return cs[i];
7157                 }
7158         }
7159         return null;
7160     },
7161
7162     /**
7163      * Finds the first child by a custom function. The child matches if the function passed
7164      * returns true.
7165      * @param {Function} fn
7166      * @param {Object} scope (optional)
7167      * @return {Node} The found child or null if none was found
7168      */
7169     findChildBy : function(fn, scope){
7170         var cs = this.childNodes;
7171         for(var i = 0, len = cs.length; i < len; i++) {
7172                 if(fn.call(scope||cs[i], cs[i]) === true){
7173                     return cs[i];
7174                 }
7175         }
7176         return null;
7177     },
7178
7179     /**
7180      * Sorts this nodes children using the supplied sort function
7181      * @param {Function} fn
7182      * @param {Object} scope (optional)
7183      */
7184     sort : function(fn, scope){
7185         var cs = this.childNodes;
7186         var len = cs.length;
7187         if(len > 0){
7188             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7189             cs.sort(sortFn);
7190             for(var i = 0; i < len; i++){
7191                 var n = cs[i];
7192                 n.previousSibling = cs[i-1];
7193                 n.nextSibling = cs[i+1];
7194                 if(i == 0){
7195                     this.setFirstChild(n);
7196                 }
7197                 if(i == len-1){
7198                     this.setLastChild(n);
7199                 }
7200             }
7201         }
7202     },
7203
7204     /**
7205      * Returns true if this node is an ancestor (at any point) of the passed node.
7206      * @param {Node} node
7207      * @return {Boolean}
7208      */
7209     contains : function(node){
7210         return node.isAncestor(this);
7211     },
7212
7213     /**
7214      * Returns true if the passed node is an ancestor (at any point) of this node.
7215      * @param {Node} node
7216      * @return {Boolean}
7217      */
7218     isAncestor : function(node){
7219         var p = this.parentNode;
7220         while(p){
7221             if(p == node){
7222                 return true;
7223             }
7224             p = p.parentNode;
7225         }
7226         return false;
7227     },
7228
7229     toString : function(){
7230         return "[Node"+(this.id?" "+this.id:"")+"]";
7231     }
7232 });/*
7233  * Based on:
7234  * Ext JS Library 1.1.1
7235  * Copyright(c) 2006-2007, Ext JS, LLC.
7236  *
7237  * Originally Released Under LGPL - original licence link has changed is not relivant.
7238  *
7239  * Fork - LGPL
7240  * <script type="text/javascript">
7241  */
7242  
7243
7244 /**
7245  * @class Roo.ComponentMgr
7246  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7247  * @singleton
7248  */
7249 Roo.ComponentMgr = function(){
7250     var all = new Roo.util.MixedCollection();
7251
7252     return {
7253         /**
7254          * Registers a component.
7255          * @param {Roo.Component} c The component
7256          */
7257         register : function(c){
7258             all.add(c);
7259         },
7260
7261         /**
7262          * Unregisters a component.
7263          * @param {Roo.Component} c The component
7264          */
7265         unregister : function(c){
7266             all.remove(c);
7267         },
7268
7269         /**
7270          * Returns a component by id
7271          * @param {String} id The component id
7272          */
7273         get : function(id){
7274             return all.get(id);
7275         },
7276
7277         /**
7278          * Registers a function that will be called when a specified component is added to ComponentMgr
7279          * @param {String} id The component id
7280          * @param {Funtction} fn The callback function
7281          * @param {Object} scope The scope of the callback
7282          */
7283         onAvailable : function(id, fn, scope){
7284             all.on("add", function(index, o){
7285                 if(o.id == id){
7286                     fn.call(scope || o, o);
7287                     all.un("add", fn, scope);
7288                 }
7289             });
7290         }
7291     };
7292 }();/*
7293  * Based on:
7294  * Ext JS Library 1.1.1
7295  * Copyright(c) 2006-2007, Ext JS, LLC.
7296  *
7297  * Originally Released Under LGPL - original licence link has changed is not relivant.
7298  *
7299  * Fork - LGPL
7300  * <script type="text/javascript">
7301  */
7302  
7303 /**
7304  * @class Roo.Component
7305  * @extends Roo.util.Observable
7306  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7307  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7308  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7309  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7310  * All visual components (widgets) that require rendering into a layout should subclass Component.
7311  * @constructor
7312  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7313  * 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
7314  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7315  */
7316 Roo.Component = function(config){
7317     config = config || {};
7318     if(config.tagName || config.dom || typeof config == "string"){ // element object
7319         config = {el: config, id: config.id || config};
7320     }
7321     this.initialConfig = config;
7322
7323     Roo.apply(this, config);
7324     this.addEvents({
7325         /**
7326          * @event disable
7327          * Fires after the component is disabled.
7328              * @param {Roo.Component} this
7329              */
7330         disable : true,
7331         /**
7332          * @event enable
7333          * Fires after the component is enabled.
7334              * @param {Roo.Component} this
7335              */
7336         enable : true,
7337         /**
7338          * @event beforeshow
7339          * Fires before the component is shown.  Return false to stop the show.
7340              * @param {Roo.Component} this
7341              */
7342         beforeshow : true,
7343         /**
7344          * @event show
7345          * Fires after the component is shown.
7346              * @param {Roo.Component} this
7347              */
7348         show : true,
7349         /**
7350          * @event beforehide
7351          * Fires before the component is hidden. Return false to stop the hide.
7352              * @param {Roo.Component} this
7353              */
7354         beforehide : true,
7355         /**
7356          * @event hide
7357          * Fires after the component is hidden.
7358              * @param {Roo.Component} this
7359              */
7360         hide : true,
7361         /**
7362          * @event beforerender
7363          * Fires before the component is rendered. Return false to stop the render.
7364              * @param {Roo.Component} this
7365              */
7366         beforerender : true,
7367         /**
7368          * @event render
7369          * Fires after the component is rendered.
7370              * @param {Roo.Component} this
7371              */
7372         render : true,
7373         /**
7374          * @event beforedestroy
7375          * Fires before the component is destroyed. Return false to stop the destroy.
7376              * @param {Roo.Component} this
7377              */
7378         beforedestroy : true,
7379         /**
7380          * @event destroy
7381          * Fires after the component is destroyed.
7382              * @param {Roo.Component} this
7383              */
7384         destroy : true
7385     });
7386     if(!this.id){
7387         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7388     }
7389     Roo.ComponentMgr.register(this);
7390     Roo.Component.superclass.constructor.call(this);
7391     this.initComponent();
7392     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7393         this.render(this.renderTo);
7394         delete this.renderTo;
7395     }
7396 };
7397
7398 /** @private */
7399 Roo.Component.AUTO_ID = 1000;
7400
7401 Roo.extend(Roo.Component, Roo.util.Observable, {
7402     /**
7403      * @scope Roo.Component.prototype
7404      * @type {Boolean}
7405      * true if this component is hidden. Read-only.
7406      */
7407     hidden : false,
7408     /**
7409      * @type {Boolean}
7410      * true if this component is disabled. Read-only.
7411      */
7412     disabled : false,
7413     /**
7414      * @type {Boolean}
7415      * true if this component has been rendered. Read-only.
7416      */
7417     rendered : false,
7418     
7419     /** @cfg {String} disableClass
7420      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7421      */
7422     disabledClass : "x-item-disabled",
7423         /** @cfg {Boolean} allowDomMove
7424          * Whether the component can move the Dom node when rendering (defaults to true).
7425          */
7426     allowDomMove : true,
7427     /** @cfg {String} hideMode
7428      * How this component should hidden. Supported values are
7429      * "visibility" (css visibility), "offsets" (negative offset position) and
7430      * "display" (css display) - defaults to "display".
7431      */
7432     hideMode: 'display',
7433
7434     /** @private */
7435     ctype : "Roo.Component",
7436
7437     /**
7438      * @cfg {String} actionMode 
7439      * which property holds the element that used for  hide() / show() / disable() / enable()
7440      * default is 'el' 
7441      */
7442     actionMode : "el",
7443
7444     /** @private */
7445     getActionEl : function(){
7446         return this[this.actionMode];
7447     },
7448
7449     initComponent : Roo.emptyFn,
7450     /**
7451      * If this is a lazy rendering component, render it to its container element.
7452      * @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.
7453      */
7454     render : function(container, position){
7455         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7456             if(!container && this.el){
7457                 this.el = Roo.get(this.el);
7458                 container = this.el.dom.parentNode;
7459                 this.allowDomMove = false;
7460             }
7461             this.container = Roo.get(container);
7462             this.rendered = true;
7463             if(position !== undefined){
7464                 if(typeof position == 'number'){
7465                     position = this.container.dom.childNodes[position];
7466                 }else{
7467                     position = Roo.getDom(position);
7468                 }
7469             }
7470             this.onRender(this.container, position || null);
7471             if(this.cls){
7472                 this.el.addClass(this.cls);
7473                 delete this.cls;
7474             }
7475             if(this.style){
7476                 this.el.applyStyles(this.style);
7477                 delete this.style;
7478             }
7479             this.fireEvent("render", this);
7480             this.afterRender(this.container);
7481             if(this.hidden){
7482                 this.hide();
7483             }
7484             if(this.disabled){
7485                 this.disable();
7486             }
7487         }
7488         return this;
7489     },
7490
7491     /** @private */
7492     // default function is not really useful
7493     onRender : function(ct, position){
7494         if(this.el){
7495             this.el = Roo.get(this.el);
7496             if(this.allowDomMove !== false){
7497                 ct.dom.insertBefore(this.el.dom, position);
7498             }
7499         }
7500     },
7501
7502     /** @private */
7503     getAutoCreate : function(){
7504         var cfg = typeof this.autoCreate == "object" ?
7505                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7506         if(this.id && !cfg.id){
7507             cfg.id = this.id;
7508         }
7509         return cfg;
7510     },
7511
7512     /** @private */
7513     afterRender : Roo.emptyFn,
7514
7515     /**
7516      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7517      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7518      */
7519     destroy : function(){
7520         if(this.fireEvent("beforedestroy", this) !== false){
7521             this.purgeListeners();
7522             this.beforeDestroy();
7523             if(this.rendered){
7524                 this.el.removeAllListeners();
7525                 this.el.remove();
7526                 if(this.actionMode == "container"){
7527                     this.container.remove();
7528                 }
7529             }
7530             this.onDestroy();
7531             Roo.ComponentMgr.unregister(this);
7532             this.fireEvent("destroy", this);
7533         }
7534     },
7535
7536         /** @private */
7537     beforeDestroy : function(){
7538
7539     },
7540
7541         /** @private */
7542         onDestroy : function(){
7543
7544     },
7545
7546     /**
7547      * Returns the underlying {@link Roo.Element}.
7548      * @return {Roo.Element} The element
7549      */
7550     getEl : function(){
7551         return this.el;
7552     },
7553
7554     /**
7555      * Returns the id of this component.
7556      * @return {String}
7557      */
7558     getId : function(){
7559         return this.id;
7560     },
7561
7562     /**
7563      * Try to focus this component.
7564      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7565      * @return {Roo.Component} this
7566      */
7567     focus : function(selectText){
7568         if(this.rendered){
7569             this.el.focus();
7570             if(selectText === true){
7571                 this.el.dom.select();
7572             }
7573         }
7574         return this;
7575     },
7576
7577     /** @private */
7578     blur : function(){
7579         if(this.rendered){
7580             this.el.blur();
7581         }
7582         return this;
7583     },
7584
7585     /**
7586      * Disable this component.
7587      * @return {Roo.Component} this
7588      */
7589     disable : function(){
7590         if(this.rendered){
7591             this.onDisable();
7592         }
7593         this.disabled = true;
7594         this.fireEvent("disable", this);
7595         return this;
7596     },
7597
7598         // private
7599     onDisable : function(){
7600         this.getActionEl().addClass(this.disabledClass);
7601         this.el.dom.disabled = true;
7602     },
7603
7604     /**
7605      * Enable this component.
7606      * @return {Roo.Component} this
7607      */
7608     enable : function(){
7609         if(this.rendered){
7610             this.onEnable();
7611         }
7612         this.disabled = false;
7613         this.fireEvent("enable", this);
7614         return this;
7615     },
7616
7617         // private
7618     onEnable : function(){
7619         this.getActionEl().removeClass(this.disabledClass);
7620         this.el.dom.disabled = false;
7621     },
7622
7623     /**
7624      * Convenience function for setting disabled/enabled by boolean.
7625      * @param {Boolean} disabled
7626      */
7627     setDisabled : function(disabled){
7628         this[disabled ? "disable" : "enable"]();
7629     },
7630
7631     /**
7632      * Show this component.
7633      * @return {Roo.Component} this
7634      */
7635     show: function(){
7636         if(this.fireEvent("beforeshow", this) !== false){
7637             this.hidden = false;
7638             if(this.rendered){
7639                 this.onShow();
7640             }
7641             this.fireEvent("show", this);
7642         }
7643         return this;
7644     },
7645
7646     // private
7647     onShow : function(){
7648         var ae = this.getActionEl();
7649         if(this.hideMode == 'visibility'){
7650             ae.dom.style.visibility = "visible";
7651         }else if(this.hideMode == 'offsets'){
7652             ae.removeClass('x-hidden');
7653         }else{
7654             ae.dom.style.display = "";
7655         }
7656     },
7657
7658     /**
7659      * Hide this component.
7660      * @return {Roo.Component} this
7661      */
7662     hide: function(){
7663         if(this.fireEvent("beforehide", this) !== false){
7664             this.hidden = true;
7665             if(this.rendered){
7666                 this.onHide();
7667             }
7668             this.fireEvent("hide", this);
7669         }
7670         return this;
7671     },
7672
7673     // private
7674     onHide : function(){
7675         var ae = this.getActionEl();
7676         if(this.hideMode == 'visibility'){
7677             ae.dom.style.visibility = "hidden";
7678         }else if(this.hideMode == 'offsets'){
7679             ae.addClass('x-hidden');
7680         }else{
7681             ae.dom.style.display = "none";
7682         }
7683     },
7684
7685     /**
7686      * Convenience function to hide or show this component by boolean.
7687      * @param {Boolean} visible True to show, false to hide
7688      * @return {Roo.Component} this
7689      */
7690     setVisible: function(visible){
7691         if(visible) {
7692             this.show();
7693         }else{
7694             this.hide();
7695         }
7696         return this;
7697     },
7698
7699     /**
7700      * Returns true if this component is visible.
7701      */
7702     isVisible : function(){
7703         return this.getActionEl().isVisible();
7704     },
7705
7706     cloneConfig : function(overrides){
7707         overrides = overrides || {};
7708         var id = overrides.id || Roo.id();
7709         var cfg = Roo.applyIf(overrides, this.initialConfig);
7710         cfg.id = id; // prevent dup id
7711         return new this.constructor(cfg);
7712     }
7713 });/*
7714  * Based on:
7715  * Ext JS Library 1.1.1
7716  * Copyright(c) 2006-2007, Ext JS, LLC.
7717  *
7718  * Originally Released Under LGPL - original licence link has changed is not relivant.
7719  *
7720  * Fork - LGPL
7721  * <script type="text/javascript">
7722  */
7723  (function(){ 
7724 /**
7725  * @class Roo.Layer
7726  * @extends Roo.Element
7727  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7728  * automatic maintaining of shadow/shim positions.
7729  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7730  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7731  * you can pass a string with a CSS class name. False turns off the shadow.
7732  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7733  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7734  * @cfg {String} cls CSS class to add to the element
7735  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7736  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7737  * @constructor
7738  * @param {Object} config An object with config options.
7739  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7740  */
7741
7742 Roo.Layer = function(config, existingEl){
7743     config = config || {};
7744     var dh = Roo.DomHelper;
7745     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7746     if(existingEl){
7747         this.dom = Roo.getDom(existingEl);
7748     }
7749     if(!this.dom){
7750         var o = config.dh || {tag: "div", cls: "x-layer"};
7751         this.dom = dh.append(pel, o);
7752     }
7753     if(config.cls){
7754         this.addClass(config.cls);
7755     }
7756     this.constrain = config.constrain !== false;
7757     this.visibilityMode = Roo.Element.VISIBILITY;
7758     if(config.id){
7759         this.id = this.dom.id = config.id;
7760     }else{
7761         this.id = Roo.id(this.dom);
7762     }
7763     this.zindex = config.zindex || this.getZIndex();
7764     this.position("absolute", this.zindex);
7765     if(config.shadow){
7766         this.shadowOffset = config.shadowOffset || 4;
7767         this.shadow = new Roo.Shadow({
7768             offset : this.shadowOffset,
7769             mode : config.shadow
7770         });
7771     }else{
7772         this.shadowOffset = 0;
7773     }
7774     this.useShim = config.shim !== false && Roo.useShims;
7775     this.useDisplay = config.useDisplay;
7776     this.hide();
7777 };
7778
7779 var supr = Roo.Element.prototype;
7780
7781 // shims are shared among layer to keep from having 100 iframes
7782 var shims = [];
7783
7784 Roo.extend(Roo.Layer, Roo.Element, {
7785
7786     getZIndex : function(){
7787         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7788     },
7789
7790     getShim : function(){
7791         if(!this.useShim){
7792             return null;
7793         }
7794         if(this.shim){
7795             return this.shim;
7796         }
7797         var shim = shims.shift();
7798         if(!shim){
7799             shim = this.createShim();
7800             shim.enableDisplayMode('block');
7801             shim.dom.style.display = 'none';
7802             shim.dom.style.visibility = 'visible';
7803         }
7804         var pn = this.dom.parentNode;
7805         if(shim.dom.parentNode != pn){
7806             pn.insertBefore(shim.dom, this.dom);
7807         }
7808         shim.setStyle('z-index', this.getZIndex()-2);
7809         this.shim = shim;
7810         return shim;
7811     },
7812
7813     hideShim : function(){
7814         if(this.shim){
7815             this.shim.setDisplayed(false);
7816             shims.push(this.shim);
7817             delete this.shim;
7818         }
7819     },
7820
7821     disableShadow : function(){
7822         if(this.shadow){
7823             this.shadowDisabled = true;
7824             this.shadow.hide();
7825             this.lastShadowOffset = this.shadowOffset;
7826             this.shadowOffset = 0;
7827         }
7828     },
7829
7830     enableShadow : function(show){
7831         if(this.shadow){
7832             this.shadowDisabled = false;
7833             this.shadowOffset = this.lastShadowOffset;
7834             delete this.lastShadowOffset;
7835             if(show){
7836                 this.sync(true);
7837             }
7838         }
7839     },
7840
7841     // private
7842     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7843     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7844     sync : function(doShow){
7845         var sw = this.shadow;
7846         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7847             var sh = this.getShim();
7848
7849             var w = this.getWidth(),
7850                 h = this.getHeight();
7851
7852             var l = this.getLeft(true),
7853                 t = this.getTop(true);
7854
7855             if(sw && !this.shadowDisabled){
7856                 if(doShow && !sw.isVisible()){
7857                     sw.show(this);
7858                 }else{
7859                     sw.realign(l, t, w, h);
7860                 }
7861                 if(sh){
7862                     if(doShow){
7863                        sh.show();
7864                     }
7865                     // fit the shim behind the shadow, so it is shimmed too
7866                     var a = sw.adjusts, s = sh.dom.style;
7867                     s.left = (Math.min(l, l+a.l))+"px";
7868                     s.top = (Math.min(t, t+a.t))+"px";
7869                     s.width = (w+a.w)+"px";
7870                     s.height = (h+a.h)+"px";
7871                 }
7872             }else if(sh){
7873                 if(doShow){
7874                    sh.show();
7875                 }
7876                 sh.setSize(w, h);
7877                 sh.setLeftTop(l, t);
7878             }
7879             
7880         }
7881     },
7882
7883     // private
7884     destroy : function(){
7885         this.hideShim();
7886         if(this.shadow){
7887             this.shadow.hide();
7888         }
7889         this.removeAllListeners();
7890         var pn = this.dom.parentNode;
7891         if(pn){
7892             pn.removeChild(this.dom);
7893         }
7894         Roo.Element.uncache(this.id);
7895     },
7896
7897     remove : function(){
7898         this.destroy();
7899     },
7900
7901     // private
7902     beginUpdate : function(){
7903         this.updating = true;
7904     },
7905
7906     // private
7907     endUpdate : function(){
7908         this.updating = false;
7909         this.sync(true);
7910     },
7911
7912     // private
7913     hideUnders : function(negOffset){
7914         if(this.shadow){
7915             this.shadow.hide();
7916         }
7917         this.hideShim();
7918     },
7919
7920     // private
7921     constrainXY : function(){
7922         if(this.constrain){
7923             var vw = Roo.lib.Dom.getViewWidth(),
7924                 vh = Roo.lib.Dom.getViewHeight();
7925             var s = Roo.get(document).getScroll();
7926
7927             var xy = this.getXY();
7928             var x = xy[0], y = xy[1];   
7929             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7930             // only move it if it needs it
7931             var moved = false;
7932             // first validate right/bottom
7933             if((x + w) > vw+s.left){
7934                 x = vw - w - this.shadowOffset;
7935                 moved = true;
7936             }
7937             if((y + h) > vh+s.top){
7938                 y = vh - h - this.shadowOffset;
7939                 moved = true;
7940             }
7941             // then make sure top/left isn't negative
7942             if(x < s.left){
7943                 x = s.left;
7944                 moved = true;
7945             }
7946             if(y < s.top){
7947                 y = s.top;
7948                 moved = true;
7949             }
7950             if(moved){
7951                 if(this.avoidY){
7952                     var ay = this.avoidY;
7953                     if(y <= ay && (y+h) >= ay){
7954                         y = ay-h-5;   
7955                     }
7956                 }
7957                 xy = [x, y];
7958                 this.storeXY(xy);
7959                 supr.setXY.call(this, xy);
7960                 this.sync();
7961             }
7962         }
7963     },
7964
7965     isVisible : function(){
7966         return this.visible;    
7967     },
7968
7969     // private
7970     showAction : function(){
7971         this.visible = true; // track visibility to prevent getStyle calls
7972         if(this.useDisplay === true){
7973             this.setDisplayed("");
7974         }else if(this.lastXY){
7975             supr.setXY.call(this, this.lastXY);
7976         }else if(this.lastLT){
7977             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7978         }
7979     },
7980
7981     // private
7982     hideAction : function(){
7983         this.visible = false;
7984         if(this.useDisplay === true){
7985             this.setDisplayed(false);
7986         }else{
7987             this.setLeftTop(-10000,-10000);
7988         }
7989     },
7990
7991     // overridden Element method
7992     setVisible : function(v, a, d, c, e){
7993         if(v){
7994             this.showAction();
7995         }
7996         if(a && v){
7997             var cb = function(){
7998                 this.sync(true);
7999                 if(c){
8000                     c();
8001                 }
8002             }.createDelegate(this);
8003             supr.setVisible.call(this, true, true, d, cb, e);
8004         }else{
8005             if(!v){
8006                 this.hideUnders(true);
8007             }
8008             var cb = c;
8009             if(a){
8010                 cb = function(){
8011                     this.hideAction();
8012                     if(c){
8013                         c();
8014                     }
8015                 }.createDelegate(this);
8016             }
8017             supr.setVisible.call(this, v, a, d, cb, e);
8018             if(v){
8019                 this.sync(true);
8020             }else if(!a){
8021                 this.hideAction();
8022             }
8023         }
8024     },
8025
8026     storeXY : function(xy){
8027         delete this.lastLT;
8028         this.lastXY = xy;
8029     },
8030
8031     storeLeftTop : function(left, top){
8032         delete this.lastXY;
8033         this.lastLT = [left, top];
8034     },
8035
8036     // private
8037     beforeFx : function(){
8038         this.beforeAction();
8039         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8040     },
8041
8042     // private
8043     afterFx : function(){
8044         Roo.Layer.superclass.afterFx.apply(this, arguments);
8045         this.sync(this.isVisible());
8046     },
8047
8048     // private
8049     beforeAction : function(){
8050         if(!this.updating && this.shadow){
8051             this.shadow.hide();
8052         }
8053     },
8054
8055     // overridden Element method
8056     setLeft : function(left){
8057         this.storeLeftTop(left, this.getTop(true));
8058         supr.setLeft.apply(this, arguments);
8059         this.sync();
8060     },
8061
8062     setTop : function(top){
8063         this.storeLeftTop(this.getLeft(true), top);
8064         supr.setTop.apply(this, arguments);
8065         this.sync();
8066     },
8067
8068     setLeftTop : function(left, top){
8069         this.storeLeftTop(left, top);
8070         supr.setLeftTop.apply(this, arguments);
8071         this.sync();
8072     },
8073
8074     setXY : function(xy, a, d, c, e){
8075         this.fixDisplay();
8076         this.beforeAction();
8077         this.storeXY(xy);
8078         var cb = this.createCB(c);
8079         supr.setXY.call(this, xy, a, d, cb, e);
8080         if(!a){
8081             cb();
8082         }
8083     },
8084
8085     // private
8086     createCB : function(c){
8087         var el = this;
8088         return function(){
8089             el.constrainXY();
8090             el.sync(true);
8091             if(c){
8092                 c();
8093             }
8094         };
8095     },
8096
8097     // overridden Element method
8098     setX : function(x, a, d, c, e){
8099         this.setXY([x, this.getY()], a, d, c, e);
8100     },
8101
8102     // overridden Element method
8103     setY : function(y, a, d, c, e){
8104         this.setXY([this.getX(), y], a, d, c, e);
8105     },
8106
8107     // overridden Element method
8108     setSize : function(w, h, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setSize.call(this, w, h, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setWidth : function(w, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setWidth.call(this, w, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setHeight : function(h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         supr.setHeight.call(this, h, a, d, cb, e);
8132         if(!a){
8133             cb();
8134         }
8135     },
8136
8137     // overridden Element method
8138     setBounds : function(x, y, w, h, a, d, c, e){
8139         this.beforeAction();
8140         var cb = this.createCB(c);
8141         if(!a){
8142             this.storeXY([x, y]);
8143             supr.setXY.call(this, [x, y]);
8144             supr.setSize.call(this, w, h, a, d, cb, e);
8145             cb();
8146         }else{
8147             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8148         }
8149         return this;
8150     },
8151     
8152     /**
8153      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8154      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8155      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8156      * @param {Number} zindex The new z-index to set
8157      * @return {this} The Layer
8158      */
8159     setZIndex : function(zindex){
8160         this.zindex = zindex;
8161         this.setStyle("z-index", zindex + 2);
8162         if(this.shadow){
8163             this.shadow.setZIndex(zindex + 1);
8164         }
8165         if(this.shim){
8166             this.shim.setStyle("z-index", zindex);
8167         }
8168     }
8169 });
8170 })();/*
8171  * Based on:
8172  * Ext JS Library 1.1.1
8173  * Copyright(c) 2006-2007, Ext JS, LLC.
8174  *
8175  * Originally Released Under LGPL - original licence link has changed is not relivant.
8176  *
8177  * Fork - LGPL
8178  * <script type="text/javascript">
8179  */
8180
8181
8182 /**
8183  * @class Roo.Shadow
8184  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8185  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8186  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8187  * @constructor
8188  * Create a new Shadow
8189  * @param {Object} config The config object
8190  */
8191 Roo.Shadow = function(config){
8192     Roo.apply(this, config);
8193     if(typeof this.mode != "string"){
8194         this.mode = this.defaultMode;
8195     }
8196     var o = this.offset, a = {h: 0};
8197     var rad = Math.floor(this.offset/2);
8198     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8199         case "drop":
8200             a.w = 0;
8201             a.l = a.t = o;
8202             a.t -= 1;
8203             if(Roo.isIE){
8204                 a.l -= this.offset + rad;
8205                 a.t -= this.offset + rad;
8206                 a.w -= rad;
8207                 a.h -= rad;
8208                 a.t += 1;
8209             }
8210         break;
8211         case "sides":
8212             a.w = (o*2);
8213             a.l = -o;
8214             a.t = o-1;
8215             if(Roo.isIE){
8216                 a.l -= (this.offset - rad);
8217                 a.t -= this.offset + rad;
8218                 a.l += 1;
8219                 a.w -= (this.offset - rad)*2;
8220                 a.w -= rad + 1;
8221                 a.h -= 1;
8222             }
8223         break;
8224         case "frame":
8225             a.w = a.h = (o*2);
8226             a.l = a.t = -o;
8227             a.t += 1;
8228             a.h -= 2;
8229             if(Roo.isIE){
8230                 a.l -= (this.offset - rad);
8231                 a.t -= (this.offset - rad);
8232                 a.l += 1;
8233                 a.w -= (this.offset + rad + 1);
8234                 a.h -= (this.offset + rad);
8235                 a.h += 1;
8236             }
8237         break;
8238     };
8239
8240     this.adjusts = a;
8241 };
8242
8243 Roo.Shadow.prototype = {
8244     /**
8245      * @cfg {String} mode
8246      * The shadow display mode.  Supports the following options:<br />
8247      * sides: Shadow displays on both sides and bottom only<br />
8248      * frame: Shadow displays equally on all four sides<br />
8249      * drop: Traditional bottom-right drop shadow (default)
8250      */
8251     /**
8252      * @cfg {String} offset
8253      * The number of pixels to offset the shadow from the element (defaults to 4)
8254      */
8255     offset: 4,
8256
8257     // private
8258     defaultMode: "drop",
8259
8260     /**
8261      * Displays the shadow under the target element
8262      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8263      */
8264     show : function(target){
8265         target = Roo.get(target);
8266         if(!this.el){
8267             this.el = Roo.Shadow.Pool.pull();
8268             if(this.el.dom.nextSibling != target.dom){
8269                 this.el.insertBefore(target);
8270             }
8271         }
8272         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8273         if(Roo.isIE){
8274             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8275         }
8276         this.realign(
8277             target.getLeft(true),
8278             target.getTop(true),
8279             target.getWidth(),
8280             target.getHeight()
8281         );
8282         this.el.dom.style.display = "block";
8283     },
8284
8285     /**
8286      * Returns true if the shadow is visible, else false
8287      */
8288     isVisible : function(){
8289         return this.el ? true : false;  
8290     },
8291
8292     /**
8293      * Direct alignment when values are already available. Show must be called at least once before
8294      * calling this method to ensure it is initialized.
8295      * @param {Number} left The target element left position
8296      * @param {Number} top The target element top position
8297      * @param {Number} width The target element width
8298      * @param {Number} height The target element height
8299      */
8300     realign : function(l, t, w, h){
8301         if(!this.el){
8302             return;
8303         }
8304         var a = this.adjusts, d = this.el.dom, s = d.style;
8305         var iea = 0;
8306         s.left = (l+a.l)+"px";
8307         s.top = (t+a.t)+"px";
8308         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8309  
8310         if(s.width != sws || s.height != shs){
8311             s.width = sws;
8312             s.height = shs;
8313             if(!Roo.isIE){
8314                 var cn = d.childNodes;
8315                 var sww = Math.max(0, (sw-12))+"px";
8316                 cn[0].childNodes[1].style.width = sww;
8317                 cn[1].childNodes[1].style.width = sww;
8318                 cn[2].childNodes[1].style.width = sww;
8319                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8320             }
8321         }
8322     },
8323
8324     /**
8325      * Hides this shadow
8326      */
8327     hide : function(){
8328         if(this.el){
8329             this.el.dom.style.display = "none";
8330             Roo.Shadow.Pool.push(this.el);
8331             delete this.el;
8332         }
8333     },
8334
8335     /**
8336      * Adjust the z-index of this shadow
8337      * @param {Number} zindex The new z-index
8338      */
8339     setZIndex : function(z){
8340         this.zIndex = z;
8341         if(this.el){
8342             this.el.setStyle("z-index", z);
8343         }
8344     }
8345 };
8346
8347 // Private utility class that manages the internal Shadow cache
8348 Roo.Shadow.Pool = function(){
8349     var p = [];
8350     var markup = Roo.isIE ?
8351                  '<div class="x-ie-shadow"></div>' :
8352                  '<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>';
8353     return {
8354         pull : function(){
8355             var sh = p.shift();
8356             if(!sh){
8357                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8358                 sh.autoBoxAdjust = false;
8359             }
8360             return sh;
8361         },
8362
8363         push : function(sh){
8364             p.push(sh);
8365         }
8366     };
8367 }();/*
8368  * Based on:
8369  * Ext JS Library 1.1.1
8370  * Copyright(c) 2006-2007, Ext JS, LLC.
8371  *
8372  * Originally Released Under LGPL - original licence link has changed is not relivant.
8373  *
8374  * Fork - LGPL
8375  * <script type="text/javascript">
8376  */
8377
8378 /**
8379  * @class Roo.BoxComponent
8380  * @extends Roo.Component
8381  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8382  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8383  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8384  * layout containers.
8385  * @constructor
8386  * @param {Roo.Element/String/Object} config The configuration options.
8387  */
8388 Roo.BoxComponent = function(config){
8389     Roo.Component.call(this, config);
8390     this.addEvents({
8391         /**
8392          * @event resize
8393          * Fires after the component is resized.
8394              * @param {Roo.Component} this
8395              * @param {Number} adjWidth The box-adjusted width that was set
8396              * @param {Number} adjHeight The box-adjusted height that was set
8397              * @param {Number} rawWidth The width that was originally specified
8398              * @param {Number} rawHeight The height that was originally specified
8399              */
8400         resize : true,
8401         /**
8402          * @event move
8403          * Fires after the component is moved.
8404              * @param {Roo.Component} this
8405              * @param {Number} x The new x position
8406              * @param {Number} y The new y position
8407              */
8408         move : true
8409     });
8410 };
8411
8412 Roo.extend(Roo.BoxComponent, Roo.Component, {
8413     // private, set in afterRender to signify that the component has been rendered
8414     boxReady : false,
8415     // private, used to defer height settings to subclasses
8416     deferHeight: false,
8417     /** @cfg {Number} width
8418      * width (optional) size of component
8419      */
8420      /** @cfg {Number} height
8421      * height (optional) size of component
8422      */
8423      
8424     /**
8425      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8426      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8427      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8428      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8429      * @return {Roo.BoxComponent} this
8430      */
8431     setSize : function(w, h){
8432         // support for standard size objects
8433         if(typeof w == 'object'){
8434             h = w.height;
8435             w = w.width;
8436         }
8437         // not rendered
8438         if(!this.boxReady){
8439             this.width = w;
8440             this.height = h;
8441             return this;
8442         }
8443
8444         // prevent recalcs when not needed
8445         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8446             return this;
8447         }
8448         this.lastSize = {width: w, height: h};
8449
8450         var adj = this.adjustSize(w, h);
8451         var aw = adj.width, ah = adj.height;
8452         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8453             var rz = this.getResizeEl();
8454             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8455                 rz.setSize(aw, ah);
8456             }else if(!this.deferHeight && ah !== undefined){
8457                 rz.setHeight(ah);
8458             }else if(aw !== undefined){
8459                 rz.setWidth(aw);
8460             }
8461             this.onResize(aw, ah, w, h);
8462             this.fireEvent('resize', this, aw, ah, w, h);
8463         }
8464         return this;
8465     },
8466
8467     /**
8468      * Gets the current size of the component's underlying element.
8469      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8470      */
8471     getSize : function(){
8472         return this.el.getSize();
8473     },
8474
8475     /**
8476      * Gets the current XY position of the component's underlying element.
8477      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8478      * @return {Array} The XY position of the element (e.g., [100, 200])
8479      */
8480     getPosition : function(local){
8481         if(local === true){
8482             return [this.el.getLeft(true), this.el.getTop(true)];
8483         }
8484         return this.xy || this.el.getXY();
8485     },
8486
8487     /**
8488      * Gets the current box measurements of the component's underlying element.
8489      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8490      * @returns {Object} box An object in the format {x, y, width, height}
8491      */
8492     getBox : function(local){
8493         var s = this.el.getSize();
8494         if(local){
8495             s.x = this.el.getLeft(true);
8496             s.y = this.el.getTop(true);
8497         }else{
8498             var xy = this.xy || this.el.getXY();
8499             s.x = xy[0];
8500             s.y = xy[1];
8501         }
8502         return s;
8503     },
8504
8505     /**
8506      * Sets the current box measurements of the component's underlying element.
8507      * @param {Object} box An object in the format {x, y, width, height}
8508      * @returns {Roo.BoxComponent} this
8509      */
8510     updateBox : function(box){
8511         this.setSize(box.width, box.height);
8512         this.setPagePosition(box.x, box.y);
8513         return this;
8514     },
8515
8516     // protected
8517     getResizeEl : function(){
8518         return this.resizeEl || this.el;
8519     },
8520
8521     // protected
8522     getPositionEl : function(){
8523         return this.positionEl || this.el;
8524     },
8525
8526     /**
8527      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8528      * This method fires the move event.
8529      * @param {Number} left The new left
8530      * @param {Number} top The new top
8531      * @returns {Roo.BoxComponent} this
8532      */
8533     setPosition : function(x, y){
8534         this.x = x;
8535         this.y = y;
8536         if(!this.boxReady){
8537             return this;
8538         }
8539         var adj = this.adjustPosition(x, y);
8540         var ax = adj.x, ay = adj.y;
8541
8542         var el = this.getPositionEl();
8543         if(ax !== undefined || ay !== undefined){
8544             if(ax !== undefined && ay !== undefined){
8545                 el.setLeftTop(ax, ay);
8546             }else if(ax !== undefined){
8547                 el.setLeft(ax);
8548             }else if(ay !== undefined){
8549                 el.setTop(ay);
8550             }
8551             this.onPosition(ax, ay);
8552             this.fireEvent('move', this, ax, ay);
8553         }
8554         return this;
8555     },
8556
8557     /**
8558      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8559      * This method fires the move event.
8560      * @param {Number} x The new x position
8561      * @param {Number} y The new y position
8562      * @returns {Roo.BoxComponent} this
8563      */
8564     setPagePosition : function(x, y){
8565         this.pageX = x;
8566         this.pageY = y;
8567         if(!this.boxReady){
8568             return;
8569         }
8570         if(x === undefined || y === undefined){ // cannot translate undefined points
8571             return;
8572         }
8573         var p = this.el.translatePoints(x, y);
8574         this.setPosition(p.left, p.top);
8575         return this;
8576     },
8577
8578     // private
8579     onRender : function(ct, position){
8580         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8581         if(this.resizeEl){
8582             this.resizeEl = Roo.get(this.resizeEl);
8583         }
8584         if(this.positionEl){
8585             this.positionEl = Roo.get(this.positionEl);
8586         }
8587     },
8588
8589     // private
8590     afterRender : function(){
8591         Roo.BoxComponent.superclass.afterRender.call(this);
8592         this.boxReady = true;
8593         this.setSize(this.width, this.height);
8594         if(this.x || this.y){
8595             this.setPosition(this.x, this.y);
8596         }
8597         if(this.pageX || this.pageY){
8598             this.setPagePosition(this.pageX, this.pageY);
8599         }
8600     },
8601
8602     /**
8603      * Force the component's size to recalculate based on the underlying element's current height and width.
8604      * @returns {Roo.BoxComponent} this
8605      */
8606     syncSize : function(){
8607         delete this.lastSize;
8608         this.setSize(this.el.getWidth(), this.el.getHeight());
8609         return this;
8610     },
8611
8612     /**
8613      * Called after the component is resized, this method is empty by default but can be implemented by any
8614      * subclass that needs to perform custom logic after a resize occurs.
8615      * @param {Number} adjWidth The box-adjusted width that was set
8616      * @param {Number} adjHeight The box-adjusted height that was set
8617      * @param {Number} rawWidth The width that was originally specified
8618      * @param {Number} rawHeight The height that was originally specified
8619      */
8620     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8621
8622     },
8623
8624     /**
8625      * Called after the component is moved, this method is empty by default but can be implemented by any
8626      * subclass that needs to perform custom logic after a move occurs.
8627      * @param {Number} x The new x position
8628      * @param {Number} y The new y position
8629      */
8630     onPosition : function(x, y){
8631
8632     },
8633
8634     // private
8635     adjustSize : function(w, h){
8636         if(this.autoWidth){
8637             w = 'auto';
8638         }
8639         if(this.autoHeight){
8640             h = 'auto';
8641         }
8642         return {width : w, height: h};
8643     },
8644
8645     // private
8646     adjustPosition : function(x, y){
8647         return {x : x, y: y};
8648     }
8649 });/*
8650  * Based on:
8651  * Ext JS Library 1.1.1
8652  * Copyright(c) 2006-2007, Ext JS, LLC.
8653  *
8654  * Originally Released Under LGPL - original licence link has changed is not relivant.
8655  *
8656  * Fork - LGPL
8657  * <script type="text/javascript">
8658  */
8659
8660
8661 /**
8662  * @class Roo.SplitBar
8663  * @extends Roo.util.Observable
8664  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8665  * <br><br>
8666  * Usage:
8667  * <pre><code>
8668 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8669                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8670 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8671 split.minSize = 100;
8672 split.maxSize = 600;
8673 split.animate = true;
8674 split.on('moved', splitterMoved);
8675 </code></pre>
8676  * @constructor
8677  * Create a new SplitBar
8678  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8679  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8680  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8681  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8682                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8683                         position of the SplitBar).
8684  */
8685 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8686     
8687     /** @private */
8688     this.el = Roo.get(dragElement, true);
8689     this.el.dom.unselectable = "on";
8690     /** @private */
8691     this.resizingEl = Roo.get(resizingElement, true);
8692
8693     /**
8694      * @private
8695      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8696      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8697      * @type Number
8698      */
8699     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8700     
8701     /**
8702      * The minimum size of the resizing element. (Defaults to 0)
8703      * @type Number
8704      */
8705     this.minSize = 0;
8706     
8707     /**
8708      * The maximum size of the resizing element. (Defaults to 2000)
8709      * @type Number
8710      */
8711     this.maxSize = 2000;
8712     
8713     /**
8714      * Whether to animate the transition to the new size
8715      * @type Boolean
8716      */
8717     this.animate = false;
8718     
8719     /**
8720      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8721      * @type Boolean
8722      */
8723     this.useShim = false;
8724     
8725     /** @private */
8726     this.shim = null;
8727     
8728     if(!existingProxy){
8729         /** @private */
8730         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8731     }else{
8732         this.proxy = Roo.get(existingProxy).dom;
8733     }
8734     /** @private */
8735     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8736     
8737     /** @private */
8738     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8739     
8740     /** @private */
8741     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8742     
8743     /** @private */
8744     this.dragSpecs = {};
8745     
8746     /**
8747      * @private The adapter to use to positon and resize elements
8748      */
8749     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8750     this.adapter.init(this);
8751     
8752     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8753         /** @private */
8754         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8755         this.el.addClass("x-splitbar-h");
8756     }else{
8757         /** @private */
8758         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8759         this.el.addClass("x-splitbar-v");
8760     }
8761     
8762     this.addEvents({
8763         /**
8764          * @event resize
8765          * Fires when the splitter is moved (alias for {@link #event-moved})
8766          * @param {Roo.SplitBar} this
8767          * @param {Number} newSize the new width or height
8768          */
8769         "resize" : true,
8770         /**
8771          * @event moved
8772          * Fires when the splitter is moved
8773          * @param {Roo.SplitBar} this
8774          * @param {Number} newSize the new width or height
8775          */
8776         "moved" : true,
8777         /**
8778          * @event beforeresize
8779          * Fires before the splitter is dragged
8780          * @param {Roo.SplitBar} this
8781          */
8782         "beforeresize" : true,
8783
8784         "beforeapply" : true
8785     });
8786
8787     Roo.util.Observable.call(this);
8788 };
8789
8790 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8791     onStartProxyDrag : function(x, y){
8792         this.fireEvent("beforeresize", this);
8793         if(!this.overlay){
8794             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8795             o.unselectable();
8796             o.enableDisplayMode("block");
8797             // all splitbars share the same overlay
8798             Roo.SplitBar.prototype.overlay = o;
8799         }
8800         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8801         this.overlay.show();
8802         Roo.get(this.proxy).setDisplayed("block");
8803         var size = this.adapter.getElementSize(this);
8804         this.activeMinSize = this.getMinimumSize();;
8805         this.activeMaxSize = this.getMaximumSize();;
8806         var c1 = size - this.activeMinSize;
8807         var c2 = Math.max(this.activeMaxSize - size, 0);
8808         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8809             this.dd.resetConstraints();
8810             this.dd.setXConstraint(
8811                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8812                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8813             );
8814             this.dd.setYConstraint(0, 0);
8815         }else{
8816             this.dd.resetConstraints();
8817             this.dd.setXConstraint(0, 0);
8818             this.dd.setYConstraint(
8819                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8820                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8821             );
8822          }
8823         this.dragSpecs.startSize = size;
8824         this.dragSpecs.startPoint = [x, y];
8825         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8826     },
8827     
8828     /** 
8829      * @private Called after the drag operation by the DDProxy
8830      */
8831     onEndProxyDrag : function(e){
8832         Roo.get(this.proxy).setDisplayed(false);
8833         var endPoint = Roo.lib.Event.getXY(e);
8834         if(this.overlay){
8835             this.overlay.hide();
8836         }
8837         var newSize;
8838         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8839             newSize = this.dragSpecs.startSize + 
8840                 (this.placement == Roo.SplitBar.LEFT ?
8841                     endPoint[0] - this.dragSpecs.startPoint[0] :
8842                     this.dragSpecs.startPoint[0] - endPoint[0]
8843                 );
8844         }else{
8845             newSize = this.dragSpecs.startSize + 
8846                 (this.placement == Roo.SplitBar.TOP ?
8847                     endPoint[1] - this.dragSpecs.startPoint[1] :
8848                     this.dragSpecs.startPoint[1] - endPoint[1]
8849                 );
8850         }
8851         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8852         if(newSize != this.dragSpecs.startSize){
8853             if(this.fireEvent('beforeapply', this, newSize) !== false){
8854                 this.adapter.setElementSize(this, newSize);
8855                 this.fireEvent("moved", this, newSize);
8856                 this.fireEvent("resize", this, newSize);
8857             }
8858         }
8859     },
8860     
8861     /**
8862      * Get the adapter this SplitBar uses
8863      * @return The adapter object
8864      */
8865     getAdapter : function(){
8866         return this.adapter;
8867     },
8868     
8869     /**
8870      * Set the adapter this SplitBar uses
8871      * @param {Object} adapter A SplitBar adapter object
8872      */
8873     setAdapter : function(adapter){
8874         this.adapter = adapter;
8875         this.adapter.init(this);
8876     },
8877     
8878     /**
8879      * Gets the minimum size for the resizing element
8880      * @return {Number} The minimum size
8881      */
8882     getMinimumSize : function(){
8883         return this.minSize;
8884     },
8885     
8886     /**
8887      * Sets the minimum size for the resizing element
8888      * @param {Number} minSize The minimum size
8889      */
8890     setMinimumSize : function(minSize){
8891         this.minSize = minSize;
8892     },
8893     
8894     /**
8895      * Gets the maximum size for the resizing element
8896      * @return {Number} The maximum size
8897      */
8898     getMaximumSize : function(){
8899         return this.maxSize;
8900     },
8901     
8902     /**
8903      * Sets the maximum size for the resizing element
8904      * @param {Number} maxSize The maximum size
8905      */
8906     setMaximumSize : function(maxSize){
8907         this.maxSize = maxSize;
8908     },
8909     
8910     /**
8911      * Sets the initialize size for the resizing element
8912      * @param {Number} size The initial size
8913      */
8914     setCurrentSize : function(size){
8915         var oldAnimate = this.animate;
8916         this.animate = false;
8917         this.adapter.setElementSize(this, size);
8918         this.animate = oldAnimate;
8919     },
8920     
8921     /**
8922      * Destroy this splitbar. 
8923      * @param {Boolean} removeEl True to remove the element
8924      */
8925     destroy : function(removeEl){
8926         if(this.shim){
8927             this.shim.remove();
8928         }
8929         this.dd.unreg();
8930         this.proxy.parentNode.removeChild(this.proxy);
8931         if(removeEl){
8932             this.el.remove();
8933         }
8934     }
8935 });
8936
8937 /**
8938  * @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.
8939  */
8940 Roo.SplitBar.createProxy = function(dir){
8941     var proxy = new Roo.Element(document.createElement("div"));
8942     proxy.unselectable();
8943     var cls = 'x-splitbar-proxy';
8944     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8945     document.body.appendChild(proxy.dom);
8946     return proxy.dom;
8947 };
8948
8949 /** 
8950  * @class Roo.SplitBar.BasicLayoutAdapter
8951  * Default Adapter. It assumes the splitter and resizing element are not positioned
8952  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8953  */
8954 Roo.SplitBar.BasicLayoutAdapter = function(){
8955 };
8956
8957 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8958     // do nothing for now
8959     init : function(s){
8960     
8961     },
8962     /**
8963      * Called before drag operations to get the current size of the resizing element. 
8964      * @param {Roo.SplitBar} s The SplitBar using this adapter
8965      */
8966      getElementSize : function(s){
8967         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8968             return s.resizingEl.getWidth();
8969         }else{
8970             return s.resizingEl.getHeight();
8971         }
8972     },
8973     
8974     /**
8975      * Called after drag operations to set the size of the resizing element.
8976      * @param {Roo.SplitBar} s The SplitBar using this adapter
8977      * @param {Number} newSize The new size to set
8978      * @param {Function} onComplete A function to be invoked when resizing is complete
8979      */
8980     setElementSize : function(s, newSize, onComplete){
8981         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8982             if(!s.animate){
8983                 s.resizingEl.setWidth(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }else{
8991             
8992             if(!s.animate){
8993                 s.resizingEl.setHeight(newSize);
8994                 if(onComplete){
8995                     onComplete(s, newSize);
8996                 }
8997             }else{
8998                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8999             }
9000         }
9001     }
9002 };
9003
9004 /** 
9005  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9006  * @extends Roo.SplitBar.BasicLayoutAdapter
9007  * Adapter that  moves the splitter element to align with the resized sizing element. 
9008  * Used with an absolute positioned SplitBar.
9009  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9010  * document.body, make sure you assign an id to the body element.
9011  */
9012 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9013     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9014     this.container = Roo.get(container);
9015 };
9016
9017 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9018     init : function(s){
9019         this.basic.init(s);
9020     },
9021     
9022     getElementSize : function(s){
9023         return this.basic.getElementSize(s);
9024     },
9025     
9026     setElementSize : function(s, newSize, onComplete){
9027         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9028     },
9029     
9030     moveSplitter : function(s){
9031         var yes = Roo.SplitBar;
9032         switch(s.placement){
9033             case yes.LEFT:
9034                 s.el.setX(s.resizingEl.getRight());
9035                 break;
9036             case yes.RIGHT:
9037                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9038                 break;
9039             case yes.TOP:
9040                 s.el.setY(s.resizingEl.getBottom());
9041                 break;
9042             case yes.BOTTOM:
9043                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9044                 break;
9045         }
9046     }
9047 };
9048
9049 /**
9050  * Orientation constant - Create a vertical SplitBar
9051  * @static
9052  * @type Number
9053  */
9054 Roo.SplitBar.VERTICAL = 1;
9055
9056 /**
9057  * Orientation constant - Create a horizontal SplitBar
9058  * @static
9059  * @type Number
9060  */
9061 Roo.SplitBar.HORIZONTAL = 2;
9062
9063 /**
9064  * Placement constant - The resizing element is to the left of the splitter element
9065  * @static
9066  * @type Number
9067  */
9068 Roo.SplitBar.LEFT = 1;
9069
9070 /**
9071  * Placement constant - The resizing element is to the right of the splitter element
9072  * @static
9073  * @type Number
9074  */
9075 Roo.SplitBar.RIGHT = 2;
9076
9077 /**
9078  * Placement constant - The resizing element is positioned above the splitter element
9079  * @static
9080  * @type Number
9081  */
9082 Roo.SplitBar.TOP = 3;
9083
9084 /**
9085  * Placement constant - The resizing element is positioned under splitter element
9086  * @static
9087  * @type Number
9088  */
9089 Roo.SplitBar.BOTTOM = 4;
9090 /*
9091  * Based on:
9092  * Ext JS Library 1.1.1
9093  * Copyright(c) 2006-2007, Ext JS, LLC.
9094  *
9095  * Originally Released Under LGPL - original licence link has changed is not relivant.
9096  *
9097  * Fork - LGPL
9098  * <script type="text/javascript">
9099  */
9100
9101 /**
9102  * @class Roo.View
9103  * @extends Roo.util.Observable
9104  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9105  * This class also supports single and multi selection modes. <br>
9106  * Create a data model bound view:
9107  <pre><code>
9108  var store = new Roo.data.Store(...);
9109
9110  var view = new Roo.View({
9111     el : "my-element",
9112     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9113  
9114     singleSelect: true,
9115     selectedClass: "ydataview-selected",
9116     store: store
9117  });
9118
9119  // listen for node click?
9120  view.on("click", function(vw, index, node, e){
9121  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9122  });
9123
9124  // load XML data
9125  dataModel.load("foobar.xml");
9126  </code></pre>
9127  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9128  * <br><br>
9129  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9130  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9131  * 
9132  * Note: old style constructor is still suported (container, template, config)
9133  * 
9134  * @constructor
9135  * Create a new View
9136  * @param {Object} config The config object
9137  * 
9138  */
9139 Roo.View = function(config, depreciated_tpl, depreciated_config){
9140     
9141     if (typeof(depreciated_tpl) == 'undefined') {
9142         // new way.. - universal constructor.
9143         Roo.apply(this, config);
9144         this.el  = Roo.get(this.el);
9145     } else {
9146         // old format..
9147         this.el  = Roo.get(config);
9148         this.tpl = depreciated_tpl;
9149         Roo.apply(this, depreciated_config);
9150     }
9151      
9152     
9153     if(typeof(this.tpl) == "string"){
9154         this.tpl = new Roo.Template(this.tpl);
9155     } else {
9156         // support xtype ctors..
9157         this.tpl = new Roo.factory(this.tpl, Roo);
9158     }
9159     
9160     
9161     this.tpl.compile();
9162    
9163
9164      
9165     /** @private */
9166     this.addEvents({
9167         /**
9168          * @event beforeclick
9169          * Fires before a click is processed. Returns false to cancel the default action.
9170          * @param {Roo.View} this
9171          * @param {Number} index The index of the target node
9172          * @param {HTMLElement} node The target node
9173          * @param {Roo.EventObject} e The raw event object
9174          */
9175             "beforeclick" : true,
9176         /**
9177          * @event click
9178          * Fires when a template node is clicked.
9179          * @param {Roo.View} this
9180          * @param {Number} index The index of the target node
9181          * @param {HTMLElement} node The target node
9182          * @param {Roo.EventObject} e The raw event object
9183          */
9184             "click" : true,
9185         /**
9186          * @event dblclick
9187          * Fires when a template node is double clicked.
9188          * @param {Roo.View} this
9189          * @param {Number} index The index of the target node
9190          * @param {HTMLElement} node The target node
9191          * @param {Roo.EventObject} e The raw event object
9192          */
9193             "dblclick" : true,
9194         /**
9195          * @event contextmenu
9196          * Fires when a template node is right clicked.
9197          * @param {Roo.View} this
9198          * @param {Number} index The index of the target node
9199          * @param {HTMLElement} node The target node
9200          * @param {Roo.EventObject} e The raw event object
9201          */
9202             "contextmenu" : true,
9203         /**
9204          * @event selectionchange
9205          * Fires when the selected nodes change.
9206          * @param {Roo.View} this
9207          * @param {Array} selections Array of the selected nodes
9208          */
9209             "selectionchange" : true,
9210     
9211         /**
9212          * @event beforeselect
9213          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9214          * @param {Roo.View} this
9215          * @param {HTMLElement} node The node to be selected
9216          * @param {Array} selections Array of currently selected nodes
9217          */
9218             "beforeselect" : true,
9219         /**
9220          * @event preparedata
9221          * Fires on every row to render, to allow you to change the data.
9222          * @param {Roo.View} this
9223          * @param {Object} data to be rendered (change this)
9224          */
9225           "preparedata" : true
9226         });
9227
9228     this.el.on({
9229         "click": this.onClick,
9230         "dblclick": this.onDblClick,
9231         "contextmenu": this.onContextMenu,
9232         scope:this
9233     });
9234
9235     this.selections = [];
9236     this.nodes = [];
9237     this.cmp = new Roo.CompositeElementLite([]);
9238     if(this.store){
9239         this.store = Roo.factory(this.store, Roo.data);
9240         this.setStore(this.store, true);
9241     }
9242     Roo.View.superclass.constructor.call(this);
9243 };
9244
9245 Roo.extend(Roo.View, Roo.util.Observable, {
9246     
9247      /**
9248      * @cfg {Roo.data.Store} store Data store to load data from.
9249      */
9250     store : false,
9251     
9252     /**
9253      * @cfg {String|Roo.Element} el The container element.
9254      */
9255     el : '',
9256     
9257     /**
9258      * @cfg {String|Roo.Template} tpl The template used by this View 
9259      */
9260     tpl : false,
9261     /**
9262      * @cfg {String} dataName the named area of the template to use as the data area
9263      *                          Works with domtemplates roo-name="name"
9264      */
9265     dataName: false,
9266     /**
9267      * @cfg {String} selectedClass The css class to add to selected nodes
9268      */
9269     selectedClass : "x-view-selected",
9270      /**
9271      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9272      */
9273     emptyText : "",
9274     /**
9275      * @cfg {Boolean} multiSelect Allow multiple selection
9276      */
9277     multiSelect : false,
9278     /**
9279      * @cfg {Boolean} singleSelect Allow single selection
9280      */
9281     singleSelect:  false,
9282     
9283     /**
9284      * @cfg {Boolean} toggleSelect - selecting 
9285      */
9286     toggleSelect : false,
9287     
9288     /**
9289      * Returns the element this view is bound to.
9290      * @return {Roo.Element}
9291      */
9292     getEl : function(){
9293         return this.el;
9294     },
9295
9296     /**
9297      * Refreshes the view.
9298      */
9299     refresh : function(){
9300         var t = this.tpl;
9301         
9302         // if we are using something like 'domtemplate', then
9303         // the what gets used is:
9304         // t.applySubtemplate(NAME, data, wrapping data..)
9305         // the outer template then get' applied with
9306         //     the store 'extra data'
9307         // and the body get's added to the
9308         //      roo-name="data" node?
9309         //      <span class='roo-tpl-{name}'></span> ?????
9310         
9311         
9312         
9313         this.clearSelections();
9314         this.el.update("");
9315         var html = [];
9316         var records = this.store.getRange();
9317         if(records.length < 1) {
9318             
9319             // is this valid??  = should it render a template??
9320             
9321             this.el.update(this.emptyText);
9322             return;
9323         }
9324         var el = this.el;
9325         if (this.dataName) {
9326             this.el.update(t.apply(this.store.meta)); //????
9327             el = this.el.child('.roo-tpl-' + this.dataName);
9328         }
9329         
9330         for(var i = 0, len = records.length; i < len; i++){
9331             var data = this.prepareData(records[i].data, i, records[i]);
9332             this.fireEvent("preparedata", this, data, i, records[i]);
9333             html[html.length] = Roo.util.Format.trim(
9334                 this.dataName ?
9335                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9336                     t.apply(data)
9337             );
9338         }
9339         
9340         
9341         
9342         el.update(html.join(""));
9343         this.nodes = el.dom.childNodes;
9344         this.updateIndexes(0);
9345     },
9346
9347     /**
9348      * Function to override to reformat the data that is sent to
9349      * the template for each node.
9350      * DEPRICATED - use the preparedata event handler.
9351      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9352      * a JSON object for an UpdateManager bound view).
9353      */
9354     prepareData : function(data, index, record)
9355     {
9356         this.fireEvent("preparedata", this, data, index, record);
9357         return data;
9358     },
9359
9360     onUpdate : function(ds, record){
9361         this.clearSelections();
9362         var index = this.store.indexOf(record);
9363         var n = this.nodes[index];
9364         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9365         n.parentNode.removeChild(n);
9366         this.updateIndexes(index, index);
9367     },
9368
9369     
9370     
9371 // --------- FIXME     
9372     onAdd : function(ds, records, index)
9373     {
9374         this.clearSelections();
9375         if(this.nodes.length == 0){
9376             this.refresh();
9377             return;
9378         }
9379         var n = this.nodes[index];
9380         for(var i = 0, len = records.length; i < len; i++){
9381             var d = this.prepareData(records[i].data, i, records[i]);
9382             if(n){
9383                 this.tpl.insertBefore(n, d);
9384             }else{
9385                 
9386                 this.tpl.append(this.el, d);
9387             }
9388         }
9389         this.updateIndexes(index);
9390     },
9391
9392     onRemove : function(ds, record, index){
9393         this.clearSelections();
9394         var el = this.dataName  ?
9395             this.el.child('.roo-tpl-' + this.dataName) :
9396             this.el; 
9397         el.dom.removeChild(this.nodes[index]);
9398         this.updateIndexes(index);
9399     },
9400
9401     /**
9402      * Refresh an individual node.
9403      * @param {Number} index
9404      */
9405     refreshNode : function(index){
9406         this.onUpdate(this.store, this.store.getAt(index));
9407     },
9408
9409     updateIndexes : function(startIndex, endIndex){
9410         var ns = this.nodes;
9411         startIndex = startIndex || 0;
9412         endIndex = endIndex || ns.length - 1;
9413         for(var i = startIndex; i <= endIndex; i++){
9414             ns[i].nodeIndex = i;
9415         }
9416     },
9417
9418     /**
9419      * Changes the data store this view uses and refresh the view.
9420      * @param {Store} store
9421      */
9422     setStore : function(store, initial){
9423         if(!initial && this.store){
9424             this.store.un("datachanged", this.refresh);
9425             this.store.un("add", this.onAdd);
9426             this.store.un("remove", this.onRemove);
9427             this.store.un("update", this.onUpdate);
9428             this.store.un("clear", this.refresh);
9429         }
9430         if(store){
9431           
9432             store.on("datachanged", this.refresh, this);
9433             store.on("add", this.onAdd, this);
9434             store.on("remove", this.onRemove, this);
9435             store.on("update", this.onUpdate, this);
9436             store.on("clear", this.refresh, this);
9437         }
9438         
9439         if(store){
9440             this.refresh();
9441         }
9442     },
9443
9444     /**
9445      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9446      * @param {HTMLElement} node
9447      * @return {HTMLElement} The template node
9448      */
9449     findItemFromChild : function(node){
9450         var el = this.dataName  ?
9451             this.el.child('.roo-tpl-' + this.dataName,true) :
9452             this.el.dom; 
9453         
9454         if(!node || node.parentNode == el){
9455                     return node;
9456             }
9457             var p = node.parentNode;
9458             while(p && p != el){
9459             if(p.parentNode == el){
9460                 return p;
9461             }
9462             p = p.parentNode;
9463         }
9464             return null;
9465     },
9466
9467     /** @ignore */
9468     onClick : function(e){
9469         var item = this.findItemFromChild(e.getTarget());
9470         if(item){
9471             var index = this.indexOf(item);
9472             if(this.onItemClick(item, index, e) !== false){
9473                 this.fireEvent("click", this, index, item, e);
9474             }
9475         }else{
9476             this.clearSelections();
9477         }
9478     },
9479
9480     /** @ignore */
9481     onContextMenu : function(e){
9482         var item = this.findItemFromChild(e.getTarget());
9483         if(item){
9484             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9485         }
9486     },
9487
9488     /** @ignore */
9489     onDblClick : function(e){
9490         var item = this.findItemFromChild(e.getTarget());
9491         if(item){
9492             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9493         }
9494     },
9495
9496     onItemClick : function(item, index, e)
9497     {
9498         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9499             return false;
9500         }
9501         if (this.toggleSelect) {
9502             var m = this.isSelected(item) ? 'unselect' : 'select';
9503             Roo.log(m);
9504             var _t = this;
9505             _t[m](item, true, false);
9506             return true;
9507         }
9508         if(this.multiSelect || this.singleSelect){
9509             if(this.multiSelect && e.shiftKey && this.lastSelection){
9510                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9511             }else{
9512                 this.select(item, this.multiSelect && e.ctrlKey);
9513                 this.lastSelection = item;
9514             }
9515             e.preventDefault();
9516         }
9517         return true;
9518     },
9519
9520     /**
9521      * Get the number of selected nodes.
9522      * @return {Number}
9523      */
9524     getSelectionCount : function(){
9525         return this.selections.length;
9526     },
9527
9528     /**
9529      * Get the currently selected nodes.
9530      * @return {Array} An array of HTMLElements
9531      */
9532     getSelectedNodes : function(){
9533         return this.selections;
9534     },
9535
9536     /**
9537      * Get the indexes of the selected nodes.
9538      * @return {Array}
9539      */
9540     getSelectedIndexes : function(){
9541         var indexes = [], s = this.selections;
9542         for(var i = 0, len = s.length; i < len; i++){
9543             indexes.push(s[i].nodeIndex);
9544         }
9545         return indexes;
9546     },
9547
9548     /**
9549      * Clear all selections
9550      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9551      */
9552     clearSelections : function(suppressEvent){
9553         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9554             this.cmp.elements = this.selections;
9555             this.cmp.removeClass(this.selectedClass);
9556             this.selections = [];
9557             if(!suppressEvent){
9558                 this.fireEvent("selectionchange", this, this.selections);
9559             }
9560         }
9561     },
9562
9563     /**
9564      * Returns true if the passed node is selected
9565      * @param {HTMLElement/Number} node The node or node index
9566      * @return {Boolean}
9567      */
9568     isSelected : function(node){
9569         var s = this.selections;
9570         if(s.length < 1){
9571             return false;
9572         }
9573         node = this.getNode(node);
9574         return s.indexOf(node) !== -1;
9575     },
9576
9577     /**
9578      * Selects nodes.
9579      * @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
9580      * @param {Boolean} keepExisting (optional) true to keep existing selections
9581      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9582      */
9583     select : function(nodeInfo, keepExisting, suppressEvent){
9584         if(nodeInfo instanceof Array){
9585             if(!keepExisting){
9586                 this.clearSelections(true);
9587             }
9588             for(var i = 0, len = nodeInfo.length; i < len; i++){
9589                 this.select(nodeInfo[i], true, true);
9590             }
9591             return;
9592         } 
9593         var node = this.getNode(nodeInfo);
9594         if(!node || this.isSelected(node)){
9595             return; // already selected.
9596         }
9597         if(!keepExisting){
9598             this.clearSelections(true);
9599         }
9600         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9601             Roo.fly(node).addClass(this.selectedClass);
9602             this.selections.push(node);
9603             if(!suppressEvent){
9604                 this.fireEvent("selectionchange", this, this.selections);
9605             }
9606         }
9607         
9608         
9609     },
9610       /**
9611      * Unselects nodes.
9612      * @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
9613      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9614      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9615      */
9616     unselect : function(nodeInfo, keepExisting, suppressEvent)
9617     {
9618         if(nodeInfo instanceof Array){
9619             Roo.each(this.selections, function(s) {
9620                 this.unselect(s, nodeInfo);
9621             }, this);
9622             return;
9623         }
9624         var node = this.getNode(nodeInfo);
9625         if(!node || !this.isSelected(node)){
9626             Roo.log("not selected");
9627             return; // not selected.
9628         }
9629         // fireevent???
9630         var ns = [];
9631         Roo.each(this.selections, function(s) {
9632             if (s == node ) {
9633                 Roo.fly(node).removeClass(this.selectedClass);
9634
9635                 return;
9636             }
9637             ns.push(s);
9638         },this);
9639         
9640         this.selections= ns;
9641         this.fireEvent("selectionchange", this, this.selections);
9642     },
9643
9644     /**
9645      * Gets a template node.
9646      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9647      * @return {HTMLElement} The node or null if it wasn't found
9648      */
9649     getNode : function(nodeInfo){
9650         if(typeof nodeInfo == "string"){
9651             return document.getElementById(nodeInfo);
9652         }else if(typeof nodeInfo == "number"){
9653             return this.nodes[nodeInfo];
9654         }
9655         return nodeInfo;
9656     },
9657
9658     /**
9659      * Gets a range template nodes.
9660      * @param {Number} startIndex
9661      * @param {Number} endIndex
9662      * @return {Array} An array of nodes
9663      */
9664     getNodes : function(start, end){
9665         var ns = this.nodes;
9666         start = start || 0;
9667         end = typeof end == "undefined" ? ns.length - 1 : end;
9668         var nodes = [];
9669         if(start <= end){
9670             for(var i = start; i <= end; i++){
9671                 nodes.push(ns[i]);
9672             }
9673         } else{
9674             for(var i = start; i >= end; i--){
9675                 nodes.push(ns[i]);
9676             }
9677         }
9678         return nodes;
9679     },
9680
9681     /**
9682      * Finds the index of the passed node
9683      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9684      * @return {Number} The index of the node or -1
9685      */
9686     indexOf : function(node){
9687         node = this.getNode(node);
9688         if(typeof node.nodeIndex == "number"){
9689             return node.nodeIndex;
9690         }
9691         var ns = this.nodes;
9692         for(var i = 0, len = ns.length; i < len; i++){
9693             if(ns[i] == node){
9694                 return i;
9695             }
9696         }
9697         return -1;
9698     }
9699 });
9700 /*
9701  * Based on:
9702  * Ext JS Library 1.1.1
9703  * Copyright(c) 2006-2007, Ext JS, LLC.
9704  *
9705  * Originally Released Under LGPL - original licence link has changed is not relivant.
9706  *
9707  * Fork - LGPL
9708  * <script type="text/javascript">
9709  */
9710
9711 /**
9712  * @class Roo.JsonView
9713  * @extends Roo.View
9714  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9715 <pre><code>
9716 var view = new Roo.JsonView({
9717     container: "my-element",
9718     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9719     multiSelect: true, 
9720     jsonRoot: "data" 
9721 });
9722
9723 // listen for node click?
9724 view.on("click", function(vw, index, node, e){
9725     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9726 });
9727
9728 // direct load of JSON data
9729 view.load("foobar.php");
9730
9731 // Example from my blog list
9732 var tpl = new Roo.Template(
9733     '&lt;div class="entry"&gt;' +
9734     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9735     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9736     "&lt;/div&gt;&lt;hr /&gt;"
9737 );
9738
9739 var moreView = new Roo.JsonView({
9740     container :  "entry-list", 
9741     template : tpl,
9742     jsonRoot: "posts"
9743 });
9744 moreView.on("beforerender", this.sortEntries, this);
9745 moreView.load({
9746     url: "/blog/get-posts.php",
9747     params: "allposts=true",
9748     text: "Loading Blog Entries..."
9749 });
9750 </code></pre>
9751
9752 * Note: old code is supported with arguments : (container, template, config)
9753
9754
9755  * @constructor
9756  * Create a new JsonView
9757  * 
9758  * @param {Object} config The config object
9759  * 
9760  */
9761 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9762     
9763     
9764     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9765
9766     var um = this.el.getUpdateManager();
9767     um.setRenderer(this);
9768     um.on("update", this.onLoad, this);
9769     um.on("failure", this.onLoadException, this);
9770
9771     /**
9772      * @event beforerender
9773      * Fires before rendering of the downloaded JSON data.
9774      * @param {Roo.JsonView} this
9775      * @param {Object} data The JSON data loaded
9776      */
9777     /**
9778      * @event load
9779      * Fires when data is loaded.
9780      * @param {Roo.JsonView} this
9781      * @param {Object} data The JSON data loaded
9782      * @param {Object} response The raw Connect response object
9783      */
9784     /**
9785      * @event loadexception
9786      * Fires when loading fails.
9787      * @param {Roo.JsonView} this
9788      * @param {Object} response The raw Connect response object
9789      */
9790     this.addEvents({
9791         'beforerender' : true,
9792         'load' : true,
9793         'loadexception' : true
9794     });
9795 };
9796 Roo.extend(Roo.JsonView, Roo.View, {
9797     /**
9798      * @type {String} The root property in the loaded JSON object that contains the data
9799      */
9800     jsonRoot : "",
9801
9802     /**
9803      * Refreshes the view.
9804      */
9805     refresh : function(){
9806         this.clearSelections();
9807         this.el.update("");
9808         var html = [];
9809         var o = this.jsonData;
9810         if(o && o.length > 0){
9811             for(var i = 0, len = o.length; i < len; i++){
9812                 var data = this.prepareData(o[i], i, o);
9813                 html[html.length] = this.tpl.apply(data);
9814             }
9815         }else{
9816             html.push(this.emptyText);
9817         }
9818         this.el.update(html.join(""));
9819         this.nodes = this.el.dom.childNodes;
9820         this.updateIndexes(0);
9821     },
9822
9823     /**
9824      * 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.
9825      * @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:
9826      <pre><code>
9827      view.load({
9828          url: "your-url.php",
9829          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9830          callback: yourFunction,
9831          scope: yourObject, //(optional scope)
9832          discardUrl: false,
9833          nocache: false,
9834          text: "Loading...",
9835          timeout: 30,
9836          scripts: false
9837      });
9838      </code></pre>
9839      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9840      * 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.
9841      * @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}
9842      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9843      * @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.
9844      */
9845     load : function(){
9846         var um = this.el.getUpdateManager();
9847         um.update.apply(um, arguments);
9848     },
9849
9850     render : function(el, response){
9851         this.clearSelections();
9852         this.el.update("");
9853         var o;
9854         try{
9855             o = Roo.util.JSON.decode(response.responseText);
9856             if(this.jsonRoot){
9857                 
9858                 o = o[this.jsonRoot];
9859             }
9860         } catch(e){
9861         }
9862         /**
9863          * The current JSON data or null
9864          */
9865         this.jsonData = o;
9866         this.beforeRender();
9867         this.refresh();
9868     },
9869
9870 /**
9871  * Get the number of records in the current JSON dataset
9872  * @return {Number}
9873  */
9874     getCount : function(){
9875         return this.jsonData ? this.jsonData.length : 0;
9876     },
9877
9878 /**
9879  * Returns the JSON object for the specified node(s)
9880  * @param {HTMLElement/Array} node The node or an array of nodes
9881  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9882  * you get the JSON object for the node
9883  */
9884     getNodeData : function(node){
9885         if(node instanceof Array){
9886             var data = [];
9887             for(var i = 0, len = node.length; i < len; i++){
9888                 data.push(this.getNodeData(node[i]));
9889             }
9890             return data;
9891         }
9892         return this.jsonData[this.indexOf(node)] || null;
9893     },
9894
9895     beforeRender : function(){
9896         this.snapshot = this.jsonData;
9897         if(this.sortInfo){
9898             this.sort.apply(this, this.sortInfo);
9899         }
9900         this.fireEvent("beforerender", this, this.jsonData);
9901     },
9902
9903     onLoad : function(el, o){
9904         this.fireEvent("load", this, this.jsonData, o);
9905     },
9906
9907     onLoadException : function(el, o){
9908         this.fireEvent("loadexception", this, o);
9909     },
9910
9911 /**
9912  * Filter the data by a specific property.
9913  * @param {String} property A property on your JSON objects
9914  * @param {String/RegExp} value Either string that the property values
9915  * should start with, or a RegExp to test against the property
9916  */
9917     filter : function(property, value){
9918         if(this.jsonData){
9919             var data = [];
9920             var ss = this.snapshot;
9921             if(typeof value == "string"){
9922                 var vlen = value.length;
9923                 if(vlen == 0){
9924                     this.clearFilter();
9925                     return;
9926                 }
9927                 value = value.toLowerCase();
9928                 for(var i = 0, len = ss.length; i < len; i++){
9929                     var o = ss[i];
9930                     if(o[property].substr(0, vlen).toLowerCase() == value){
9931                         data.push(o);
9932                     }
9933                 }
9934             } else if(value.exec){ // regex?
9935                 for(var i = 0, len = ss.length; i < len; i++){
9936                     var o = ss[i];
9937                     if(value.test(o[property])){
9938                         data.push(o);
9939                     }
9940                 }
9941             } else{
9942                 return;
9943             }
9944             this.jsonData = data;
9945             this.refresh();
9946         }
9947     },
9948
9949 /**
9950  * Filter by a function. The passed function will be called with each
9951  * object in the current dataset. If the function returns true the value is kept,
9952  * otherwise it is filtered.
9953  * @param {Function} fn
9954  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9955  */
9956     filterBy : function(fn, scope){
9957         if(this.jsonData){
9958             var data = [];
9959             var ss = this.snapshot;
9960             for(var i = 0, len = ss.length; i < len; i++){
9961                 var o = ss[i];
9962                 if(fn.call(scope || this, o)){
9963                     data.push(o);
9964                 }
9965             }
9966             this.jsonData = data;
9967             this.refresh();
9968         }
9969     },
9970
9971 /**
9972  * Clears the current filter.
9973  */
9974     clearFilter : function(){
9975         if(this.snapshot && this.jsonData != this.snapshot){
9976             this.jsonData = this.snapshot;
9977             this.refresh();
9978         }
9979     },
9980
9981
9982 /**
9983  * Sorts the data for this view and refreshes it.
9984  * @param {String} property A property on your JSON objects to sort on
9985  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9986  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9987  */
9988     sort : function(property, dir, sortType){
9989         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9990         if(this.jsonData){
9991             var p = property;
9992             var dsc = dir && dir.toLowerCase() == "desc";
9993             var f = function(o1, o2){
9994                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9995                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9996                 ;
9997                 if(v1 < v2){
9998                     return dsc ? +1 : -1;
9999                 } else if(v1 > v2){
10000                     return dsc ? -1 : +1;
10001                 } else{
10002                     return 0;
10003                 }
10004             };
10005             this.jsonData.sort(f);
10006             this.refresh();
10007             if(this.jsonData != this.snapshot){
10008                 this.snapshot.sort(f);
10009             }
10010         }
10011     }
10012 });/*
10013  * Based on:
10014  * Ext JS Library 1.1.1
10015  * Copyright(c) 2006-2007, Ext JS, LLC.
10016  *
10017  * Originally Released Under LGPL - original licence link has changed is not relivant.
10018  *
10019  * Fork - LGPL
10020  * <script type="text/javascript">
10021  */
10022  
10023
10024 /**
10025  * @class Roo.ColorPalette
10026  * @extends Roo.Component
10027  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10028  * Here's an example of typical usage:
10029  * <pre><code>
10030 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10031 cp.render('my-div');
10032
10033 cp.on('select', function(palette, selColor){
10034     // do something with selColor
10035 });
10036 </code></pre>
10037  * @constructor
10038  * Create a new ColorPalette
10039  * @param {Object} config The config object
10040  */
10041 Roo.ColorPalette = function(config){
10042     Roo.ColorPalette.superclass.constructor.call(this, config);
10043     this.addEvents({
10044         /**
10045              * @event select
10046              * Fires when a color is selected
10047              * @param {ColorPalette} this
10048              * @param {String} color The 6-digit color hex code (without the # symbol)
10049              */
10050         select: true
10051     });
10052
10053     if(this.handler){
10054         this.on("select", this.handler, this.scope, true);
10055     }
10056 };
10057 Roo.extend(Roo.ColorPalette, Roo.Component, {
10058     /**
10059      * @cfg {String} itemCls
10060      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10061      */
10062     itemCls : "x-color-palette",
10063     /**
10064      * @cfg {String} value
10065      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10066      * the hex codes are case-sensitive.
10067      */
10068     value : null,
10069     clickEvent:'click',
10070     // private
10071     ctype: "Roo.ColorPalette",
10072
10073     /**
10074      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10075      */
10076     allowReselect : false,
10077
10078     /**
10079      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10080      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10081      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10082      * of colors with the width setting until the box is symmetrical.</p>
10083      * <p>You can override individual colors if needed:</p>
10084      * <pre><code>
10085 var cp = new Roo.ColorPalette();
10086 cp.colors[0] = "FF0000";  // change the first box to red
10087 </code></pre>
10088
10089 Or you can provide a custom array of your own for complete control:
10090 <pre><code>
10091 var cp = new Roo.ColorPalette();
10092 cp.colors = ["000000", "993300", "333300"];
10093 </code></pre>
10094      * @type Array
10095      */
10096     colors : [
10097         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10098         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10099         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10100         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10101         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10102     ],
10103
10104     // private
10105     onRender : function(container, position){
10106         var t = new Roo.MasterTemplate(
10107             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10108         );
10109         var c = this.colors;
10110         for(var i = 0, len = c.length; i < len; i++){
10111             t.add([c[i]]);
10112         }
10113         var el = document.createElement("div");
10114         el.className = this.itemCls;
10115         t.overwrite(el);
10116         container.dom.insertBefore(el, position);
10117         this.el = Roo.get(el);
10118         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10119         if(this.clickEvent != 'click'){
10120             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10121         }
10122     },
10123
10124     // private
10125     afterRender : function(){
10126         Roo.ColorPalette.superclass.afterRender.call(this);
10127         if(this.value){
10128             var s = this.value;
10129             this.value = null;
10130             this.select(s);
10131         }
10132     },
10133
10134     // private
10135     handleClick : function(e, t){
10136         e.preventDefault();
10137         if(!this.disabled){
10138             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10139             this.select(c.toUpperCase());
10140         }
10141     },
10142
10143     /**
10144      * Selects the specified color in the palette (fires the select event)
10145      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10146      */
10147     select : function(color){
10148         color = color.replace("#", "");
10149         if(color != this.value || this.allowReselect){
10150             var el = this.el;
10151             if(this.value){
10152                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10153             }
10154             el.child("a.color-"+color).addClass("x-color-palette-sel");
10155             this.value = color;
10156             this.fireEvent("select", this, color);
10157         }
10158     }
10159 });/*
10160  * Based on:
10161  * Ext JS Library 1.1.1
10162  * Copyright(c) 2006-2007, Ext JS, LLC.
10163  *
10164  * Originally Released Under LGPL - original licence link has changed is not relivant.
10165  *
10166  * Fork - LGPL
10167  * <script type="text/javascript">
10168  */
10169  
10170 /**
10171  * @class Roo.DatePicker
10172  * @extends Roo.Component
10173  * Simple date picker class.
10174  * @constructor
10175  * Create a new DatePicker
10176  * @param {Object} config The config object
10177  */
10178 Roo.DatePicker = function(config){
10179     Roo.DatePicker.superclass.constructor.call(this, config);
10180
10181     this.value = config && config.value ?
10182                  config.value.clearTime() : new Date().clearTime();
10183
10184     this.addEvents({
10185         /**
10186              * @event select
10187              * Fires when a date is selected
10188              * @param {DatePicker} this
10189              * @param {Date} date The selected date
10190              */
10191         'select': true,
10192         /**
10193              * @event monthchange
10194              * Fires when the displayed month changes 
10195              * @param {DatePicker} this
10196              * @param {Date} date The selected month
10197              */
10198         'monthchange': true
10199     });
10200
10201     if(this.handler){
10202         this.on("select", this.handler,  this.scope || this);
10203     }
10204     // build the disabledDatesRE
10205     if(!this.disabledDatesRE && this.disabledDates){
10206         var dd = this.disabledDates;
10207         var re = "(?:";
10208         for(var i = 0; i < dd.length; i++){
10209             re += dd[i];
10210             if(i != dd.length-1) re += "|";
10211         }
10212         this.disabledDatesRE = new RegExp(re + ")");
10213     }
10214 };
10215
10216 Roo.extend(Roo.DatePicker, Roo.Component, {
10217     /**
10218      * @cfg {String} todayText
10219      * The text to display on the button that selects the current date (defaults to "Today")
10220      */
10221     todayText : "Today",
10222     /**
10223      * @cfg {String} okText
10224      * The text to display on the ok button
10225      */
10226     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10227     /**
10228      * @cfg {String} cancelText
10229      * The text to display on the cancel button
10230      */
10231     cancelText : "Cancel",
10232     /**
10233      * @cfg {String} todayTip
10234      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10235      */
10236     todayTip : "{0} (Spacebar)",
10237     /**
10238      * @cfg {Date} minDate
10239      * Minimum allowable date (JavaScript date object, defaults to null)
10240      */
10241     minDate : null,
10242     /**
10243      * @cfg {Date} maxDate
10244      * Maximum allowable date (JavaScript date object, defaults to null)
10245      */
10246     maxDate : null,
10247     /**
10248      * @cfg {String} minText
10249      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10250      */
10251     minText : "This date is before the minimum date",
10252     /**
10253      * @cfg {String} maxText
10254      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10255      */
10256     maxText : "This date is after the maximum date",
10257     /**
10258      * @cfg {String} format
10259      * The default date format string which can be overriden for localization support.  The format must be
10260      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10261      */
10262     format : "m/d/y",
10263     /**
10264      * @cfg {Array} disabledDays
10265      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10266      */
10267     disabledDays : null,
10268     /**
10269      * @cfg {String} disabledDaysText
10270      * The tooltip to display when the date falls on a disabled day (defaults to "")
10271      */
10272     disabledDaysText : "",
10273     /**
10274      * @cfg {RegExp} disabledDatesRE
10275      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10276      */
10277     disabledDatesRE : null,
10278     /**
10279      * @cfg {String} disabledDatesText
10280      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10281      */
10282     disabledDatesText : "",
10283     /**
10284      * @cfg {Boolean} constrainToViewport
10285      * True to constrain the date picker to the viewport (defaults to true)
10286      */
10287     constrainToViewport : true,
10288     /**
10289      * @cfg {Array} monthNames
10290      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10291      */
10292     monthNames : Date.monthNames,
10293     /**
10294      * @cfg {Array} dayNames
10295      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10296      */
10297     dayNames : Date.dayNames,
10298     /**
10299      * @cfg {String} nextText
10300      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10301      */
10302     nextText: 'Next Month (Control+Right)',
10303     /**
10304      * @cfg {String} prevText
10305      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10306      */
10307     prevText: 'Previous Month (Control+Left)',
10308     /**
10309      * @cfg {String} monthYearText
10310      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10311      */
10312     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10313     /**
10314      * @cfg {Number} startDay
10315      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10316      */
10317     startDay : 0,
10318     /**
10319      * @cfg {Bool} showClear
10320      * Show a clear button (usefull for date form elements that can be blank.)
10321      */
10322     
10323     showClear: false,
10324     
10325     /**
10326      * Sets the value of the date field
10327      * @param {Date} value The date to set
10328      */
10329     setValue : function(value){
10330         var old = this.value;
10331         this.value = value.clearTime(true);
10332         if(this.el){
10333             this.update(this.value);
10334         }
10335     },
10336
10337     /**
10338      * Gets the current selected value of the date field
10339      * @return {Date} The selected date
10340      */
10341     getValue : function(){
10342         return this.value;
10343     },
10344
10345     // private
10346     focus : function(){
10347         if(this.el){
10348             this.update(this.activeDate);
10349         }
10350     },
10351
10352     // private
10353     onRender : function(container, position){
10354         var m = [
10355              '<table cellspacing="0">',
10356                 '<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>',
10357                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10358         var dn = this.dayNames;
10359         for(var i = 0; i < 7; i++){
10360             var d = this.startDay+i;
10361             if(d > 6){
10362                 d = d-7;
10363             }
10364             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10365         }
10366         m[m.length] = "</tr></thead><tbody><tr>";
10367         for(var i = 0; i < 42; i++) {
10368             if(i % 7 == 0 && i != 0){
10369                 m[m.length] = "</tr><tr>";
10370             }
10371             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10372         }
10373         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10374             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10375
10376         var el = document.createElement("div");
10377         el.className = "x-date-picker";
10378         el.innerHTML = m.join("");
10379
10380         container.dom.insertBefore(el, position);
10381
10382         this.el = Roo.get(el);
10383         this.eventEl = Roo.get(el.firstChild);
10384
10385         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10386             handler: this.showPrevMonth,
10387             scope: this,
10388             preventDefault:true,
10389             stopDefault:true
10390         });
10391
10392         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10393             handler: this.showNextMonth,
10394             scope: this,
10395             preventDefault:true,
10396             stopDefault:true
10397         });
10398
10399         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10400
10401         this.monthPicker = this.el.down('div.x-date-mp');
10402         this.monthPicker.enableDisplayMode('block');
10403         
10404         var kn = new Roo.KeyNav(this.eventEl, {
10405             "left" : function(e){
10406                 e.ctrlKey ?
10407                     this.showPrevMonth() :
10408                     this.update(this.activeDate.add("d", -1));
10409             },
10410
10411             "right" : function(e){
10412                 e.ctrlKey ?
10413                     this.showNextMonth() :
10414                     this.update(this.activeDate.add("d", 1));
10415             },
10416
10417             "up" : function(e){
10418                 e.ctrlKey ?
10419                     this.showNextYear() :
10420                     this.update(this.activeDate.add("d", -7));
10421             },
10422
10423             "down" : function(e){
10424                 e.ctrlKey ?
10425                     this.showPrevYear() :
10426                     this.update(this.activeDate.add("d", 7));
10427             },
10428
10429             "pageUp" : function(e){
10430                 this.showNextMonth();
10431             },
10432
10433             "pageDown" : function(e){
10434                 this.showPrevMonth();
10435             },
10436
10437             "enter" : function(e){
10438                 e.stopPropagation();
10439                 return true;
10440             },
10441
10442             scope : this
10443         });
10444
10445         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10446
10447         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10448
10449         this.el.unselectable();
10450         
10451         this.cells = this.el.select("table.x-date-inner tbody td");
10452         this.textNodes = this.el.query("table.x-date-inner tbody span");
10453
10454         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10455             text: "&#160;",
10456             tooltip: this.monthYearText
10457         });
10458
10459         this.mbtn.on('click', this.showMonthPicker, this);
10460         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10461
10462
10463         var today = (new Date()).dateFormat(this.format);
10464         
10465         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10466         if (this.showClear) {
10467             baseTb.add( new Roo.Toolbar.Fill());
10468         }
10469         baseTb.add({
10470             text: String.format(this.todayText, today),
10471             tooltip: String.format(this.todayTip, today),
10472             handler: this.selectToday,
10473             scope: this
10474         });
10475         
10476         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10477             
10478         //});
10479         if (this.showClear) {
10480             
10481             baseTb.add( new Roo.Toolbar.Fill());
10482             baseTb.add({
10483                 text: '&#160;',
10484                 cls: 'x-btn-icon x-btn-clear',
10485                 handler: function() {
10486                     //this.value = '';
10487                     this.fireEvent("select", this, '');
10488                 },
10489                 scope: this
10490             });
10491         }
10492         
10493         
10494         if(Roo.isIE){
10495             this.el.repaint();
10496         }
10497         this.update(this.value);
10498     },
10499
10500     createMonthPicker : function(){
10501         if(!this.monthPicker.dom.firstChild){
10502             var buf = ['<table border="0" cellspacing="0">'];
10503             for(var i = 0; i < 6; i++){
10504                 buf.push(
10505                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10506                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10507                     i == 0 ?
10508                     '<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>' :
10509                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10510                 );
10511             }
10512             buf.push(
10513                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10514                     this.okText,
10515                     '</button><button type="button" class="x-date-mp-cancel">',
10516                     this.cancelText,
10517                     '</button></td></tr>',
10518                 '</table>'
10519             );
10520             this.monthPicker.update(buf.join(''));
10521             this.monthPicker.on('click', this.onMonthClick, this);
10522             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10523
10524             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10525             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10526
10527             this.mpMonths.each(function(m, a, i){
10528                 i += 1;
10529                 if((i%2) == 0){
10530                     m.dom.xmonth = 5 + Math.round(i * .5);
10531                 }else{
10532                     m.dom.xmonth = Math.round((i-1) * .5);
10533                 }
10534             });
10535         }
10536     },
10537
10538     showMonthPicker : function(){
10539         this.createMonthPicker();
10540         var size = this.el.getSize();
10541         this.monthPicker.setSize(size);
10542         this.monthPicker.child('table').setSize(size);
10543
10544         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10545         this.updateMPMonth(this.mpSelMonth);
10546         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10547         this.updateMPYear(this.mpSelYear);
10548
10549         this.monthPicker.slideIn('t', {duration:.2});
10550     },
10551
10552     updateMPYear : function(y){
10553         this.mpyear = y;
10554         var ys = this.mpYears.elements;
10555         for(var i = 1; i <= 10; i++){
10556             var td = ys[i-1], y2;
10557             if((i%2) == 0){
10558                 y2 = y + Math.round(i * .5);
10559                 td.firstChild.innerHTML = y2;
10560                 td.xyear = y2;
10561             }else{
10562                 y2 = y - (5-Math.round(i * .5));
10563                 td.firstChild.innerHTML = y2;
10564                 td.xyear = y2;
10565             }
10566             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10567         }
10568     },
10569
10570     updateMPMonth : function(sm){
10571         this.mpMonths.each(function(m, a, i){
10572             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10573         });
10574     },
10575
10576     selectMPMonth: function(m){
10577         
10578     },
10579
10580     onMonthClick : function(e, t){
10581         e.stopEvent();
10582         var el = new Roo.Element(t), pn;
10583         if(el.is('button.x-date-mp-cancel')){
10584             this.hideMonthPicker();
10585         }
10586         else if(el.is('button.x-date-mp-ok')){
10587             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10588             this.hideMonthPicker();
10589         }
10590         else if(pn = el.up('td.x-date-mp-month', 2)){
10591             this.mpMonths.removeClass('x-date-mp-sel');
10592             pn.addClass('x-date-mp-sel');
10593             this.mpSelMonth = pn.dom.xmonth;
10594         }
10595         else if(pn = el.up('td.x-date-mp-year', 2)){
10596             this.mpYears.removeClass('x-date-mp-sel');
10597             pn.addClass('x-date-mp-sel');
10598             this.mpSelYear = pn.dom.xyear;
10599         }
10600         else if(el.is('a.x-date-mp-prev')){
10601             this.updateMPYear(this.mpyear-10);
10602         }
10603         else if(el.is('a.x-date-mp-next')){
10604             this.updateMPYear(this.mpyear+10);
10605         }
10606     },
10607
10608     onMonthDblClick : function(e, t){
10609         e.stopEvent();
10610         var el = new Roo.Element(t), pn;
10611         if(pn = el.up('td.x-date-mp-month', 2)){
10612             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10613             this.hideMonthPicker();
10614         }
10615         else if(pn = el.up('td.x-date-mp-year', 2)){
10616             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10617             this.hideMonthPicker();
10618         }
10619     },
10620
10621     hideMonthPicker : function(disableAnim){
10622         if(this.monthPicker){
10623             if(disableAnim === true){
10624                 this.monthPicker.hide();
10625             }else{
10626                 this.monthPicker.slideOut('t', {duration:.2});
10627             }
10628         }
10629     },
10630
10631     // private
10632     showPrevMonth : function(e){
10633         this.update(this.activeDate.add("mo", -1));
10634     },
10635
10636     // private
10637     showNextMonth : function(e){
10638         this.update(this.activeDate.add("mo", 1));
10639     },
10640
10641     // private
10642     showPrevYear : function(){
10643         this.update(this.activeDate.add("y", -1));
10644     },
10645
10646     // private
10647     showNextYear : function(){
10648         this.update(this.activeDate.add("y", 1));
10649     },
10650
10651     // private
10652     handleMouseWheel : function(e){
10653         var delta = e.getWheelDelta();
10654         if(delta > 0){
10655             this.showPrevMonth();
10656             e.stopEvent();
10657         } else if(delta < 0){
10658             this.showNextMonth();
10659             e.stopEvent();
10660         }
10661     },
10662
10663     // private
10664     handleDateClick : function(e, t){
10665         e.stopEvent();
10666         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10667             this.setValue(new Date(t.dateValue));
10668             this.fireEvent("select", this, this.value);
10669         }
10670     },
10671
10672     // private
10673     selectToday : function(){
10674         this.setValue(new Date().clearTime());
10675         this.fireEvent("select", this, this.value);
10676     },
10677
10678     // private
10679     update : function(date)
10680     {
10681         var vd = this.activeDate;
10682         this.activeDate = date;
10683         if(vd && this.el){
10684             var t = date.getTime();
10685             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10686                 this.cells.removeClass("x-date-selected");
10687                 this.cells.each(function(c){
10688                    if(c.dom.firstChild.dateValue == t){
10689                        c.addClass("x-date-selected");
10690                        setTimeout(function(){
10691                             try{c.dom.firstChild.focus();}catch(e){}
10692                        }, 50);
10693                        return false;
10694                    }
10695                 });
10696                 return;
10697             }
10698         }
10699         
10700         var days = date.getDaysInMonth();
10701         var firstOfMonth = date.getFirstDateOfMonth();
10702         var startingPos = firstOfMonth.getDay()-this.startDay;
10703
10704         if(startingPos <= this.startDay){
10705             startingPos += 7;
10706         }
10707
10708         var pm = date.add("mo", -1);
10709         var prevStart = pm.getDaysInMonth()-startingPos;
10710
10711         var cells = this.cells.elements;
10712         var textEls = this.textNodes;
10713         days += startingPos;
10714
10715         // convert everything to numbers so it's fast
10716         var day = 86400000;
10717         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10718         var today = new Date().clearTime().getTime();
10719         var sel = date.clearTime().getTime();
10720         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10721         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10722         var ddMatch = this.disabledDatesRE;
10723         var ddText = this.disabledDatesText;
10724         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10725         var ddaysText = this.disabledDaysText;
10726         var format = this.format;
10727
10728         var setCellClass = function(cal, cell){
10729             cell.title = "";
10730             var t = d.getTime();
10731             cell.firstChild.dateValue = t;
10732             if(t == today){
10733                 cell.className += " x-date-today";
10734                 cell.title = cal.todayText;
10735             }
10736             if(t == sel){
10737                 cell.className += " x-date-selected";
10738                 setTimeout(function(){
10739                     try{cell.firstChild.focus();}catch(e){}
10740                 }, 50);
10741             }
10742             // disabling
10743             if(t < min) {
10744                 cell.className = " x-date-disabled";
10745                 cell.title = cal.minText;
10746                 return;
10747             }
10748             if(t > max) {
10749                 cell.className = " x-date-disabled";
10750                 cell.title = cal.maxText;
10751                 return;
10752             }
10753             if(ddays){
10754                 if(ddays.indexOf(d.getDay()) != -1){
10755                     cell.title = ddaysText;
10756                     cell.className = " x-date-disabled";
10757                 }
10758             }
10759             if(ddMatch && format){
10760                 var fvalue = d.dateFormat(format);
10761                 if(ddMatch.test(fvalue)){
10762                     cell.title = ddText.replace("%0", fvalue);
10763                     cell.className = " x-date-disabled";
10764                 }
10765             }
10766         };
10767
10768         var i = 0;
10769         for(; i < startingPos; i++) {
10770             textEls[i].innerHTML = (++prevStart);
10771             d.setDate(d.getDate()+1);
10772             cells[i].className = "x-date-prevday";
10773             setCellClass(this, cells[i]);
10774         }
10775         for(; i < days; i++){
10776             intDay = i - startingPos + 1;
10777             textEls[i].innerHTML = (intDay);
10778             d.setDate(d.getDate()+1);
10779             cells[i].className = "x-date-active";
10780             setCellClass(this, cells[i]);
10781         }
10782         var extraDays = 0;
10783         for(; i < 42; i++) {
10784              textEls[i].innerHTML = (++extraDays);
10785              d.setDate(d.getDate()+1);
10786              cells[i].className = "x-date-nextday";
10787              setCellClass(this, cells[i]);
10788         }
10789
10790         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10791         this.fireEvent('monthchange', this, date);
10792         
10793         if(!this.internalRender){
10794             var main = this.el.dom.firstChild;
10795             var w = main.offsetWidth;
10796             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10797             Roo.fly(main).setWidth(w);
10798             this.internalRender = true;
10799             // opera does not respect the auto grow header center column
10800             // then, after it gets a width opera refuses to recalculate
10801             // without a second pass
10802             if(Roo.isOpera && !this.secondPass){
10803                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10804                 this.secondPass = true;
10805                 this.update.defer(10, this, [date]);
10806             }
10807         }
10808         
10809         
10810     }
10811 });        /*
10812  * Based on:
10813  * Ext JS Library 1.1.1
10814  * Copyright(c) 2006-2007, Ext JS, LLC.
10815  *
10816  * Originally Released Under LGPL - original licence link has changed is not relivant.
10817  *
10818  * Fork - LGPL
10819  * <script type="text/javascript">
10820  */
10821 /**
10822  * @class Roo.TabPanel
10823  * @extends Roo.util.Observable
10824  * A lightweight tab container.
10825  * <br><br>
10826  * Usage:
10827  * <pre><code>
10828 // basic tabs 1, built from existing content
10829 var tabs = new Roo.TabPanel("tabs1");
10830 tabs.addTab("script", "View Script");
10831 tabs.addTab("markup", "View Markup");
10832 tabs.activate("script");
10833
10834 // more advanced tabs, built from javascript
10835 var jtabs = new Roo.TabPanel("jtabs");
10836 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10837
10838 // set up the UpdateManager
10839 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10840 var updater = tab2.getUpdateManager();
10841 updater.setDefaultUrl("ajax1.htm");
10842 tab2.on('activate', updater.refresh, updater, true);
10843
10844 // Use setUrl for Ajax loading
10845 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10846 tab3.setUrl("ajax2.htm", null, true);
10847
10848 // Disabled tab
10849 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10850 tab4.disable();
10851
10852 jtabs.activate("jtabs-1");
10853  * </code></pre>
10854  * @constructor
10855  * Create a new TabPanel.
10856  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10857  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10858  */
10859 Roo.TabPanel = function(container, config){
10860     /**
10861     * The container element for this TabPanel.
10862     * @type Roo.Element
10863     */
10864     this.el = Roo.get(container, true);
10865     if(config){
10866         if(typeof config == "boolean"){
10867             this.tabPosition = config ? "bottom" : "top";
10868         }else{
10869             Roo.apply(this, config);
10870         }
10871     }
10872     if(this.tabPosition == "bottom"){
10873         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10874         this.el.addClass("x-tabs-bottom");
10875     }
10876     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10877     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10878     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10879     if(Roo.isIE){
10880         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10881     }
10882     if(this.tabPosition != "bottom"){
10883         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10884          * @type Roo.Element
10885          */
10886         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10887         this.el.addClass("x-tabs-top");
10888     }
10889     this.items = [];
10890
10891     this.bodyEl.setStyle("position", "relative");
10892
10893     this.active = null;
10894     this.activateDelegate = this.activate.createDelegate(this);
10895
10896     this.addEvents({
10897         /**
10898          * @event tabchange
10899          * Fires when the active tab changes
10900          * @param {Roo.TabPanel} this
10901          * @param {Roo.TabPanelItem} activePanel The new active tab
10902          */
10903         "tabchange": true,
10904         /**
10905          * @event beforetabchange
10906          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10907          * @param {Roo.TabPanel} this
10908          * @param {Object} e Set cancel to true on this object to cancel the tab change
10909          * @param {Roo.TabPanelItem} tab The tab being changed to
10910          */
10911         "beforetabchange" : true
10912     });
10913
10914     Roo.EventManager.onWindowResize(this.onResize, this);
10915     this.cpad = this.el.getPadding("lr");
10916     this.hiddenCount = 0;
10917
10918
10919     // toolbar on the tabbar support...
10920     if (this.toolbar) {
10921         var tcfg = this.toolbar;
10922         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10923         this.toolbar = new Roo.Toolbar(tcfg);
10924         if (Roo.isSafari) {
10925             var tbl = tcfg.container.child('table', true);
10926             tbl.setAttribute('width', '100%');
10927         }
10928         
10929     }
10930    
10931
10932
10933     Roo.TabPanel.superclass.constructor.call(this);
10934 };
10935
10936 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10937     /*
10938      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10939      */
10940     tabPosition : "top",
10941     /*
10942      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10943      */
10944     currentTabWidth : 0,
10945     /*
10946      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10947      */
10948     minTabWidth : 40,
10949     /*
10950      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10951      */
10952     maxTabWidth : 250,
10953     /*
10954      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10955      */
10956     preferredTabWidth : 175,
10957     /*
10958      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10959      */
10960     resizeTabs : false,
10961     /*
10962      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10963      */
10964     monitorResize : true,
10965     /*
10966      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10967      */
10968     toolbar : false,
10969
10970     /**
10971      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10972      * @param {String} id The id of the div to use <b>or create</b>
10973      * @param {String} text The text for the tab
10974      * @param {String} content (optional) Content to put in the TabPanelItem body
10975      * @param {Boolean} closable (optional) True to create a close icon on the tab
10976      * @return {Roo.TabPanelItem} The created TabPanelItem
10977      */
10978     addTab : function(id, text, content, closable){
10979         var item = new Roo.TabPanelItem(this, id, text, closable);
10980         this.addTabItem(item);
10981         if(content){
10982             item.setContent(content);
10983         }
10984         return item;
10985     },
10986
10987     /**
10988      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10989      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10990      * @return {Roo.TabPanelItem}
10991      */
10992     getTab : function(id){
10993         return this.items[id];
10994     },
10995
10996     /**
10997      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10998      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10999      */
11000     hideTab : function(id){
11001         var t = this.items[id];
11002         if(!t.isHidden()){
11003            t.setHidden(true);
11004            this.hiddenCount++;
11005            this.autoSizeTabs();
11006         }
11007     },
11008
11009     /**
11010      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11011      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11012      */
11013     unhideTab : function(id){
11014         var t = this.items[id];
11015         if(t.isHidden()){
11016            t.setHidden(false);
11017            this.hiddenCount--;
11018            this.autoSizeTabs();
11019         }
11020     },
11021
11022     /**
11023      * Adds an existing {@link Roo.TabPanelItem}.
11024      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11025      */
11026     addTabItem : function(item){
11027         this.items[item.id] = item;
11028         this.items.push(item);
11029         if(this.resizeTabs){
11030            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11031            this.autoSizeTabs();
11032         }else{
11033             item.autoSize();
11034         }
11035     },
11036
11037     /**
11038      * Removes a {@link Roo.TabPanelItem}.
11039      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11040      */
11041     removeTab : function(id){
11042         var items = this.items;
11043         var tab = items[id];
11044         if(!tab) { return; }
11045         var index = items.indexOf(tab);
11046         if(this.active == tab && items.length > 1){
11047             var newTab = this.getNextAvailable(index);
11048             if(newTab) {
11049                 newTab.activate();
11050             }
11051         }
11052         this.stripEl.dom.removeChild(tab.pnode.dom);
11053         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11054             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11055         }
11056         items.splice(index, 1);
11057         delete this.items[tab.id];
11058         tab.fireEvent("close", tab);
11059         tab.purgeListeners();
11060         this.autoSizeTabs();
11061     },
11062
11063     getNextAvailable : function(start){
11064         var items = this.items;
11065         var index = start;
11066         // look for a next tab that will slide over to
11067         // replace the one being removed
11068         while(index < items.length){
11069             var item = items[++index];
11070             if(item && !item.isHidden()){
11071                 return item;
11072             }
11073         }
11074         // if one isn't found select the previous tab (on the left)
11075         index = start;
11076         while(index >= 0){
11077             var item = items[--index];
11078             if(item && !item.isHidden()){
11079                 return item;
11080             }
11081         }
11082         return null;
11083     },
11084
11085     /**
11086      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11087      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11088      */
11089     disableTab : function(id){
11090         var tab = this.items[id];
11091         if(tab && this.active != tab){
11092             tab.disable();
11093         }
11094     },
11095
11096     /**
11097      * Enables a {@link Roo.TabPanelItem} that is disabled.
11098      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11099      */
11100     enableTab : function(id){
11101         var tab = this.items[id];
11102         tab.enable();
11103     },
11104
11105     /**
11106      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11107      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11108      * @return {Roo.TabPanelItem} The TabPanelItem.
11109      */
11110     activate : function(id){
11111         var tab = this.items[id];
11112         if(!tab){
11113             return null;
11114         }
11115         if(tab == this.active || tab.disabled){
11116             return tab;
11117         }
11118         var e = {};
11119         this.fireEvent("beforetabchange", this, e, tab);
11120         if(e.cancel !== true && !tab.disabled){
11121             if(this.active){
11122                 this.active.hide();
11123             }
11124             this.active = this.items[id];
11125             this.active.show();
11126             this.fireEvent("tabchange", this, this.active);
11127         }
11128         return tab;
11129     },
11130
11131     /**
11132      * Gets the active {@link Roo.TabPanelItem}.
11133      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11134      */
11135     getActiveTab : function(){
11136         return this.active;
11137     },
11138
11139     /**
11140      * Updates the tab body element to fit the height of the container element
11141      * for overflow scrolling
11142      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11143      */
11144     syncHeight : function(targetHeight){
11145         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11146         var bm = this.bodyEl.getMargins();
11147         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11148         this.bodyEl.setHeight(newHeight);
11149         return newHeight;
11150     },
11151
11152     onResize : function(){
11153         if(this.monitorResize){
11154             this.autoSizeTabs();
11155         }
11156     },
11157
11158     /**
11159      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11160      */
11161     beginUpdate : function(){
11162         this.updating = true;
11163     },
11164
11165     /**
11166      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11167      */
11168     endUpdate : function(){
11169         this.updating = false;
11170         this.autoSizeTabs();
11171     },
11172
11173     /**
11174      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11175      */
11176     autoSizeTabs : function(){
11177         var count = this.items.length;
11178         var vcount = count - this.hiddenCount;
11179         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11180         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11181         var availWidth = Math.floor(w / vcount);
11182         var b = this.stripBody;
11183         if(b.getWidth() > w){
11184             var tabs = this.items;
11185             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11186             if(availWidth < this.minTabWidth){
11187                 /*if(!this.sleft){    // incomplete scrolling code
11188                     this.createScrollButtons();
11189                 }
11190                 this.showScroll();
11191                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11192             }
11193         }else{
11194             if(this.currentTabWidth < this.preferredTabWidth){
11195                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11196             }
11197         }
11198     },
11199
11200     /**
11201      * Returns the number of tabs in this TabPanel.
11202      * @return {Number}
11203      */
11204      getCount : function(){
11205          return this.items.length;
11206      },
11207
11208     /**
11209      * Resizes all the tabs to the passed width
11210      * @param {Number} The new width
11211      */
11212     setTabWidth : function(width){
11213         this.currentTabWidth = width;
11214         for(var i = 0, len = this.items.length; i < len; i++) {
11215                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11216         }
11217     },
11218
11219     /**
11220      * Destroys this TabPanel
11221      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11222      */
11223     destroy : function(removeEl){
11224         Roo.EventManager.removeResizeListener(this.onResize, this);
11225         for(var i = 0, len = this.items.length; i < len; i++){
11226             this.items[i].purgeListeners();
11227         }
11228         if(removeEl === true){
11229             this.el.update("");
11230             this.el.remove();
11231         }
11232     }
11233 });
11234
11235 /**
11236  * @class Roo.TabPanelItem
11237  * @extends Roo.util.Observable
11238  * Represents an individual item (tab plus body) in a TabPanel.
11239  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11240  * @param {String} id The id of this TabPanelItem
11241  * @param {String} text The text for the tab of this TabPanelItem
11242  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11243  */
11244 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11245     /**
11246      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11247      * @type Roo.TabPanel
11248      */
11249     this.tabPanel = tabPanel;
11250     /**
11251      * The id for this TabPanelItem
11252      * @type String
11253      */
11254     this.id = id;
11255     /** @private */
11256     this.disabled = false;
11257     /** @private */
11258     this.text = text;
11259     /** @private */
11260     this.loaded = false;
11261     this.closable = closable;
11262
11263     /**
11264      * The body element for this TabPanelItem.
11265      * @type Roo.Element
11266      */
11267     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11268     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11269     this.bodyEl.setStyle("display", "block");
11270     this.bodyEl.setStyle("zoom", "1");
11271     this.hideAction();
11272
11273     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11274     /** @private */
11275     this.el = Roo.get(els.el, true);
11276     this.inner = Roo.get(els.inner, true);
11277     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11278     this.pnode = Roo.get(els.el.parentNode, true);
11279     this.el.on("mousedown", this.onTabMouseDown, this);
11280     this.el.on("click", this.onTabClick, this);
11281     /** @private */
11282     if(closable){
11283         var c = Roo.get(els.close, true);
11284         c.dom.title = this.closeText;
11285         c.addClassOnOver("close-over");
11286         c.on("click", this.closeClick, this);
11287      }
11288
11289     this.addEvents({
11290          /**
11291          * @event activate
11292          * Fires when this tab becomes the active tab.
11293          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11294          * @param {Roo.TabPanelItem} this
11295          */
11296         "activate": true,
11297         /**
11298          * @event beforeclose
11299          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11300          * @param {Roo.TabPanelItem} this
11301          * @param {Object} e Set cancel to true on this object to cancel the close.
11302          */
11303         "beforeclose": true,
11304         /**
11305          * @event close
11306          * Fires when this tab is closed.
11307          * @param {Roo.TabPanelItem} this
11308          */
11309          "close": true,
11310         /**
11311          * @event deactivate
11312          * Fires when this tab is no longer the active tab.
11313          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11314          * @param {Roo.TabPanelItem} this
11315          */
11316          "deactivate" : true
11317     });
11318     this.hidden = false;
11319
11320     Roo.TabPanelItem.superclass.constructor.call(this);
11321 };
11322
11323 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11324     purgeListeners : function(){
11325        Roo.util.Observable.prototype.purgeListeners.call(this);
11326        this.el.removeAllListeners();
11327     },
11328     /**
11329      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11330      */
11331     show : function(){
11332         this.pnode.addClass("on");
11333         this.showAction();
11334         if(Roo.isOpera){
11335             this.tabPanel.stripWrap.repaint();
11336         }
11337         this.fireEvent("activate", this.tabPanel, this);
11338     },
11339
11340     /**
11341      * Returns true if this tab is the active tab.
11342      * @return {Boolean}
11343      */
11344     isActive : function(){
11345         return this.tabPanel.getActiveTab() == this;
11346     },
11347
11348     /**
11349      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11350      */
11351     hide : function(){
11352         this.pnode.removeClass("on");
11353         this.hideAction();
11354         this.fireEvent("deactivate", this.tabPanel, this);
11355     },
11356
11357     hideAction : function(){
11358         this.bodyEl.hide();
11359         this.bodyEl.setStyle("position", "absolute");
11360         this.bodyEl.setLeft("-20000px");
11361         this.bodyEl.setTop("-20000px");
11362     },
11363
11364     showAction : function(){
11365         this.bodyEl.setStyle("position", "relative");
11366         this.bodyEl.setTop("");
11367         this.bodyEl.setLeft("");
11368         this.bodyEl.show();
11369     },
11370
11371     /**
11372      * Set the tooltip for the tab.
11373      * @param {String} tooltip The tab's tooltip
11374      */
11375     setTooltip : function(text){
11376         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11377             this.textEl.dom.qtip = text;
11378             this.textEl.dom.removeAttribute('title');
11379         }else{
11380             this.textEl.dom.title = text;
11381         }
11382     },
11383
11384     onTabClick : function(e){
11385         e.preventDefault();
11386         this.tabPanel.activate(this.id);
11387     },
11388
11389     onTabMouseDown : function(e){
11390         e.preventDefault();
11391         this.tabPanel.activate(this.id);
11392     },
11393
11394     getWidth : function(){
11395         return this.inner.getWidth();
11396     },
11397
11398     setWidth : function(width){
11399         var iwidth = width - this.pnode.getPadding("lr");
11400         this.inner.setWidth(iwidth);
11401         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11402         this.pnode.setWidth(width);
11403     },
11404
11405     /**
11406      * Show or hide the tab
11407      * @param {Boolean} hidden True to hide or false to show.
11408      */
11409     setHidden : function(hidden){
11410         this.hidden = hidden;
11411         this.pnode.setStyle("display", hidden ? "none" : "");
11412     },
11413
11414     /**
11415      * Returns true if this tab is "hidden"
11416      * @return {Boolean}
11417      */
11418     isHidden : function(){
11419         return this.hidden;
11420     },
11421
11422     /**
11423      * Returns the text for this tab
11424      * @return {String}
11425      */
11426     getText : function(){
11427         return this.text;
11428     },
11429
11430     autoSize : function(){
11431         //this.el.beginMeasure();
11432         this.textEl.setWidth(1);
11433         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11434         //this.el.endMeasure();
11435     },
11436
11437     /**
11438      * Sets the text for the tab (Note: this also sets the tooltip text)
11439      * @param {String} text The tab's text and tooltip
11440      */
11441     setText : function(text){
11442         this.text = text;
11443         this.textEl.update(text);
11444         this.setTooltip(text);
11445         if(!this.tabPanel.resizeTabs){
11446             this.autoSize();
11447         }
11448     },
11449     /**
11450      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11451      */
11452     activate : function(){
11453         this.tabPanel.activate(this.id);
11454     },
11455
11456     /**
11457      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11458      */
11459     disable : function(){
11460         if(this.tabPanel.active != this){
11461             this.disabled = true;
11462             this.pnode.addClass("disabled");
11463         }
11464     },
11465
11466     /**
11467      * Enables this TabPanelItem if it was previously disabled.
11468      */
11469     enable : function(){
11470         this.disabled = false;
11471         this.pnode.removeClass("disabled");
11472     },
11473
11474     /**
11475      * Sets the content for this TabPanelItem.
11476      * @param {String} content The content
11477      * @param {Boolean} loadScripts true to look for and load scripts
11478      */
11479     setContent : function(content, loadScripts){
11480         this.bodyEl.update(content, loadScripts);
11481     },
11482
11483     /**
11484      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11485      * @return {Roo.UpdateManager} The UpdateManager
11486      */
11487     getUpdateManager : function(){
11488         return this.bodyEl.getUpdateManager();
11489     },
11490
11491     /**
11492      * Set a URL to be used to load the content for this TabPanelItem.
11493      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11494      * @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)
11495      * @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)
11496      * @return {Roo.UpdateManager} The UpdateManager
11497      */
11498     setUrl : function(url, params, loadOnce){
11499         if(this.refreshDelegate){
11500             this.un('activate', this.refreshDelegate);
11501         }
11502         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11503         this.on("activate", this.refreshDelegate);
11504         return this.bodyEl.getUpdateManager();
11505     },
11506
11507     /** @private */
11508     _handleRefresh : function(url, params, loadOnce){
11509         if(!loadOnce || !this.loaded){
11510             var updater = this.bodyEl.getUpdateManager();
11511             updater.update(url, params, this._setLoaded.createDelegate(this));
11512         }
11513     },
11514
11515     /**
11516      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11517      *   Will fail silently if the setUrl method has not been called.
11518      *   This does not activate the panel, just updates its content.
11519      */
11520     refresh : function(){
11521         if(this.refreshDelegate){
11522            this.loaded = false;
11523            this.refreshDelegate();
11524         }
11525     },
11526
11527     /** @private */
11528     _setLoaded : function(){
11529         this.loaded = true;
11530     },
11531
11532     /** @private */
11533     closeClick : function(e){
11534         var o = {};
11535         e.stopEvent();
11536         this.fireEvent("beforeclose", this, o);
11537         if(o.cancel !== true){
11538             this.tabPanel.removeTab(this.id);
11539         }
11540     },
11541     /**
11542      * The text displayed in the tooltip for the close icon.
11543      * @type String
11544      */
11545     closeText : "Close this tab"
11546 });
11547
11548 /** @private */
11549 Roo.TabPanel.prototype.createStrip = function(container){
11550     var strip = document.createElement("div");
11551     strip.className = "x-tabs-wrap";
11552     container.appendChild(strip);
11553     return strip;
11554 };
11555 /** @private */
11556 Roo.TabPanel.prototype.createStripList = function(strip){
11557     // div wrapper for retard IE
11558     // returns the "tr" element.
11559     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11560         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11561         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11562     return strip.firstChild.firstChild.firstChild.firstChild;
11563 };
11564 /** @private */
11565 Roo.TabPanel.prototype.createBody = function(container){
11566     var body = document.createElement("div");
11567     Roo.id(body, "tab-body");
11568     Roo.fly(body).addClass("x-tabs-body");
11569     container.appendChild(body);
11570     return body;
11571 };
11572 /** @private */
11573 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11574     var body = Roo.getDom(id);
11575     if(!body){
11576         body = document.createElement("div");
11577         body.id = id;
11578     }
11579     Roo.fly(body).addClass("x-tabs-item-body");
11580     bodyEl.insertBefore(body, bodyEl.firstChild);
11581     return body;
11582 };
11583 /** @private */
11584 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11585     var td = document.createElement("td");
11586     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11587     //stripEl.appendChild(td);
11588     if(closable){
11589         td.className = "x-tabs-closable";
11590         if(!this.closeTpl){
11591             this.closeTpl = new Roo.Template(
11592                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11593                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11594                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11595             );
11596         }
11597         var el = this.closeTpl.overwrite(td, {"text": text});
11598         var close = el.getElementsByTagName("div")[0];
11599         var inner = el.getElementsByTagName("em")[0];
11600         return {"el": el, "close": close, "inner": inner};
11601     } else {
11602         if(!this.tabTpl){
11603             this.tabTpl = new Roo.Template(
11604                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11605                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11606             );
11607         }
11608         var el = this.tabTpl.overwrite(td, {"text": text});
11609         var inner = el.getElementsByTagName("em")[0];
11610         return {"el": el, "inner": inner};
11611     }
11612 };/*
11613  * Based on:
11614  * Ext JS Library 1.1.1
11615  * Copyright(c) 2006-2007, Ext JS, LLC.
11616  *
11617  * Originally Released Under LGPL - original licence link has changed is not relivant.
11618  *
11619  * Fork - LGPL
11620  * <script type="text/javascript">
11621  */
11622
11623 /**
11624  * @class Roo.Button
11625  * @extends Roo.util.Observable
11626  * Simple Button class
11627  * @cfg {String} text The button text
11628  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11629  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11630  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11631  * @cfg {Object} scope The scope of the handler
11632  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11633  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11634  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11635  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11636  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11637  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11638    applies if enableToggle = true)
11639  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11640  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11641   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11642  * @constructor
11643  * Create a new button
11644  * @param {Object} config The config object
11645  */
11646 Roo.Button = function(renderTo, config)
11647 {
11648     if (!config) {
11649         config = renderTo;
11650         renderTo = config.renderTo || false;
11651     }
11652     
11653     Roo.apply(this, config);
11654     this.addEvents({
11655         /**
11656              * @event click
11657              * Fires when this button is clicked
11658              * @param {Button} this
11659              * @param {EventObject} e The click event
11660              */
11661             "click" : true,
11662         /**
11663              * @event toggle
11664              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11665              * @param {Button} this
11666              * @param {Boolean} pressed
11667              */
11668             "toggle" : true,
11669         /**
11670              * @event mouseover
11671              * Fires when the mouse hovers over the button
11672              * @param {Button} this
11673              * @param {Event} e The event object
11674              */
11675         'mouseover' : true,
11676         /**
11677              * @event mouseout
11678              * Fires when the mouse exits the button
11679              * @param {Button} this
11680              * @param {Event} e The event object
11681              */
11682         'mouseout': true,
11683          /**
11684              * @event render
11685              * Fires when the button is rendered
11686              * @param {Button} this
11687              */
11688         'render': true
11689     });
11690     if(this.menu){
11691         this.menu = Roo.menu.MenuMgr.get(this.menu);
11692     }
11693     // register listeners first!!  - so render can be captured..
11694     Roo.util.Observable.call(this);
11695     if(renderTo){
11696         this.render(renderTo);
11697     }
11698     
11699   
11700 };
11701
11702 Roo.extend(Roo.Button, Roo.util.Observable, {
11703     /**
11704      * 
11705      */
11706     
11707     /**
11708      * Read-only. True if this button is hidden
11709      * @type Boolean
11710      */
11711     hidden : false,
11712     /**
11713      * Read-only. True if this button is disabled
11714      * @type Boolean
11715      */
11716     disabled : false,
11717     /**
11718      * Read-only. True if this button is pressed (only if enableToggle = true)
11719      * @type Boolean
11720      */
11721     pressed : false,
11722
11723     /**
11724      * @cfg {Number} tabIndex 
11725      * The DOM tabIndex for this button (defaults to undefined)
11726      */
11727     tabIndex : undefined,
11728
11729     /**
11730      * @cfg {Boolean} enableToggle
11731      * True to enable pressed/not pressed toggling (defaults to false)
11732      */
11733     enableToggle: false,
11734     /**
11735      * @cfg {Mixed} menu
11736      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11737      */
11738     menu : undefined,
11739     /**
11740      * @cfg {String} menuAlign
11741      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11742      */
11743     menuAlign : "tl-bl?",
11744
11745     /**
11746      * @cfg {String} iconCls
11747      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11748      */
11749     iconCls : undefined,
11750     /**
11751      * @cfg {String} type
11752      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11753      */
11754     type : 'button',
11755
11756     // private
11757     menuClassTarget: 'tr',
11758
11759     /**
11760      * @cfg {String} clickEvent
11761      * The type of event to map to the button's event handler (defaults to 'click')
11762      */
11763     clickEvent : 'click',
11764
11765     /**
11766      * @cfg {Boolean} handleMouseEvents
11767      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11768      */
11769     handleMouseEvents : true,
11770
11771     /**
11772      * @cfg {String} tooltipType
11773      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11774      */
11775     tooltipType : 'qtip',
11776
11777     /**
11778      * @cfg {String} cls
11779      * A CSS class to apply to the button's main element.
11780      */
11781     
11782     /**
11783      * @cfg {Roo.Template} template (Optional)
11784      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11785      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11786      * require code modifications if required elements (e.g. a button) aren't present.
11787      */
11788
11789     // private
11790     render : function(renderTo){
11791         var btn;
11792         if(this.hideParent){
11793             this.parentEl = Roo.get(renderTo);
11794         }
11795         if(!this.dhconfig){
11796             if(!this.template){
11797                 if(!Roo.Button.buttonTemplate){
11798                     // hideous table template
11799                     Roo.Button.buttonTemplate = new Roo.Template(
11800                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11801                         '<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>',
11802                         "</tr></tbody></table>");
11803                 }
11804                 this.template = Roo.Button.buttonTemplate;
11805             }
11806             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11807             var btnEl = btn.child("button:first");
11808             btnEl.on('focus', this.onFocus, this);
11809             btnEl.on('blur', this.onBlur, this);
11810             if(this.cls){
11811                 btn.addClass(this.cls);
11812             }
11813             if(this.icon){
11814                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11815             }
11816             if(this.iconCls){
11817                 btnEl.addClass(this.iconCls);
11818                 if(!this.cls){
11819                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11820                 }
11821             }
11822             if(this.tabIndex !== undefined){
11823                 btnEl.dom.tabIndex = this.tabIndex;
11824             }
11825             if(this.tooltip){
11826                 if(typeof this.tooltip == 'object'){
11827                     Roo.QuickTips.tips(Roo.apply({
11828                           target: btnEl.id
11829                     }, this.tooltip));
11830                 } else {
11831                     btnEl.dom[this.tooltipType] = this.tooltip;
11832                 }
11833             }
11834         }else{
11835             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11836         }
11837         this.el = btn;
11838         if(this.id){
11839             this.el.dom.id = this.el.id = this.id;
11840         }
11841         if(this.menu){
11842             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11843             this.menu.on("show", this.onMenuShow, this);
11844             this.menu.on("hide", this.onMenuHide, this);
11845         }
11846         btn.addClass("x-btn");
11847         if(Roo.isIE && !Roo.isIE7){
11848             this.autoWidth.defer(1, this);
11849         }else{
11850             this.autoWidth();
11851         }
11852         if(this.handleMouseEvents){
11853             btn.on("mouseover", this.onMouseOver, this);
11854             btn.on("mouseout", this.onMouseOut, this);
11855             btn.on("mousedown", this.onMouseDown, this);
11856         }
11857         btn.on(this.clickEvent, this.onClick, this);
11858         //btn.on("mouseup", this.onMouseUp, this);
11859         if(this.hidden){
11860             this.hide();
11861         }
11862         if(this.disabled){
11863             this.disable();
11864         }
11865         Roo.ButtonToggleMgr.register(this);
11866         if(this.pressed){
11867             this.el.addClass("x-btn-pressed");
11868         }
11869         if(this.repeat){
11870             var repeater = new Roo.util.ClickRepeater(btn,
11871                 typeof this.repeat == "object" ? this.repeat : {}
11872             );
11873             repeater.on("click", this.onClick,  this);
11874         }
11875         
11876         this.fireEvent('render', this);
11877         
11878     },
11879     /**
11880      * Returns the button's underlying element
11881      * @return {Roo.Element} The element
11882      */
11883     getEl : function(){
11884         return this.el;  
11885     },
11886     
11887     /**
11888      * Destroys this Button and removes any listeners.
11889      */
11890     destroy : function(){
11891         Roo.ButtonToggleMgr.unregister(this);
11892         this.el.removeAllListeners();
11893         this.purgeListeners();
11894         this.el.remove();
11895     },
11896
11897     // private
11898     autoWidth : function(){
11899         if(this.el){
11900             this.el.setWidth("auto");
11901             if(Roo.isIE7 && Roo.isStrict){
11902                 var ib = this.el.child('button');
11903                 if(ib && ib.getWidth() > 20){
11904                     ib.clip();
11905                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11906                 }
11907             }
11908             if(this.minWidth){
11909                 if(this.hidden){
11910                     this.el.beginMeasure();
11911                 }
11912                 if(this.el.getWidth() < this.minWidth){
11913                     this.el.setWidth(this.minWidth);
11914                 }
11915                 if(this.hidden){
11916                     this.el.endMeasure();
11917                 }
11918             }
11919         }
11920     },
11921
11922     /**
11923      * Assigns this button's click handler
11924      * @param {Function} handler The function to call when the button is clicked
11925      * @param {Object} scope (optional) Scope for the function passed in
11926      */
11927     setHandler : function(handler, scope){
11928         this.handler = handler;
11929         this.scope = scope;  
11930     },
11931     
11932     /**
11933      * Sets this button's text
11934      * @param {String} text The button text
11935      */
11936     setText : function(text){
11937         this.text = text;
11938         if(this.el){
11939             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11940         }
11941         this.autoWidth();
11942     },
11943     
11944     /**
11945      * Gets the text for this button
11946      * @return {String} The button text
11947      */
11948     getText : function(){
11949         return this.text;  
11950     },
11951     
11952     /**
11953      * Show this button
11954      */
11955     show: function(){
11956         this.hidden = false;
11957         if(this.el){
11958             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11959         }
11960     },
11961     
11962     /**
11963      * Hide this button
11964      */
11965     hide: function(){
11966         this.hidden = true;
11967         if(this.el){
11968             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11969         }
11970     },
11971     
11972     /**
11973      * Convenience function for boolean show/hide
11974      * @param {Boolean} visible True to show, false to hide
11975      */
11976     setVisible: function(visible){
11977         if(visible) {
11978             this.show();
11979         }else{
11980             this.hide();
11981         }
11982     },
11983     
11984     /**
11985      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11986      * @param {Boolean} state (optional) Force a particular state
11987      */
11988     toggle : function(state){
11989         state = state === undefined ? !this.pressed : state;
11990         if(state != this.pressed){
11991             if(state){
11992                 this.el.addClass("x-btn-pressed");
11993                 this.pressed = true;
11994                 this.fireEvent("toggle", this, true);
11995             }else{
11996                 this.el.removeClass("x-btn-pressed");
11997                 this.pressed = false;
11998                 this.fireEvent("toggle", this, false);
11999             }
12000             if(this.toggleHandler){
12001                 this.toggleHandler.call(this.scope || this, this, state);
12002             }
12003         }
12004     },
12005     
12006     /**
12007      * Focus the button
12008      */
12009     focus : function(){
12010         this.el.child('button:first').focus();
12011     },
12012     
12013     /**
12014      * Disable this button
12015      */
12016     disable : function(){
12017         if(this.el){
12018             this.el.addClass("x-btn-disabled");
12019         }
12020         this.disabled = true;
12021     },
12022     
12023     /**
12024      * Enable this button
12025      */
12026     enable : function(){
12027         if(this.el){
12028             this.el.removeClass("x-btn-disabled");
12029         }
12030         this.disabled = false;
12031     },
12032
12033     /**
12034      * Convenience function for boolean enable/disable
12035      * @param {Boolean} enabled True to enable, false to disable
12036      */
12037     setDisabled : function(v){
12038         this[v !== true ? "enable" : "disable"]();
12039     },
12040
12041     // private
12042     onClick : function(e){
12043         if(e){
12044             e.preventDefault();
12045         }
12046         if(e.button != 0){
12047             return;
12048         }
12049         if(!this.disabled){
12050             if(this.enableToggle){
12051                 this.toggle();
12052             }
12053             if(this.menu && !this.menu.isVisible()){
12054                 this.menu.show(this.el, this.menuAlign);
12055             }
12056             this.fireEvent("click", this, e);
12057             if(this.handler){
12058                 this.el.removeClass("x-btn-over");
12059                 this.handler.call(this.scope || this, this, e);
12060             }
12061         }
12062     },
12063     // private
12064     onMouseOver : function(e){
12065         if(!this.disabled){
12066             this.el.addClass("x-btn-over");
12067             this.fireEvent('mouseover', this, e);
12068         }
12069     },
12070     // private
12071     onMouseOut : function(e){
12072         if(!e.within(this.el,  true)){
12073             this.el.removeClass("x-btn-over");
12074             this.fireEvent('mouseout', this, e);
12075         }
12076     },
12077     // private
12078     onFocus : function(e){
12079         if(!this.disabled){
12080             this.el.addClass("x-btn-focus");
12081         }
12082     },
12083     // private
12084     onBlur : function(e){
12085         this.el.removeClass("x-btn-focus");
12086     },
12087     // private
12088     onMouseDown : function(e){
12089         if(!this.disabled && e.button == 0){
12090             this.el.addClass("x-btn-click");
12091             Roo.get(document).on('mouseup', this.onMouseUp, this);
12092         }
12093     },
12094     // private
12095     onMouseUp : function(e){
12096         if(e.button == 0){
12097             this.el.removeClass("x-btn-click");
12098             Roo.get(document).un('mouseup', this.onMouseUp, this);
12099         }
12100     },
12101     // private
12102     onMenuShow : function(e){
12103         this.el.addClass("x-btn-menu-active");
12104     },
12105     // private
12106     onMenuHide : function(e){
12107         this.el.removeClass("x-btn-menu-active");
12108     }   
12109 });
12110
12111 // Private utility class used by Button
12112 Roo.ButtonToggleMgr = function(){
12113    var groups = {};
12114    
12115    function toggleGroup(btn, state){
12116        if(state){
12117            var g = groups[btn.toggleGroup];
12118            for(var i = 0, l = g.length; i < l; i++){
12119                if(g[i] != btn){
12120                    g[i].toggle(false);
12121                }
12122            }
12123        }
12124    }
12125    
12126    return {
12127        register : function(btn){
12128            if(!btn.toggleGroup){
12129                return;
12130            }
12131            var g = groups[btn.toggleGroup];
12132            if(!g){
12133                g = groups[btn.toggleGroup] = [];
12134            }
12135            g.push(btn);
12136            btn.on("toggle", toggleGroup);
12137        },
12138        
12139        unregister : function(btn){
12140            if(!btn.toggleGroup){
12141                return;
12142            }
12143            var g = groups[btn.toggleGroup];
12144            if(g){
12145                g.remove(btn);
12146                btn.un("toggle", toggleGroup);
12147            }
12148        }
12149    };
12150 }();/*
12151  * Based on:
12152  * Ext JS Library 1.1.1
12153  * Copyright(c) 2006-2007, Ext JS, LLC.
12154  *
12155  * Originally Released Under LGPL - original licence link has changed is not relivant.
12156  *
12157  * Fork - LGPL
12158  * <script type="text/javascript">
12159  */
12160  
12161 /**
12162  * @class Roo.SplitButton
12163  * @extends Roo.Button
12164  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12165  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12166  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12167  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12168  * @cfg {String} arrowTooltip The title attribute of the arrow
12169  * @constructor
12170  * Create a new menu button
12171  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12172  * @param {Object} config The config object
12173  */
12174 Roo.SplitButton = function(renderTo, config){
12175     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12176     /**
12177      * @event arrowclick
12178      * Fires when this button's arrow is clicked
12179      * @param {SplitButton} this
12180      * @param {EventObject} e The click event
12181      */
12182     this.addEvents({"arrowclick":true});
12183 };
12184
12185 Roo.extend(Roo.SplitButton, Roo.Button, {
12186     render : function(renderTo){
12187         // this is one sweet looking template!
12188         var tpl = new Roo.Template(
12189             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12190             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12191             '<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>',
12192             "</tbody></table></td><td>",
12193             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12194             '<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>',
12195             "</tbody></table></td></tr></table>"
12196         );
12197         var btn = tpl.append(renderTo, [this.text, this.type], true);
12198         var btnEl = btn.child("button");
12199         if(this.cls){
12200             btn.addClass(this.cls);
12201         }
12202         if(this.icon){
12203             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12204         }
12205         if(this.iconCls){
12206             btnEl.addClass(this.iconCls);
12207             if(!this.cls){
12208                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12209             }
12210         }
12211         this.el = btn;
12212         if(this.handleMouseEvents){
12213             btn.on("mouseover", this.onMouseOver, this);
12214             btn.on("mouseout", this.onMouseOut, this);
12215             btn.on("mousedown", this.onMouseDown, this);
12216             btn.on("mouseup", this.onMouseUp, this);
12217         }
12218         btn.on(this.clickEvent, this.onClick, this);
12219         if(this.tooltip){
12220             if(typeof this.tooltip == 'object'){
12221                 Roo.QuickTips.tips(Roo.apply({
12222                       target: btnEl.id
12223                 }, this.tooltip));
12224             } else {
12225                 btnEl.dom[this.tooltipType] = this.tooltip;
12226             }
12227         }
12228         if(this.arrowTooltip){
12229             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12230         }
12231         if(this.hidden){
12232             this.hide();
12233         }
12234         if(this.disabled){
12235             this.disable();
12236         }
12237         if(this.pressed){
12238             this.el.addClass("x-btn-pressed");
12239         }
12240         if(Roo.isIE && !Roo.isIE7){
12241             this.autoWidth.defer(1, this);
12242         }else{
12243             this.autoWidth();
12244         }
12245         if(this.menu){
12246             this.menu.on("show", this.onMenuShow, this);
12247             this.menu.on("hide", this.onMenuHide, this);
12248         }
12249         this.fireEvent('render', this);
12250     },
12251
12252     // private
12253     autoWidth : function(){
12254         if(this.el){
12255             var tbl = this.el.child("table:first");
12256             var tbl2 = this.el.child("table:last");
12257             this.el.setWidth("auto");
12258             tbl.setWidth("auto");
12259             if(Roo.isIE7 && Roo.isStrict){
12260                 var ib = this.el.child('button:first');
12261                 if(ib && ib.getWidth() > 20){
12262                     ib.clip();
12263                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12264                 }
12265             }
12266             if(this.minWidth){
12267                 if(this.hidden){
12268                     this.el.beginMeasure();
12269                 }
12270                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12271                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12272                 }
12273                 if(this.hidden){
12274                     this.el.endMeasure();
12275                 }
12276             }
12277             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12278         } 
12279     },
12280     /**
12281      * Sets this button's click handler
12282      * @param {Function} handler The function to call when the button is clicked
12283      * @param {Object} scope (optional) Scope for the function passed above
12284      */
12285     setHandler : function(handler, scope){
12286         this.handler = handler;
12287         this.scope = scope;  
12288     },
12289     
12290     /**
12291      * Sets this button's arrow click handler
12292      * @param {Function} handler The function to call when the arrow is clicked
12293      * @param {Object} scope (optional) Scope for the function passed above
12294      */
12295     setArrowHandler : function(handler, scope){
12296         this.arrowHandler = handler;
12297         this.scope = scope;  
12298     },
12299     
12300     /**
12301      * Focus the button
12302      */
12303     focus : function(){
12304         if(this.el){
12305             this.el.child("button:first").focus();
12306         }
12307     },
12308
12309     // private
12310     onClick : function(e){
12311         e.preventDefault();
12312         if(!this.disabled){
12313             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12314                 if(this.menu && !this.menu.isVisible()){
12315                     this.menu.show(this.el, this.menuAlign);
12316                 }
12317                 this.fireEvent("arrowclick", this, e);
12318                 if(this.arrowHandler){
12319                     this.arrowHandler.call(this.scope || this, this, e);
12320                 }
12321             }else{
12322                 this.fireEvent("click", this, e);
12323                 if(this.handler){
12324                     this.handler.call(this.scope || this, this, e);
12325                 }
12326             }
12327         }
12328     },
12329     // private
12330     onMouseDown : function(e){
12331         if(!this.disabled){
12332             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12333         }
12334     },
12335     // private
12336     onMouseUp : function(e){
12337         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12338     }   
12339 });
12340
12341
12342 // backwards compat
12343 Roo.MenuButton = Roo.SplitButton;/*
12344  * Based on:
12345  * Ext JS Library 1.1.1
12346  * Copyright(c) 2006-2007, Ext JS, LLC.
12347  *
12348  * Originally Released Under LGPL - original licence link has changed is not relivant.
12349  *
12350  * Fork - LGPL
12351  * <script type="text/javascript">
12352  */
12353
12354 /**
12355  * @class Roo.Toolbar
12356  * Basic Toolbar class.
12357  * @constructor
12358  * Creates a new Toolbar
12359  * @param {Object} container The config object
12360  */ 
12361 Roo.Toolbar = function(container, buttons, config)
12362 {
12363     /// old consturctor format still supported..
12364     if(container instanceof Array){ // omit the container for later rendering
12365         buttons = container;
12366         config = buttons;
12367         container = null;
12368     }
12369     if (typeof(container) == 'object' && container.xtype) {
12370         config = container;
12371         container = config.container;
12372         buttons = config.buttons || []; // not really - use items!!
12373     }
12374     var xitems = [];
12375     if (config && config.items) {
12376         xitems = config.items;
12377         delete config.items;
12378     }
12379     Roo.apply(this, config);
12380     this.buttons = buttons;
12381     
12382     if(container){
12383         this.render(container);
12384     }
12385     this.xitems = xitems;
12386     Roo.each(xitems, function(b) {
12387         this.add(b);
12388     }, this);
12389     
12390 };
12391
12392 Roo.Toolbar.prototype = {
12393     /**
12394      * @cfg {Array} items
12395      * array of button configs or elements to add (will be converted to a MixedCollection)
12396      */
12397     
12398     /**
12399      * @cfg {String/HTMLElement/Element} container
12400      * The id or element that will contain the toolbar
12401      */
12402     // private
12403     render : function(ct){
12404         this.el = Roo.get(ct);
12405         if(this.cls){
12406             this.el.addClass(this.cls);
12407         }
12408         // using a table allows for vertical alignment
12409         // 100% width is needed by Safari...
12410         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12411         this.tr = this.el.child("tr", true);
12412         var autoId = 0;
12413         this.items = new Roo.util.MixedCollection(false, function(o){
12414             return o.id || ("item" + (++autoId));
12415         });
12416         if(this.buttons){
12417             this.add.apply(this, this.buttons);
12418             delete this.buttons;
12419         }
12420     },
12421
12422     /**
12423      * Adds element(s) to the toolbar -- this function takes a variable number of 
12424      * arguments of mixed type and adds them to the toolbar.
12425      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12426      * <ul>
12427      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12428      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12429      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12430      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12431      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12432      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12433      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12434      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12435      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12436      * </ul>
12437      * @param {Mixed} arg2
12438      * @param {Mixed} etc.
12439      */
12440     add : function(){
12441         var a = arguments, l = a.length;
12442         for(var i = 0; i < l; i++){
12443             this._add(a[i]);
12444         }
12445     },
12446     // private..
12447     _add : function(el) {
12448         
12449         if (el.xtype) {
12450             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12451         }
12452         
12453         if (el.applyTo){ // some kind of form field
12454             return this.addField(el);
12455         } 
12456         if (el.render){ // some kind of Toolbar.Item
12457             return this.addItem(el);
12458         }
12459         if (typeof el == "string"){ // string
12460             if(el == "separator" || el == "-"){
12461                 return this.addSeparator();
12462             }
12463             if (el == " "){
12464                 return this.addSpacer();
12465             }
12466             if(el == "->"){
12467                 return this.addFill();
12468             }
12469             return this.addText(el);
12470             
12471         }
12472         if(el.tagName){ // element
12473             return this.addElement(el);
12474         }
12475         if(typeof el == "object"){ // must be button config?
12476             return this.addButton(el);
12477         }
12478         // and now what?!?!
12479         return false;
12480         
12481     },
12482     
12483     /**
12484      * Add an Xtype element
12485      * @param {Object} xtype Xtype Object
12486      * @return {Object} created Object
12487      */
12488     addxtype : function(e){
12489         return this.add(e);  
12490     },
12491     
12492     /**
12493      * Returns the Element for this toolbar.
12494      * @return {Roo.Element}
12495      */
12496     getEl : function(){
12497         return this.el;  
12498     },
12499     
12500     /**
12501      * Adds a separator
12502      * @return {Roo.Toolbar.Item} The separator item
12503      */
12504     addSeparator : function(){
12505         return this.addItem(new Roo.Toolbar.Separator());
12506     },
12507
12508     /**
12509      * Adds a spacer element
12510      * @return {Roo.Toolbar.Spacer} The spacer item
12511      */
12512     addSpacer : function(){
12513         return this.addItem(new Roo.Toolbar.Spacer());
12514     },
12515
12516     /**
12517      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12518      * @return {Roo.Toolbar.Fill} The fill item
12519      */
12520     addFill : function(){
12521         return this.addItem(new Roo.Toolbar.Fill());
12522     },
12523
12524     /**
12525      * Adds any standard HTML element to the toolbar
12526      * @param {String/HTMLElement/Element} el The element or id of the element to add
12527      * @return {Roo.Toolbar.Item} The element's item
12528      */
12529     addElement : function(el){
12530         return this.addItem(new Roo.Toolbar.Item(el));
12531     },
12532     /**
12533      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12534      * @type Roo.util.MixedCollection  
12535      */
12536     items : false,
12537      
12538     /**
12539      * Adds any Toolbar.Item or subclass
12540      * @param {Roo.Toolbar.Item} item
12541      * @return {Roo.Toolbar.Item} The item
12542      */
12543     addItem : function(item){
12544         var td = this.nextBlock();
12545         item.render(td);
12546         this.items.add(item);
12547         return item;
12548     },
12549     
12550     /**
12551      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12552      * @param {Object/Array} config A button config or array of configs
12553      * @return {Roo.Toolbar.Button/Array}
12554      */
12555     addButton : function(config){
12556         if(config instanceof Array){
12557             var buttons = [];
12558             for(var i = 0, len = config.length; i < len; i++) {
12559                 buttons.push(this.addButton(config[i]));
12560             }
12561             return buttons;
12562         }
12563         var b = config;
12564         if(!(config instanceof Roo.Toolbar.Button)){
12565             b = config.split ?
12566                 new Roo.Toolbar.SplitButton(config) :
12567                 new Roo.Toolbar.Button(config);
12568         }
12569         var td = this.nextBlock();
12570         b.render(td);
12571         this.items.add(b);
12572         return b;
12573     },
12574     
12575     /**
12576      * Adds text to the toolbar
12577      * @param {String} text The text to add
12578      * @return {Roo.Toolbar.Item} The element's item
12579      */
12580     addText : function(text){
12581         return this.addItem(new Roo.Toolbar.TextItem(text));
12582     },
12583     
12584     /**
12585      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12586      * @param {Number} index The index where the item is to be inserted
12587      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12588      * @return {Roo.Toolbar.Button/Item}
12589      */
12590     insertButton : function(index, item){
12591         if(item instanceof Array){
12592             var buttons = [];
12593             for(var i = 0, len = item.length; i < len; i++) {
12594                buttons.push(this.insertButton(index + i, item[i]));
12595             }
12596             return buttons;
12597         }
12598         if (!(item instanceof Roo.Toolbar.Button)){
12599            item = new Roo.Toolbar.Button(item);
12600         }
12601         var td = document.createElement("td");
12602         this.tr.insertBefore(td, this.tr.childNodes[index]);
12603         item.render(td);
12604         this.items.insert(index, item);
12605         return item;
12606     },
12607     
12608     /**
12609      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12610      * @param {Object} config
12611      * @return {Roo.Toolbar.Item} The element's item
12612      */
12613     addDom : function(config, returnEl){
12614         var td = this.nextBlock();
12615         Roo.DomHelper.overwrite(td, config);
12616         var ti = new Roo.Toolbar.Item(td.firstChild);
12617         ti.render(td);
12618         this.items.add(ti);
12619         return ti;
12620     },
12621
12622     /**
12623      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12624      * @type Roo.util.MixedCollection  
12625      */
12626     fields : false,
12627     
12628     /**
12629      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12630      * Note: the field should not have been rendered yet. For a field that has already been
12631      * rendered, use {@link #addElement}.
12632      * @param {Roo.form.Field} field
12633      * @return {Roo.ToolbarItem}
12634      */
12635      
12636       
12637     addField : function(field) {
12638         if (!this.fields) {
12639             var autoId = 0;
12640             this.fields = new Roo.util.MixedCollection(false, function(o){
12641                 return o.id || ("item" + (++autoId));
12642             });
12643
12644         }
12645         
12646         var td = this.nextBlock();
12647         field.render(td);
12648         var ti = new Roo.Toolbar.Item(td.firstChild);
12649         ti.render(td);
12650         this.items.add(ti);
12651         this.fields.add(field);
12652         return ti;
12653     },
12654     /**
12655      * Hide the toolbar
12656      * @method hide
12657      */
12658      
12659       
12660     hide : function()
12661     {
12662         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12663         this.el.child('div').hide();
12664     },
12665     /**
12666      * Show the toolbar
12667      * @method show
12668      */
12669     show : function()
12670     {
12671         this.el.child('div').show();
12672     },
12673       
12674     // private
12675     nextBlock : function(){
12676         var td = document.createElement("td");
12677         this.tr.appendChild(td);
12678         return td;
12679     },
12680
12681     // private
12682     destroy : function(){
12683         if(this.items){ // rendered?
12684             Roo.destroy.apply(Roo, this.items.items);
12685         }
12686         if(this.fields){ // rendered?
12687             Roo.destroy.apply(Roo, this.fields.items);
12688         }
12689         Roo.Element.uncache(this.el, this.tr);
12690     }
12691 };
12692
12693 /**
12694  * @class Roo.Toolbar.Item
12695  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12696  * @constructor
12697  * Creates a new Item
12698  * @param {HTMLElement} el 
12699  */
12700 Roo.Toolbar.Item = function(el){
12701     this.el = Roo.getDom(el);
12702     this.id = Roo.id(this.el);
12703     this.hidden = false;
12704 };
12705
12706 Roo.Toolbar.Item.prototype = {
12707     
12708     /**
12709      * Get this item's HTML Element
12710      * @return {HTMLElement}
12711      */
12712     getEl : function(){
12713        return this.el;  
12714     },
12715
12716     // private
12717     render : function(td){
12718         this.td = td;
12719         td.appendChild(this.el);
12720     },
12721     
12722     /**
12723      * Removes and destroys this item.
12724      */
12725     destroy : function(){
12726         this.td.parentNode.removeChild(this.td);
12727     },
12728     
12729     /**
12730      * Shows this item.
12731      */
12732     show: function(){
12733         this.hidden = false;
12734         this.td.style.display = "";
12735     },
12736     
12737     /**
12738      * Hides this item.
12739      */
12740     hide: function(){
12741         this.hidden = true;
12742         this.td.style.display = "none";
12743     },
12744     
12745     /**
12746      * Convenience function for boolean show/hide.
12747      * @param {Boolean} visible true to show/false to hide
12748      */
12749     setVisible: function(visible){
12750         if(visible) {
12751             this.show();
12752         }else{
12753             this.hide();
12754         }
12755     },
12756     
12757     /**
12758      * Try to focus this item.
12759      */
12760     focus : function(){
12761         Roo.fly(this.el).focus();
12762     },
12763     
12764     /**
12765      * Disables this item.
12766      */
12767     disable : function(){
12768         Roo.fly(this.td).addClass("x-item-disabled");
12769         this.disabled = true;
12770         this.el.disabled = true;
12771     },
12772     
12773     /**
12774      * Enables this item.
12775      */
12776     enable : function(){
12777         Roo.fly(this.td).removeClass("x-item-disabled");
12778         this.disabled = false;
12779         this.el.disabled = false;
12780     }
12781 };
12782
12783
12784 /**
12785  * @class Roo.Toolbar.Separator
12786  * @extends Roo.Toolbar.Item
12787  * A simple toolbar separator class
12788  * @constructor
12789  * Creates a new Separator
12790  */
12791 Roo.Toolbar.Separator = function(){
12792     var s = document.createElement("span");
12793     s.className = "ytb-sep";
12794     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12795 };
12796 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12797     enable:Roo.emptyFn,
12798     disable:Roo.emptyFn,
12799     focus:Roo.emptyFn
12800 });
12801
12802 /**
12803  * @class Roo.Toolbar.Spacer
12804  * @extends Roo.Toolbar.Item
12805  * A simple element that adds extra horizontal space to a toolbar.
12806  * @constructor
12807  * Creates a new Spacer
12808  */
12809 Roo.Toolbar.Spacer = function(){
12810     var s = document.createElement("div");
12811     s.className = "ytb-spacer";
12812     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12813 };
12814 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12815     enable:Roo.emptyFn,
12816     disable:Roo.emptyFn,
12817     focus:Roo.emptyFn
12818 });
12819
12820 /**
12821  * @class Roo.Toolbar.Fill
12822  * @extends Roo.Toolbar.Spacer
12823  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12824  * @constructor
12825  * Creates a new Spacer
12826  */
12827 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12828     // private
12829     render : function(td){
12830         td.style.width = '100%';
12831         Roo.Toolbar.Fill.superclass.render.call(this, td);
12832     }
12833 });
12834
12835 /**
12836  * @class Roo.Toolbar.TextItem
12837  * @extends Roo.Toolbar.Item
12838  * A simple class that renders text directly into a toolbar.
12839  * @constructor
12840  * Creates a new TextItem
12841  * @param {String} text
12842  */
12843 Roo.Toolbar.TextItem = function(text){
12844     if (typeof(text) == 'object') {
12845         text = text.text;
12846     }
12847     var s = document.createElement("span");
12848     s.className = "ytb-text";
12849     s.innerHTML = text;
12850     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12851 };
12852 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12853     enable:Roo.emptyFn,
12854     disable:Roo.emptyFn,
12855     focus:Roo.emptyFn
12856 });
12857
12858 /**
12859  * @class Roo.Toolbar.Button
12860  * @extends Roo.Button
12861  * A button that renders into a toolbar.
12862  * @constructor
12863  * Creates a new Button
12864  * @param {Object} config A standard {@link Roo.Button} config object
12865  */
12866 Roo.Toolbar.Button = function(config){
12867     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12868 };
12869 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12870     render : function(td){
12871         this.td = td;
12872         Roo.Toolbar.Button.superclass.render.call(this, td);
12873     },
12874     
12875     /**
12876      * Removes and destroys this button
12877      */
12878     destroy : function(){
12879         Roo.Toolbar.Button.superclass.destroy.call(this);
12880         this.td.parentNode.removeChild(this.td);
12881     },
12882     
12883     /**
12884      * Shows this button
12885      */
12886     show: function(){
12887         this.hidden = false;
12888         this.td.style.display = "";
12889     },
12890     
12891     /**
12892      * Hides this button
12893      */
12894     hide: function(){
12895         this.hidden = true;
12896         this.td.style.display = "none";
12897     },
12898
12899     /**
12900      * Disables this item
12901      */
12902     disable : function(){
12903         Roo.fly(this.td).addClass("x-item-disabled");
12904         this.disabled = true;
12905     },
12906
12907     /**
12908      * Enables this item
12909      */
12910     enable : function(){
12911         Roo.fly(this.td).removeClass("x-item-disabled");
12912         this.disabled = false;
12913     }
12914 });
12915 // backwards compat
12916 Roo.ToolbarButton = Roo.Toolbar.Button;
12917
12918 /**
12919  * @class Roo.Toolbar.SplitButton
12920  * @extends Roo.SplitButton
12921  * A menu button that renders into a toolbar.
12922  * @constructor
12923  * Creates a new SplitButton
12924  * @param {Object} config A standard {@link Roo.SplitButton} config object
12925  */
12926 Roo.Toolbar.SplitButton = function(config){
12927     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12928 };
12929 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12930     render : function(td){
12931         this.td = td;
12932         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12933     },
12934     
12935     /**
12936      * Removes and destroys this button
12937      */
12938     destroy : function(){
12939         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12940         this.td.parentNode.removeChild(this.td);
12941     },
12942     
12943     /**
12944      * Shows this button
12945      */
12946     show: function(){
12947         this.hidden = false;
12948         this.td.style.display = "";
12949     },
12950     
12951     /**
12952      * Hides this button
12953      */
12954     hide: function(){
12955         this.hidden = true;
12956         this.td.style.display = "none";
12957     }
12958 });
12959
12960 // backwards compat
12961 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12962  * Based on:
12963  * Ext JS Library 1.1.1
12964  * Copyright(c) 2006-2007, Ext JS, LLC.
12965  *
12966  * Originally Released Under LGPL - original licence link has changed is not relivant.
12967  *
12968  * Fork - LGPL
12969  * <script type="text/javascript">
12970  */
12971  
12972 /**
12973  * @class Roo.PagingToolbar
12974  * @extends Roo.Toolbar
12975  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12976  * @constructor
12977  * Create a new PagingToolbar
12978  * @param {Object} config The config object
12979  */
12980 Roo.PagingToolbar = function(el, ds, config)
12981 {
12982     // old args format still supported... - xtype is prefered..
12983     if (typeof(el) == 'object' && el.xtype) {
12984         // created from xtype...
12985         config = el;
12986         ds = el.dataSource;
12987         el = config.container;
12988     }
12989     var items = [];
12990     if (config.items) {
12991         items = config.items;
12992         config.items = [];
12993     }
12994     
12995     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12996     this.ds = ds;
12997     this.cursor = 0;
12998     this.renderButtons(this.el);
12999     this.bind(ds);
13000     
13001     // supprot items array.
13002    
13003     Roo.each(items, function(e) {
13004         this.add(Roo.factory(e));
13005     },this);
13006     
13007 };
13008
13009 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13010     /**
13011      * @cfg {Roo.data.Store} dataSource
13012      * The underlying data store providing the paged data
13013      */
13014     /**
13015      * @cfg {String/HTMLElement/Element} container
13016      * container The id or element that will contain the toolbar
13017      */
13018     /**
13019      * @cfg {Boolean} displayInfo
13020      * True to display the displayMsg (defaults to false)
13021      */
13022     /**
13023      * @cfg {Number} pageSize
13024      * The number of records to display per page (defaults to 20)
13025      */
13026     pageSize: 20,
13027     /**
13028      * @cfg {String} displayMsg
13029      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13030      */
13031     displayMsg : 'Displaying {0} - {1} of {2}',
13032     /**
13033      * @cfg {String} emptyMsg
13034      * The message to display when no records are found (defaults to "No data to display")
13035      */
13036     emptyMsg : 'No data to display',
13037     /**
13038      * Customizable piece of the default paging text (defaults to "Page")
13039      * @type String
13040      */
13041     beforePageText : "Page",
13042     /**
13043      * Customizable piece of the default paging text (defaults to "of %0")
13044      * @type String
13045      */
13046     afterPageText : "of {0}",
13047     /**
13048      * Customizable piece of the default paging text (defaults to "First Page")
13049      * @type String
13050      */
13051     firstText : "First Page",
13052     /**
13053      * Customizable piece of the default paging text (defaults to "Previous Page")
13054      * @type String
13055      */
13056     prevText : "Previous Page",
13057     /**
13058      * Customizable piece of the default paging text (defaults to "Next Page")
13059      * @type String
13060      */
13061     nextText : "Next Page",
13062     /**
13063      * Customizable piece of the default paging text (defaults to "Last Page")
13064      * @type String
13065      */
13066     lastText : "Last Page",
13067     /**
13068      * Customizable piece of the default paging text (defaults to "Refresh")
13069      * @type String
13070      */
13071     refreshText : "Refresh",
13072
13073     // private
13074     renderButtons : function(el){
13075         Roo.PagingToolbar.superclass.render.call(this, el);
13076         this.first = this.addButton({
13077             tooltip: this.firstText,
13078             cls: "x-btn-icon x-grid-page-first",
13079             disabled: true,
13080             handler: this.onClick.createDelegate(this, ["first"])
13081         });
13082         this.prev = this.addButton({
13083             tooltip: this.prevText,
13084             cls: "x-btn-icon x-grid-page-prev",
13085             disabled: true,
13086             handler: this.onClick.createDelegate(this, ["prev"])
13087         });
13088         //this.addSeparator();
13089         this.add(this.beforePageText);
13090         this.field = Roo.get(this.addDom({
13091            tag: "input",
13092            type: "text",
13093            size: "3",
13094            value: "1",
13095            cls: "x-grid-page-number"
13096         }).el);
13097         this.field.on("keydown", this.onPagingKeydown, this);
13098         this.field.on("focus", function(){this.dom.select();});
13099         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13100         this.field.setHeight(18);
13101         //this.addSeparator();
13102         this.next = this.addButton({
13103             tooltip: this.nextText,
13104             cls: "x-btn-icon x-grid-page-next",
13105             disabled: true,
13106             handler: this.onClick.createDelegate(this, ["next"])
13107         });
13108         this.last = this.addButton({
13109             tooltip: this.lastText,
13110             cls: "x-btn-icon x-grid-page-last",
13111             disabled: true,
13112             handler: this.onClick.createDelegate(this, ["last"])
13113         });
13114         //this.addSeparator();
13115         this.loading = this.addButton({
13116             tooltip: this.refreshText,
13117             cls: "x-btn-icon x-grid-loading",
13118             handler: this.onClick.createDelegate(this, ["refresh"])
13119         });
13120
13121         if(this.displayInfo){
13122             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13123         }
13124     },
13125
13126     // private
13127     updateInfo : function(){
13128         if(this.displayEl){
13129             var count = this.ds.getCount();
13130             var msg = count == 0 ?
13131                 this.emptyMsg :
13132                 String.format(
13133                     this.displayMsg,
13134                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13135                 );
13136             this.displayEl.update(msg);
13137         }
13138     },
13139
13140     // private
13141     onLoad : function(ds, r, o){
13142        this.cursor = o.params ? o.params.start : 0;
13143        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13144
13145        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13146        this.field.dom.value = ap;
13147        this.first.setDisabled(ap == 1);
13148        this.prev.setDisabled(ap == 1);
13149        this.next.setDisabled(ap == ps);
13150        this.last.setDisabled(ap == ps);
13151        this.loading.enable();
13152        this.updateInfo();
13153     },
13154
13155     // private
13156     getPageData : function(){
13157         var total = this.ds.getTotalCount();
13158         return {
13159             total : total,
13160             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13161             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13162         };
13163     },
13164
13165     // private
13166     onLoadError : function(){
13167         this.loading.enable();
13168     },
13169
13170     // private
13171     onPagingKeydown : function(e){
13172         var k = e.getKey();
13173         var d = this.getPageData();
13174         if(k == e.RETURN){
13175             var v = this.field.dom.value, pageNum;
13176             if(!v || isNaN(pageNum = parseInt(v, 10))){
13177                 this.field.dom.value = d.activePage;
13178                 return;
13179             }
13180             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13181             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13182             e.stopEvent();
13183         }
13184         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))
13185         {
13186           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13187           this.field.dom.value = pageNum;
13188           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13189           e.stopEvent();
13190         }
13191         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13192         {
13193           var v = this.field.dom.value, pageNum; 
13194           var increment = (e.shiftKey) ? 10 : 1;
13195           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13196             increment *= -1;
13197           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13198             this.field.dom.value = d.activePage;
13199             return;
13200           }
13201           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13202           {
13203             this.field.dom.value = parseInt(v, 10) + increment;
13204             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13205             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13206           }
13207           e.stopEvent();
13208         }
13209     },
13210
13211     // private
13212     beforeLoad : function(){
13213         if(this.loading){
13214             this.loading.disable();
13215         }
13216     },
13217
13218     // private
13219     onClick : function(which){
13220         var ds = this.ds;
13221         switch(which){
13222             case "first":
13223                 ds.load({params:{start: 0, limit: this.pageSize}});
13224             break;
13225             case "prev":
13226                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13227             break;
13228             case "next":
13229                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13230             break;
13231             case "last":
13232                 var total = ds.getTotalCount();
13233                 var extra = total % this.pageSize;
13234                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13235                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13236             break;
13237             case "refresh":
13238                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13239             break;
13240         }
13241     },
13242
13243     /**
13244      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13245      * @param {Roo.data.Store} store The data store to unbind
13246      */
13247     unbind : function(ds){
13248         ds.un("beforeload", this.beforeLoad, this);
13249         ds.un("load", this.onLoad, this);
13250         ds.un("loadexception", this.onLoadError, this);
13251         ds.un("remove", this.updateInfo, this);
13252         ds.un("add", this.updateInfo, this);
13253         this.ds = undefined;
13254     },
13255
13256     /**
13257      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13258      * @param {Roo.data.Store} store The data store to bind
13259      */
13260     bind : function(ds){
13261         ds.on("beforeload", this.beforeLoad, this);
13262         ds.on("load", this.onLoad, this);
13263         ds.on("loadexception", this.onLoadError, this);
13264         ds.on("remove", this.updateInfo, this);
13265         ds.on("add", this.updateInfo, this);
13266         this.ds = ds;
13267     }
13268 });/*
13269  * Based on:
13270  * Ext JS Library 1.1.1
13271  * Copyright(c) 2006-2007, Ext JS, LLC.
13272  *
13273  * Originally Released Under LGPL - original licence link has changed is not relivant.
13274  *
13275  * Fork - LGPL
13276  * <script type="text/javascript">
13277  */
13278
13279 /**
13280  * @class Roo.Resizable
13281  * @extends Roo.util.Observable
13282  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13283  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13284  * 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
13285  * the element will be wrapped for you automatically.</p>
13286  * <p>Here is the list of valid resize handles:</p>
13287  * <pre>
13288 Value   Description
13289 ------  -------------------
13290  'n'     north
13291  's'     south
13292  'e'     east
13293  'w'     west
13294  'nw'    northwest
13295  'sw'    southwest
13296  'se'    southeast
13297  'ne'    northeast
13298  'hd'    horizontal drag
13299  'all'   all
13300 </pre>
13301  * <p>Here's an example showing the creation of a typical Resizable:</p>
13302  * <pre><code>
13303 var resizer = new Roo.Resizable("element-id", {
13304     handles: 'all',
13305     minWidth: 200,
13306     minHeight: 100,
13307     maxWidth: 500,
13308     maxHeight: 400,
13309     pinned: true
13310 });
13311 resizer.on("resize", myHandler);
13312 </code></pre>
13313  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13314  * resizer.east.setDisplayed(false);</p>
13315  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13316  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13317  * resize operation's new size (defaults to [0, 0])
13318  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13319  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13320  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13321  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13322  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13323  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13324  * @cfg {Number} width The width of the element in pixels (defaults to null)
13325  * @cfg {Number} height The height of the element in pixels (defaults to null)
13326  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13327  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13328  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13329  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13330  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13331  * in favor of the handles config option (defaults to false)
13332  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13333  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13334  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13335  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13336  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13337  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13338  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13339  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13340  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13341  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13342  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13343  * @constructor
13344  * Create a new resizable component
13345  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13346  * @param {Object} config configuration options
13347   */
13348 Roo.Resizable = function(el, config)
13349 {
13350     this.el = Roo.get(el);
13351
13352     if(config && config.wrap){
13353         config.resizeChild = this.el;
13354         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13355         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13356         this.el.setStyle("overflow", "hidden");
13357         this.el.setPositioning(config.resizeChild.getPositioning());
13358         config.resizeChild.clearPositioning();
13359         if(!config.width || !config.height){
13360             var csize = config.resizeChild.getSize();
13361             this.el.setSize(csize.width, csize.height);
13362         }
13363         if(config.pinned && !config.adjustments){
13364             config.adjustments = "auto";
13365         }
13366     }
13367
13368     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13369     this.proxy.unselectable();
13370     this.proxy.enableDisplayMode('block');
13371
13372     Roo.apply(this, config);
13373
13374     if(this.pinned){
13375         this.disableTrackOver = true;
13376         this.el.addClass("x-resizable-pinned");
13377     }
13378     // if the element isn't positioned, make it relative
13379     var position = this.el.getStyle("position");
13380     if(position != "absolute" && position != "fixed"){
13381         this.el.setStyle("position", "relative");
13382     }
13383     if(!this.handles){ // no handles passed, must be legacy style
13384         this.handles = 's,e,se';
13385         if(this.multiDirectional){
13386             this.handles += ',n,w';
13387         }
13388     }
13389     if(this.handles == "all"){
13390         this.handles = "n s e w ne nw se sw";
13391     }
13392     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13393     var ps = Roo.Resizable.positions;
13394     for(var i = 0, len = hs.length; i < len; i++){
13395         if(hs[i] && ps[hs[i]]){
13396             var pos = ps[hs[i]];
13397             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13398         }
13399     }
13400     // legacy
13401     this.corner = this.southeast;
13402     
13403     // updateBox = the box can move..
13404     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13405         this.updateBox = true;
13406     }
13407
13408     this.activeHandle = null;
13409
13410     if(this.resizeChild){
13411         if(typeof this.resizeChild == "boolean"){
13412             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13413         }else{
13414             this.resizeChild = Roo.get(this.resizeChild, true);
13415         }
13416     }
13417     
13418     if(this.adjustments == "auto"){
13419         var rc = this.resizeChild;
13420         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13421         if(rc && (hw || hn)){
13422             rc.position("relative");
13423             rc.setLeft(hw ? hw.el.getWidth() : 0);
13424             rc.setTop(hn ? hn.el.getHeight() : 0);
13425         }
13426         this.adjustments = [
13427             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13428             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13429         ];
13430     }
13431
13432     if(this.draggable){
13433         this.dd = this.dynamic ?
13434             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13435         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13436     }
13437
13438     // public events
13439     this.addEvents({
13440         /**
13441          * @event beforeresize
13442          * Fired before resize is allowed. Set enabled to false to cancel resize.
13443          * @param {Roo.Resizable} this
13444          * @param {Roo.EventObject} e The mousedown event
13445          */
13446         "beforeresize" : true,
13447         /**
13448          * @event resize
13449          * Fired after a resize.
13450          * @param {Roo.Resizable} this
13451          * @param {Number} width The new width
13452          * @param {Number} height The new height
13453          * @param {Roo.EventObject} e The mouseup event
13454          */
13455         "resize" : true
13456     });
13457
13458     if(this.width !== null && this.height !== null){
13459         this.resizeTo(this.width, this.height);
13460     }else{
13461         this.updateChildSize();
13462     }
13463     if(Roo.isIE){
13464         this.el.dom.style.zoom = 1;
13465     }
13466     Roo.Resizable.superclass.constructor.call(this);
13467 };
13468
13469 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13470         resizeChild : false,
13471         adjustments : [0, 0],
13472         minWidth : 5,
13473         minHeight : 5,
13474         maxWidth : 10000,
13475         maxHeight : 10000,
13476         enabled : true,
13477         animate : false,
13478         duration : .35,
13479         dynamic : false,
13480         handles : false,
13481         multiDirectional : false,
13482         disableTrackOver : false,
13483         easing : 'easeOutStrong',
13484         widthIncrement : 0,
13485         heightIncrement : 0,
13486         pinned : false,
13487         width : null,
13488         height : null,
13489         preserveRatio : false,
13490         transparent: false,
13491         minX: 0,
13492         minY: 0,
13493         draggable: false,
13494
13495         /**
13496          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13497          */
13498         constrainTo: undefined,
13499         /**
13500          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13501          */
13502         resizeRegion: undefined,
13503
13504
13505     /**
13506      * Perform a manual resize
13507      * @param {Number} width
13508      * @param {Number} height
13509      */
13510     resizeTo : function(width, height){
13511         this.el.setSize(width, height);
13512         this.updateChildSize();
13513         this.fireEvent("resize", this, width, height, null);
13514     },
13515
13516     // private
13517     startSizing : function(e, handle){
13518         this.fireEvent("beforeresize", this, e);
13519         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13520
13521             if(!this.overlay){
13522                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13523                 this.overlay.unselectable();
13524                 this.overlay.enableDisplayMode("block");
13525                 this.overlay.on("mousemove", this.onMouseMove, this);
13526                 this.overlay.on("mouseup", this.onMouseUp, this);
13527             }
13528             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13529
13530             this.resizing = true;
13531             this.startBox = this.el.getBox();
13532             this.startPoint = e.getXY();
13533             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13534                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13535
13536             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13537             this.overlay.show();
13538
13539             if(this.constrainTo) {
13540                 var ct = Roo.get(this.constrainTo);
13541                 this.resizeRegion = ct.getRegion().adjust(
13542                     ct.getFrameWidth('t'),
13543                     ct.getFrameWidth('l'),
13544                     -ct.getFrameWidth('b'),
13545                     -ct.getFrameWidth('r')
13546                 );
13547             }
13548
13549             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13550             this.proxy.show();
13551             this.proxy.setBox(this.startBox);
13552             if(!this.dynamic){
13553                 this.proxy.setStyle('visibility', 'visible');
13554             }
13555         }
13556     },
13557
13558     // private
13559     onMouseDown : function(handle, e){
13560         if(this.enabled){
13561             e.stopEvent();
13562             this.activeHandle = handle;
13563             this.startSizing(e, handle);
13564         }
13565     },
13566
13567     // private
13568     onMouseUp : function(e){
13569         var size = this.resizeElement();
13570         this.resizing = false;
13571         this.handleOut();
13572         this.overlay.hide();
13573         this.proxy.hide();
13574         this.fireEvent("resize", this, size.width, size.height, e);
13575     },
13576
13577     // private
13578     updateChildSize : function(){
13579         if(this.resizeChild){
13580             var el = this.el;
13581             var child = this.resizeChild;
13582             var adj = this.adjustments;
13583             if(el.dom.offsetWidth){
13584                 var b = el.getSize(true);
13585                 child.setSize(b.width+adj[0], b.height+adj[1]);
13586             }
13587             // Second call here for IE
13588             // The first call enables instant resizing and
13589             // the second call corrects scroll bars if they
13590             // exist
13591             if(Roo.isIE){
13592                 setTimeout(function(){
13593                     if(el.dom.offsetWidth){
13594                         var b = el.getSize(true);
13595                         child.setSize(b.width+adj[0], b.height+adj[1]);
13596                     }
13597                 }, 10);
13598             }
13599         }
13600     },
13601
13602     // private
13603     snap : function(value, inc, min){
13604         if(!inc || !value) return value;
13605         var newValue = value;
13606         var m = value % inc;
13607         if(m > 0){
13608             if(m > (inc/2)){
13609                 newValue = value + (inc-m);
13610             }else{
13611                 newValue = value - m;
13612             }
13613         }
13614         return Math.max(min, newValue);
13615     },
13616
13617     // private
13618     resizeElement : function(){
13619         var box = this.proxy.getBox();
13620         if(this.updateBox){
13621             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13622         }else{
13623             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13624         }
13625         this.updateChildSize();
13626         if(!this.dynamic){
13627             this.proxy.hide();
13628         }
13629         return box;
13630     },
13631
13632     // private
13633     constrain : function(v, diff, m, mx){
13634         if(v - diff < m){
13635             diff = v - m;
13636         }else if(v - diff > mx){
13637             diff = mx - v;
13638         }
13639         return diff;
13640     },
13641
13642     // private
13643     onMouseMove : function(e){
13644         if(this.enabled){
13645             try{// try catch so if something goes wrong the user doesn't get hung
13646
13647             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13648                 return;
13649             }
13650
13651             //var curXY = this.startPoint;
13652             var curSize = this.curSize || this.startBox;
13653             var x = this.startBox.x, y = this.startBox.y;
13654             var ox = x, oy = y;
13655             var w = curSize.width, h = curSize.height;
13656             var ow = w, oh = h;
13657             var mw = this.minWidth, mh = this.minHeight;
13658             var mxw = this.maxWidth, mxh = this.maxHeight;
13659             var wi = this.widthIncrement;
13660             var hi = this.heightIncrement;
13661
13662             var eventXY = e.getXY();
13663             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13664             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13665
13666             var pos = this.activeHandle.position;
13667
13668             switch(pos){
13669                 case "east":
13670                     w += diffX;
13671                     w = Math.min(Math.max(mw, w), mxw);
13672                     break;
13673              
13674                 case "south":
13675                     h += diffY;
13676                     h = Math.min(Math.max(mh, h), mxh);
13677                     break;
13678                 case "southeast":
13679                     w += diffX;
13680                     h += diffY;
13681                     w = Math.min(Math.max(mw, w), mxw);
13682                     h = Math.min(Math.max(mh, h), mxh);
13683                     break;
13684                 case "north":
13685                     diffY = this.constrain(h, diffY, mh, mxh);
13686                     y += diffY;
13687                     h -= diffY;
13688                     break;
13689                 case "hdrag":
13690                     
13691                     if (wi) {
13692                         var adiffX = Math.abs(diffX);
13693                         var sub = (adiffX % wi); // how much 
13694                         if (sub > (wi/2)) { // far enough to snap
13695                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13696                         } else {
13697                             // remove difference.. 
13698                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13699                         }
13700                     }
13701                     x += diffX;
13702                     x = Math.max(this.minX, x);
13703                     break;
13704                 case "west":
13705                     diffX = this.constrain(w, diffX, mw, mxw);
13706                     x += diffX;
13707                     w -= diffX;
13708                     break;
13709                 case "northeast":
13710                     w += diffX;
13711                     w = Math.min(Math.max(mw, w), mxw);
13712                     diffY = this.constrain(h, diffY, mh, mxh);
13713                     y += diffY;
13714                     h -= diffY;
13715                     break;
13716                 case "northwest":
13717                     diffX = this.constrain(w, diffX, mw, mxw);
13718                     diffY = this.constrain(h, diffY, mh, mxh);
13719                     y += diffY;
13720                     h -= diffY;
13721                     x += diffX;
13722                     w -= diffX;
13723                     break;
13724                case "southwest":
13725                     diffX = this.constrain(w, diffX, mw, mxw);
13726                     h += diffY;
13727                     h = Math.min(Math.max(mh, h), mxh);
13728                     x += diffX;
13729                     w -= diffX;
13730                     break;
13731             }
13732
13733             var sw = this.snap(w, wi, mw);
13734             var sh = this.snap(h, hi, mh);
13735             if(sw != w || sh != h){
13736                 switch(pos){
13737                     case "northeast":
13738                         y -= sh - h;
13739                     break;
13740                     case "north":
13741                         y -= sh - h;
13742                         break;
13743                     case "southwest":
13744                         x -= sw - w;
13745                     break;
13746                     case "west":
13747                         x -= sw - w;
13748                         break;
13749                     case "northwest":
13750                         x -= sw - w;
13751                         y -= sh - h;
13752                     break;
13753                 }
13754                 w = sw;
13755                 h = sh;
13756             }
13757
13758             if(this.preserveRatio){
13759                 switch(pos){
13760                     case "southeast":
13761                     case "east":
13762                         h = oh * (w/ow);
13763                         h = Math.min(Math.max(mh, h), mxh);
13764                         w = ow * (h/oh);
13765                        break;
13766                     case "south":
13767                         w = ow * (h/oh);
13768                         w = Math.min(Math.max(mw, w), mxw);
13769                         h = oh * (w/ow);
13770                         break;
13771                     case "northeast":
13772                         w = ow * (h/oh);
13773                         w = Math.min(Math.max(mw, w), mxw);
13774                         h = oh * (w/ow);
13775                     break;
13776                     case "north":
13777                         var tw = w;
13778                         w = ow * (h/oh);
13779                         w = Math.min(Math.max(mw, w), mxw);
13780                         h = oh * (w/ow);
13781                         x += (tw - w) / 2;
13782                         break;
13783                     case "southwest":
13784                         h = oh * (w/ow);
13785                         h = Math.min(Math.max(mh, h), mxh);
13786                         var tw = w;
13787                         w = ow * (h/oh);
13788                         x += tw - w;
13789                         break;
13790                     case "west":
13791                         var th = h;
13792                         h = oh * (w/ow);
13793                         h = Math.min(Math.max(mh, h), mxh);
13794                         y += (th - h) / 2;
13795                         var tw = w;
13796                         w = ow * (h/oh);
13797                         x += tw - w;
13798                        break;
13799                     case "northwest":
13800                         var tw = w;
13801                         var th = h;
13802                         h = oh * (w/ow);
13803                         h = Math.min(Math.max(mh, h), mxh);
13804                         w = ow * (h/oh);
13805                         y += th - h;
13806                         x += tw - w;
13807                        break;
13808
13809                 }
13810             }
13811             if (pos == 'hdrag') {
13812                 w = ow;
13813             }
13814             this.proxy.setBounds(x, y, w, h);
13815             if(this.dynamic){
13816                 this.resizeElement();
13817             }
13818             }catch(e){}
13819         }
13820     },
13821
13822     // private
13823     handleOver : function(){
13824         if(this.enabled){
13825             this.el.addClass("x-resizable-over");
13826         }
13827     },
13828
13829     // private
13830     handleOut : function(){
13831         if(!this.resizing){
13832             this.el.removeClass("x-resizable-over");
13833         }
13834     },
13835
13836     /**
13837      * Returns the element this component is bound to.
13838      * @return {Roo.Element}
13839      */
13840     getEl : function(){
13841         return this.el;
13842     },
13843
13844     /**
13845      * Returns the resizeChild element (or null).
13846      * @return {Roo.Element}
13847      */
13848     getResizeChild : function(){
13849         return this.resizeChild;
13850     },
13851
13852     /**
13853      * Destroys this resizable. If the element was wrapped and
13854      * removeEl is not true then the element remains.
13855      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13856      */
13857     destroy : function(removeEl){
13858         this.proxy.remove();
13859         if(this.overlay){
13860             this.overlay.removeAllListeners();
13861             this.overlay.remove();
13862         }
13863         var ps = Roo.Resizable.positions;
13864         for(var k in ps){
13865             if(typeof ps[k] != "function" && this[ps[k]]){
13866                 var h = this[ps[k]];
13867                 h.el.removeAllListeners();
13868                 h.el.remove();
13869             }
13870         }
13871         if(removeEl){
13872             this.el.update("");
13873             this.el.remove();
13874         }
13875     }
13876 });
13877
13878 // private
13879 // hash to map config positions to true positions
13880 Roo.Resizable.positions = {
13881     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13882     hd: "hdrag"
13883 };
13884
13885 // private
13886 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13887     if(!this.tpl){
13888         // only initialize the template if resizable is used
13889         var tpl = Roo.DomHelper.createTemplate(
13890             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13891         );
13892         tpl.compile();
13893         Roo.Resizable.Handle.prototype.tpl = tpl;
13894     }
13895     this.position = pos;
13896     this.rz = rz;
13897     // show north drag fro topdra
13898     var handlepos = pos == 'hdrag' ? 'north' : pos;
13899     
13900     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13901     if (pos == 'hdrag') {
13902         this.el.setStyle('cursor', 'pointer');
13903     }
13904     this.el.unselectable();
13905     if(transparent){
13906         this.el.setOpacity(0);
13907     }
13908     this.el.on("mousedown", this.onMouseDown, this);
13909     if(!disableTrackOver){
13910         this.el.on("mouseover", this.onMouseOver, this);
13911         this.el.on("mouseout", this.onMouseOut, this);
13912     }
13913 };
13914
13915 // private
13916 Roo.Resizable.Handle.prototype = {
13917     afterResize : function(rz){
13918         // do nothing
13919     },
13920     // private
13921     onMouseDown : function(e){
13922         this.rz.onMouseDown(this, e);
13923     },
13924     // private
13925     onMouseOver : function(e){
13926         this.rz.handleOver(this, e);
13927     },
13928     // private
13929     onMouseOut : function(e){
13930         this.rz.handleOut(this, e);
13931     }
13932 };/*
13933  * Based on:
13934  * Ext JS Library 1.1.1
13935  * Copyright(c) 2006-2007, Ext JS, LLC.
13936  *
13937  * Originally Released Under LGPL - original licence link has changed is not relivant.
13938  *
13939  * Fork - LGPL
13940  * <script type="text/javascript">
13941  */
13942
13943 /**
13944  * @class Roo.Editor
13945  * @extends Roo.Component
13946  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13947  * @constructor
13948  * Create a new Editor
13949  * @param {Roo.form.Field} field The Field object (or descendant)
13950  * @param {Object} config The config object
13951  */
13952 Roo.Editor = function(field, config){
13953     Roo.Editor.superclass.constructor.call(this, config);
13954     this.field = field;
13955     this.addEvents({
13956         /**
13957              * @event beforestartedit
13958              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13959              * false from the handler of this event.
13960              * @param {Editor} this
13961              * @param {Roo.Element} boundEl The underlying element bound to this editor
13962              * @param {Mixed} value The field value being set
13963              */
13964         "beforestartedit" : true,
13965         /**
13966              * @event startedit
13967              * Fires when this editor is displayed
13968              * @param {Roo.Element} boundEl The underlying element bound to this editor
13969              * @param {Mixed} value The starting field value
13970              */
13971         "startedit" : true,
13972         /**
13973              * @event beforecomplete
13974              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13975              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13976              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13977              * event will not fire since no edit actually occurred.
13978              * @param {Editor} this
13979              * @param {Mixed} value The current field value
13980              * @param {Mixed} startValue The original field value
13981              */
13982         "beforecomplete" : true,
13983         /**
13984              * @event complete
13985              * Fires after editing is complete and any changed value has been written to the underlying field.
13986              * @param {Editor} this
13987              * @param {Mixed} value The current field value
13988              * @param {Mixed} startValue The original field value
13989              */
13990         "complete" : true,
13991         /**
13992          * @event specialkey
13993          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13994          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13995          * @param {Roo.form.Field} this
13996          * @param {Roo.EventObject} e The event object
13997          */
13998         "specialkey" : true
13999     });
14000 };
14001
14002 Roo.extend(Roo.Editor, Roo.Component, {
14003     /**
14004      * @cfg {Boolean/String} autosize
14005      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14006      * or "height" to adopt the height only (defaults to false)
14007      */
14008     /**
14009      * @cfg {Boolean} revertInvalid
14010      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14011      * validation fails (defaults to true)
14012      */
14013     /**
14014      * @cfg {Boolean} ignoreNoChange
14015      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14016      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14017      * will never be ignored.
14018      */
14019     /**
14020      * @cfg {Boolean} hideEl
14021      * False to keep the bound element visible while the editor is displayed (defaults to true)
14022      */
14023     /**
14024      * @cfg {Mixed} value
14025      * The data value of the underlying field (defaults to "")
14026      */
14027     value : "",
14028     /**
14029      * @cfg {String} alignment
14030      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14031      */
14032     alignment: "c-c?",
14033     /**
14034      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14035      * for bottom-right shadow (defaults to "frame")
14036      */
14037     shadow : "frame",
14038     /**
14039      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14040      */
14041     constrain : false,
14042     /**
14043      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14044      */
14045     completeOnEnter : false,
14046     /**
14047      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14048      */
14049     cancelOnEsc : false,
14050     /**
14051      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14052      */
14053     updateEl : false,
14054
14055     // private
14056     onRender : function(ct, position){
14057         this.el = new Roo.Layer({
14058             shadow: this.shadow,
14059             cls: "x-editor",
14060             parentEl : ct,
14061             shim : this.shim,
14062             shadowOffset:4,
14063             id: this.id,
14064             constrain: this.constrain
14065         });
14066         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14067         if(this.field.msgTarget != 'title'){
14068             this.field.msgTarget = 'qtip';
14069         }
14070         this.field.render(this.el);
14071         if(Roo.isGecko){
14072             this.field.el.dom.setAttribute('autocomplete', 'off');
14073         }
14074         this.field.on("specialkey", this.onSpecialKey, this);
14075         if(this.swallowKeys){
14076             this.field.el.swallowEvent(['keydown','keypress']);
14077         }
14078         this.field.show();
14079         this.field.on("blur", this.onBlur, this);
14080         if(this.field.grow){
14081             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14082         }
14083     },
14084
14085     onSpecialKey : function(field, e)
14086     {
14087         //Roo.log('editor onSpecialKey');
14088         if(this.completeOnEnter && e.getKey() == e.ENTER){
14089             e.stopEvent();
14090             this.completeEdit();
14091             return;
14092         }
14093         // do not fire special key otherwise it might hide close the editor...
14094         if(e.getKey() == e.ENTER){    
14095             return;
14096         }
14097         if(this.cancelOnEsc && e.getKey() == e.ESC){
14098             this.cancelEdit();
14099             return;
14100         } 
14101         this.fireEvent('specialkey', field, e);
14102     
14103     },
14104
14105     /**
14106      * Starts the editing process and shows the editor.
14107      * @param {String/HTMLElement/Element} el The element to edit
14108      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14109       * to the innerHTML of el.
14110      */
14111     startEdit : function(el, value){
14112         if(this.editing){
14113             this.completeEdit();
14114         }
14115         this.boundEl = Roo.get(el);
14116         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14117         if(!this.rendered){
14118             this.render(this.parentEl || document.body);
14119         }
14120         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14121             return;
14122         }
14123         this.startValue = v;
14124         this.field.setValue(v);
14125         if(this.autoSize){
14126             var sz = this.boundEl.getSize();
14127             switch(this.autoSize){
14128                 case "width":
14129                 this.setSize(sz.width,  "");
14130                 break;
14131                 case "height":
14132                 this.setSize("",  sz.height);
14133                 break;
14134                 default:
14135                 this.setSize(sz.width,  sz.height);
14136             }
14137         }
14138         this.el.alignTo(this.boundEl, this.alignment);
14139         this.editing = true;
14140         if(Roo.QuickTips){
14141             Roo.QuickTips.disable();
14142         }
14143         this.show();
14144     },
14145
14146     /**
14147      * Sets the height and width of this editor.
14148      * @param {Number} width The new width
14149      * @param {Number} height The new height
14150      */
14151     setSize : function(w, h){
14152         this.field.setSize(w, h);
14153         if(this.el){
14154             this.el.sync();
14155         }
14156     },
14157
14158     /**
14159      * Realigns the editor to the bound field based on the current alignment config value.
14160      */
14161     realign : function(){
14162         this.el.alignTo(this.boundEl, this.alignment);
14163     },
14164
14165     /**
14166      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14167      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14168      */
14169     completeEdit : function(remainVisible){
14170         if(!this.editing){
14171             return;
14172         }
14173         var v = this.getValue();
14174         if(this.revertInvalid !== false && !this.field.isValid()){
14175             v = this.startValue;
14176             this.cancelEdit(true);
14177         }
14178         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14179             this.editing = false;
14180             this.hide();
14181             return;
14182         }
14183         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14184             this.editing = false;
14185             if(this.updateEl && this.boundEl){
14186                 this.boundEl.update(v);
14187             }
14188             if(remainVisible !== true){
14189                 this.hide();
14190             }
14191             this.fireEvent("complete", this, v, this.startValue);
14192         }
14193     },
14194
14195     // private
14196     onShow : function(){
14197         this.el.show();
14198         if(this.hideEl !== false){
14199             this.boundEl.hide();
14200         }
14201         this.field.show();
14202         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14203             this.fixIEFocus = true;
14204             this.deferredFocus.defer(50, this);
14205         }else{
14206             this.field.focus();
14207         }
14208         this.fireEvent("startedit", this.boundEl, this.startValue);
14209     },
14210
14211     deferredFocus : function(){
14212         if(this.editing){
14213             this.field.focus();
14214         }
14215     },
14216
14217     /**
14218      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14219      * reverted to the original starting value.
14220      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14221      * cancel (defaults to false)
14222      */
14223     cancelEdit : function(remainVisible){
14224         if(this.editing){
14225             this.setValue(this.startValue);
14226             if(remainVisible !== true){
14227                 this.hide();
14228             }
14229         }
14230     },
14231
14232     // private
14233     onBlur : function(){
14234         if(this.allowBlur !== true && this.editing){
14235             this.completeEdit();
14236         }
14237     },
14238
14239     // private
14240     onHide : function(){
14241         if(this.editing){
14242             this.completeEdit();
14243             return;
14244         }
14245         this.field.blur();
14246         if(this.field.collapse){
14247             this.field.collapse();
14248         }
14249         this.el.hide();
14250         if(this.hideEl !== false){
14251             this.boundEl.show();
14252         }
14253         if(Roo.QuickTips){
14254             Roo.QuickTips.enable();
14255         }
14256     },
14257
14258     /**
14259      * Sets the data value of the editor
14260      * @param {Mixed} value Any valid value supported by the underlying field
14261      */
14262     setValue : function(v){
14263         this.field.setValue(v);
14264     },
14265
14266     /**
14267      * Gets the data value of the editor
14268      * @return {Mixed} The data value
14269      */
14270     getValue : function(){
14271         return this.field.getValue();
14272     }
14273 });/*
14274  * Based on:
14275  * Ext JS Library 1.1.1
14276  * Copyright(c) 2006-2007, Ext JS, LLC.
14277  *
14278  * Originally Released Under LGPL - original licence link has changed is not relivant.
14279  *
14280  * Fork - LGPL
14281  * <script type="text/javascript">
14282  */
14283  
14284 /**
14285  * @class Roo.BasicDialog
14286  * @extends Roo.util.Observable
14287  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14288  * <pre><code>
14289 var dlg = new Roo.BasicDialog("my-dlg", {
14290     height: 200,
14291     width: 300,
14292     minHeight: 100,
14293     minWidth: 150,
14294     modal: true,
14295     proxyDrag: true,
14296     shadow: true
14297 });
14298 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14299 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14300 dlg.addButton('Cancel', dlg.hide, dlg);
14301 dlg.show();
14302 </code></pre>
14303   <b>A Dialog should always be a direct child of the body element.</b>
14304  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14305  * @cfg {String} title Default text to display in the title bar (defaults to null)
14306  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14307  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14308  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14309  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14310  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14311  * (defaults to null with no animation)
14312  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14313  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14314  * property for valid values (defaults to 'all')
14315  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14316  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14317  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14318  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14319  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14320  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14321  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14322  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14323  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14324  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14325  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14326  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14327  * draggable = true (defaults to false)
14328  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14329  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14330  * shadow (defaults to false)
14331  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14332  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14333  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14334  * @cfg {Array} buttons Array of buttons
14335  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14336  * @constructor
14337  * Create a new BasicDialog.
14338  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14339  * @param {Object} config Configuration options
14340  */
14341 Roo.BasicDialog = function(el, config){
14342     this.el = Roo.get(el);
14343     var dh = Roo.DomHelper;
14344     if(!this.el && config && config.autoCreate){
14345         if(typeof config.autoCreate == "object"){
14346             if(!config.autoCreate.id){
14347                 config.autoCreate.id = el;
14348             }
14349             this.el = dh.append(document.body,
14350                         config.autoCreate, true);
14351         }else{
14352             this.el = dh.append(document.body,
14353                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14354         }
14355     }
14356     el = this.el;
14357     el.setDisplayed(true);
14358     el.hide = this.hideAction;
14359     this.id = el.id;
14360     el.addClass("x-dlg");
14361
14362     Roo.apply(this, config);
14363
14364     this.proxy = el.createProxy("x-dlg-proxy");
14365     this.proxy.hide = this.hideAction;
14366     this.proxy.setOpacity(.5);
14367     this.proxy.hide();
14368
14369     if(config.width){
14370         el.setWidth(config.width);
14371     }
14372     if(config.height){
14373         el.setHeight(config.height);
14374     }
14375     this.size = el.getSize();
14376     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14377         this.xy = [config.x,config.y];
14378     }else{
14379         this.xy = el.getCenterXY(true);
14380     }
14381     /** The header element @type Roo.Element */
14382     this.header = el.child("> .x-dlg-hd");
14383     /** The body element @type Roo.Element */
14384     this.body = el.child("> .x-dlg-bd");
14385     /** The footer element @type Roo.Element */
14386     this.footer = el.child("> .x-dlg-ft");
14387
14388     if(!this.header){
14389         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14390     }
14391     if(!this.body){
14392         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14393     }
14394
14395     this.header.unselectable();
14396     if(this.title){
14397         this.header.update(this.title);
14398     }
14399     // this element allows the dialog to be focused for keyboard event
14400     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14401     this.focusEl.swallowEvent("click", true);
14402
14403     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14404
14405     // wrap the body and footer for special rendering
14406     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14407     if(this.footer){
14408         this.bwrap.dom.appendChild(this.footer.dom);
14409     }
14410
14411     this.bg = this.el.createChild({
14412         tag: "div", cls:"x-dlg-bg",
14413         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14414     });
14415     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14416
14417
14418     if(this.autoScroll !== false && !this.autoTabs){
14419         this.body.setStyle("overflow", "auto");
14420     }
14421
14422     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14423
14424     if(this.closable !== false){
14425         this.el.addClass("x-dlg-closable");
14426         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14427         this.close.on("click", this.closeClick, this);
14428         this.close.addClassOnOver("x-dlg-close-over");
14429     }
14430     if(this.collapsible !== false){
14431         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14432         this.collapseBtn.on("click", this.collapseClick, this);
14433         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14434         this.header.on("dblclick", this.collapseClick, this);
14435     }
14436     if(this.resizable !== false){
14437         this.el.addClass("x-dlg-resizable");
14438         this.resizer = new Roo.Resizable(el, {
14439             minWidth: this.minWidth || 80,
14440             minHeight:this.minHeight || 80,
14441             handles: this.resizeHandles || "all",
14442             pinned: true
14443         });
14444         this.resizer.on("beforeresize", this.beforeResize, this);
14445         this.resizer.on("resize", this.onResize, this);
14446     }
14447     if(this.draggable !== false){
14448         el.addClass("x-dlg-draggable");
14449         if (!this.proxyDrag) {
14450             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14451         }
14452         else {
14453             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14454         }
14455         dd.setHandleElId(this.header.id);
14456         dd.endDrag = this.endMove.createDelegate(this);
14457         dd.startDrag = this.startMove.createDelegate(this);
14458         dd.onDrag = this.onDrag.createDelegate(this);
14459         dd.scroll = false;
14460         this.dd = dd;
14461     }
14462     if(this.modal){
14463         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14464         this.mask.enableDisplayMode("block");
14465         this.mask.hide();
14466         this.el.addClass("x-dlg-modal");
14467     }
14468     if(this.shadow){
14469         this.shadow = new Roo.Shadow({
14470             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14471             offset : this.shadowOffset
14472         });
14473     }else{
14474         this.shadowOffset = 0;
14475     }
14476     if(Roo.useShims && this.shim !== false){
14477         this.shim = this.el.createShim();
14478         this.shim.hide = this.hideAction;
14479         this.shim.hide();
14480     }else{
14481         this.shim = false;
14482     }
14483     if(this.autoTabs){
14484         this.initTabs();
14485     }
14486     if (this.buttons) { 
14487         var bts= this.buttons;
14488         this.buttons = [];
14489         Roo.each(bts, function(b) {
14490             this.addButton(b);
14491         }, this);
14492     }
14493     
14494     
14495     this.addEvents({
14496         /**
14497          * @event keydown
14498          * Fires when a key is pressed
14499          * @param {Roo.BasicDialog} this
14500          * @param {Roo.EventObject} e
14501          */
14502         "keydown" : true,
14503         /**
14504          * @event move
14505          * Fires when this dialog is moved by the user.
14506          * @param {Roo.BasicDialog} this
14507          * @param {Number} x The new page X
14508          * @param {Number} y The new page Y
14509          */
14510         "move" : true,
14511         /**
14512          * @event resize
14513          * Fires when this dialog is resized by the user.
14514          * @param {Roo.BasicDialog} this
14515          * @param {Number} width The new width
14516          * @param {Number} height The new height
14517          */
14518         "resize" : true,
14519         /**
14520          * @event beforehide
14521          * Fires before this dialog is hidden.
14522          * @param {Roo.BasicDialog} this
14523          */
14524         "beforehide" : true,
14525         /**
14526          * @event hide
14527          * Fires when this dialog is hidden.
14528          * @param {Roo.BasicDialog} this
14529          */
14530         "hide" : true,
14531         /**
14532          * @event beforeshow
14533          * Fires before this dialog is shown.
14534          * @param {Roo.BasicDialog} this
14535          */
14536         "beforeshow" : true,
14537         /**
14538          * @event show
14539          * Fires when this dialog is shown.
14540          * @param {Roo.BasicDialog} this
14541          */
14542         "show" : true
14543     });
14544     el.on("keydown", this.onKeyDown, this);
14545     el.on("mousedown", this.toFront, this);
14546     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14547     this.el.hide();
14548     Roo.DialogManager.register(this);
14549     Roo.BasicDialog.superclass.constructor.call(this);
14550 };
14551
14552 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14553     shadowOffset: Roo.isIE ? 6 : 5,
14554     minHeight: 80,
14555     minWidth: 200,
14556     minButtonWidth: 75,
14557     defaultButton: null,
14558     buttonAlign: "right",
14559     tabTag: 'div',
14560     firstShow: true,
14561
14562     /**
14563      * Sets the dialog title text
14564      * @param {String} text The title text to display
14565      * @return {Roo.BasicDialog} this
14566      */
14567     setTitle : function(text){
14568         this.header.update(text);
14569         return this;
14570     },
14571
14572     // private
14573     closeClick : function(){
14574         this.hide();
14575     },
14576
14577     // private
14578     collapseClick : function(){
14579         this[this.collapsed ? "expand" : "collapse"]();
14580     },
14581
14582     /**
14583      * Collapses the dialog to its minimized state (only the title bar is visible).
14584      * Equivalent to the user clicking the collapse dialog button.
14585      */
14586     collapse : function(){
14587         if(!this.collapsed){
14588             this.collapsed = true;
14589             this.el.addClass("x-dlg-collapsed");
14590             this.restoreHeight = this.el.getHeight();
14591             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14592         }
14593     },
14594
14595     /**
14596      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14597      * clicking the expand dialog button.
14598      */
14599     expand : function(){
14600         if(this.collapsed){
14601             this.collapsed = false;
14602             this.el.removeClass("x-dlg-collapsed");
14603             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14604         }
14605     },
14606
14607     /**
14608      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14609      * @return {Roo.TabPanel} The tabs component
14610      */
14611     initTabs : function(){
14612         var tabs = this.getTabs();
14613         while(tabs.getTab(0)){
14614             tabs.removeTab(0);
14615         }
14616         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14617             var dom = el.dom;
14618             tabs.addTab(Roo.id(dom), dom.title);
14619             dom.title = "";
14620         });
14621         tabs.activate(0);
14622         return tabs;
14623     },
14624
14625     // private
14626     beforeResize : function(){
14627         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14628     },
14629
14630     // private
14631     onResize : function(){
14632         this.refreshSize();
14633         this.syncBodyHeight();
14634         this.adjustAssets();
14635         this.focus();
14636         this.fireEvent("resize", this, this.size.width, this.size.height);
14637     },
14638
14639     // private
14640     onKeyDown : function(e){
14641         if(this.isVisible()){
14642             this.fireEvent("keydown", this, e);
14643         }
14644     },
14645
14646     /**
14647      * Resizes the dialog.
14648      * @param {Number} width
14649      * @param {Number} height
14650      * @return {Roo.BasicDialog} this
14651      */
14652     resizeTo : function(width, height){
14653         this.el.setSize(width, height);
14654         this.size = {width: width, height: height};
14655         this.syncBodyHeight();
14656         if(this.fixedcenter){
14657             this.center();
14658         }
14659         if(this.isVisible()){
14660             this.constrainXY();
14661             this.adjustAssets();
14662         }
14663         this.fireEvent("resize", this, width, height);
14664         return this;
14665     },
14666
14667
14668     /**
14669      * Resizes the dialog to fit the specified content size.
14670      * @param {Number} width
14671      * @param {Number} height
14672      * @return {Roo.BasicDialog} this
14673      */
14674     setContentSize : function(w, h){
14675         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14676         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14677         //if(!this.el.isBorderBox()){
14678             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14679             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14680         //}
14681         if(this.tabs){
14682             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14683             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14684         }
14685         this.resizeTo(w, h);
14686         return this;
14687     },
14688
14689     /**
14690      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14691      * executed in response to a particular key being pressed while the dialog is active.
14692      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14693      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14694      * @param {Function} fn The function to call
14695      * @param {Object} scope (optional) The scope of the function
14696      * @return {Roo.BasicDialog} this
14697      */
14698     addKeyListener : function(key, fn, scope){
14699         var keyCode, shift, ctrl, alt;
14700         if(typeof key == "object" && !(key instanceof Array)){
14701             keyCode = key["key"];
14702             shift = key["shift"];
14703             ctrl = key["ctrl"];
14704             alt = key["alt"];
14705         }else{
14706             keyCode = key;
14707         }
14708         var handler = function(dlg, e){
14709             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14710                 var k = e.getKey();
14711                 if(keyCode instanceof Array){
14712                     for(var i = 0, len = keyCode.length; i < len; i++){
14713                         if(keyCode[i] == k){
14714                           fn.call(scope || window, dlg, k, e);
14715                           return;
14716                         }
14717                     }
14718                 }else{
14719                     if(k == keyCode){
14720                         fn.call(scope || window, dlg, k, e);
14721                     }
14722                 }
14723             }
14724         };
14725         this.on("keydown", handler);
14726         return this;
14727     },
14728
14729     /**
14730      * Returns the TabPanel component (creates it if it doesn't exist).
14731      * Note: If you wish to simply check for the existence of tabs without creating them,
14732      * check for a null 'tabs' property.
14733      * @return {Roo.TabPanel} The tabs component
14734      */
14735     getTabs : function(){
14736         if(!this.tabs){
14737             this.el.addClass("x-dlg-auto-tabs");
14738             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14739             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14740         }
14741         return this.tabs;
14742     },
14743
14744     /**
14745      * Adds a button to the footer section of the dialog.
14746      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14747      * object or a valid Roo.DomHelper element config
14748      * @param {Function} handler The function called when the button is clicked
14749      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14750      * @return {Roo.Button} The new button
14751      */
14752     addButton : function(config, handler, scope){
14753         var dh = Roo.DomHelper;
14754         if(!this.footer){
14755             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14756         }
14757         if(!this.btnContainer){
14758             var tb = this.footer.createChild({
14759
14760                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14761                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14762             }, null, true);
14763             this.btnContainer = tb.firstChild.firstChild.firstChild;
14764         }
14765         var bconfig = {
14766             handler: handler,
14767             scope: scope,
14768             minWidth: this.minButtonWidth,
14769             hideParent:true
14770         };
14771         if(typeof config == "string"){
14772             bconfig.text = config;
14773         }else{
14774             if(config.tag){
14775                 bconfig.dhconfig = config;
14776             }else{
14777                 Roo.apply(bconfig, config);
14778             }
14779         }
14780         var fc = false;
14781         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14782             bconfig.position = Math.max(0, bconfig.position);
14783             fc = this.btnContainer.childNodes[bconfig.position];
14784         }
14785          
14786         var btn = new Roo.Button(
14787             fc ? 
14788                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14789                 : this.btnContainer.appendChild(document.createElement("td")),
14790             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14791             bconfig
14792         );
14793         this.syncBodyHeight();
14794         if(!this.buttons){
14795             /**
14796              * Array of all the buttons that have been added to this dialog via addButton
14797              * @type Array
14798              */
14799             this.buttons = [];
14800         }
14801         this.buttons.push(btn);
14802         return btn;
14803     },
14804
14805     /**
14806      * Sets the default button to be focused when the dialog is displayed.
14807      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14808      * @return {Roo.BasicDialog} this
14809      */
14810     setDefaultButton : function(btn){
14811         this.defaultButton = btn;
14812         return this;
14813     },
14814
14815     // private
14816     getHeaderFooterHeight : function(safe){
14817         var height = 0;
14818         if(this.header){
14819            height += this.header.getHeight();
14820         }
14821         if(this.footer){
14822            var fm = this.footer.getMargins();
14823             height += (this.footer.getHeight()+fm.top+fm.bottom);
14824         }
14825         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14826         height += this.centerBg.getPadding("tb");
14827         return height;
14828     },
14829
14830     // private
14831     syncBodyHeight : function(){
14832         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14833         var height = this.size.height - this.getHeaderFooterHeight(false);
14834         bd.setHeight(height-bd.getMargins("tb"));
14835         var hh = this.header.getHeight();
14836         var h = this.size.height-hh;
14837         cb.setHeight(h);
14838         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14839         bw.setHeight(h-cb.getPadding("tb"));
14840         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14841         bd.setWidth(bw.getWidth(true));
14842         if(this.tabs){
14843             this.tabs.syncHeight();
14844             if(Roo.isIE){
14845                 this.tabs.el.repaint();
14846             }
14847         }
14848     },
14849
14850     /**
14851      * Restores the previous state of the dialog if Roo.state is configured.
14852      * @return {Roo.BasicDialog} this
14853      */
14854     restoreState : function(){
14855         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14856         if(box && box.width){
14857             this.xy = [box.x, box.y];
14858             this.resizeTo(box.width, box.height);
14859         }
14860         return this;
14861     },
14862
14863     // private
14864     beforeShow : function(){
14865         this.expand();
14866         if(this.fixedcenter){
14867             this.xy = this.el.getCenterXY(true);
14868         }
14869         if(this.modal){
14870             Roo.get(document.body).addClass("x-body-masked");
14871             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14872             this.mask.show();
14873         }
14874         this.constrainXY();
14875     },
14876
14877     // private
14878     animShow : function(){
14879         var b = Roo.get(this.animateTarget).getBox();
14880         this.proxy.setSize(b.width, b.height);
14881         this.proxy.setLocation(b.x, b.y);
14882         this.proxy.show();
14883         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14884                     true, .35, this.showEl.createDelegate(this));
14885     },
14886
14887     /**
14888      * Shows the dialog.
14889      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14890      * @return {Roo.BasicDialog} this
14891      */
14892     show : function(animateTarget){
14893         if (this.fireEvent("beforeshow", this) === false){
14894             return;
14895         }
14896         if(this.syncHeightBeforeShow){
14897             this.syncBodyHeight();
14898         }else if(this.firstShow){
14899             this.firstShow = false;
14900             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14901         }
14902         this.animateTarget = animateTarget || this.animateTarget;
14903         if(!this.el.isVisible()){
14904             this.beforeShow();
14905             if(this.animateTarget && Roo.get(this.animateTarget)){
14906                 this.animShow();
14907             }else{
14908                 this.showEl();
14909             }
14910         }
14911         return this;
14912     },
14913
14914     // private
14915     showEl : function(){
14916         this.proxy.hide();
14917         this.el.setXY(this.xy);
14918         this.el.show();
14919         this.adjustAssets(true);
14920         this.toFront();
14921         this.focus();
14922         // IE peekaboo bug - fix found by Dave Fenwick
14923         if(Roo.isIE){
14924             this.el.repaint();
14925         }
14926         this.fireEvent("show", this);
14927     },
14928
14929     /**
14930      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14931      * dialog itself will receive focus.
14932      */
14933     focus : function(){
14934         if(this.defaultButton){
14935             this.defaultButton.focus();
14936         }else{
14937             this.focusEl.focus();
14938         }
14939     },
14940
14941     // private
14942     constrainXY : function(){
14943         if(this.constraintoviewport !== false){
14944             if(!this.viewSize){
14945                 if(this.container){
14946                     var s = this.container.getSize();
14947                     this.viewSize = [s.width, s.height];
14948                 }else{
14949                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14950                 }
14951             }
14952             var s = Roo.get(this.container||document).getScroll();
14953
14954             var x = this.xy[0], y = this.xy[1];
14955             var w = this.size.width, h = this.size.height;
14956             var vw = this.viewSize[0], vh = this.viewSize[1];
14957             // only move it if it needs it
14958             var moved = false;
14959             // first validate right/bottom
14960             if(x + w > vw+s.left){
14961                 x = vw - w;
14962                 moved = true;
14963             }
14964             if(y + h > vh+s.top){
14965                 y = vh - h;
14966                 moved = true;
14967             }
14968             // then make sure top/left isn't negative
14969             if(x < s.left){
14970                 x = s.left;
14971                 moved = true;
14972             }
14973             if(y < s.top){
14974                 y = s.top;
14975                 moved = true;
14976             }
14977             if(moved){
14978                 // cache xy
14979                 this.xy = [x, y];
14980                 if(this.isVisible()){
14981                     this.el.setLocation(x, y);
14982                     this.adjustAssets();
14983                 }
14984             }
14985         }
14986     },
14987
14988     // private
14989     onDrag : function(){
14990         if(!this.proxyDrag){
14991             this.xy = this.el.getXY();
14992             this.adjustAssets();
14993         }
14994     },
14995
14996     // private
14997     adjustAssets : function(doShow){
14998         var x = this.xy[0], y = this.xy[1];
14999         var w = this.size.width, h = this.size.height;
15000         if(doShow === true){
15001             if(this.shadow){
15002                 this.shadow.show(this.el);
15003             }
15004             if(this.shim){
15005                 this.shim.show();
15006             }
15007         }
15008         if(this.shadow && this.shadow.isVisible()){
15009             this.shadow.show(this.el);
15010         }
15011         if(this.shim && this.shim.isVisible()){
15012             this.shim.setBounds(x, y, w, h);
15013         }
15014     },
15015
15016     // private
15017     adjustViewport : function(w, h){
15018         if(!w || !h){
15019             w = Roo.lib.Dom.getViewWidth();
15020             h = Roo.lib.Dom.getViewHeight();
15021         }
15022         // cache the size
15023         this.viewSize = [w, h];
15024         if(this.modal && this.mask.isVisible()){
15025             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15026             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15027         }
15028         if(this.isVisible()){
15029             this.constrainXY();
15030         }
15031     },
15032
15033     /**
15034      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15035      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15036      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15037      */
15038     destroy : function(removeEl){
15039         if(this.isVisible()){
15040             this.animateTarget = null;
15041             this.hide();
15042         }
15043         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15044         if(this.tabs){
15045             this.tabs.destroy(removeEl);
15046         }
15047         Roo.destroy(
15048              this.shim,
15049              this.proxy,
15050              this.resizer,
15051              this.close,
15052              this.mask
15053         );
15054         if(this.dd){
15055             this.dd.unreg();
15056         }
15057         if(this.buttons){
15058            for(var i = 0, len = this.buttons.length; i < len; i++){
15059                this.buttons[i].destroy();
15060            }
15061         }
15062         this.el.removeAllListeners();
15063         if(removeEl === true){
15064             this.el.update("");
15065             this.el.remove();
15066         }
15067         Roo.DialogManager.unregister(this);
15068     },
15069
15070     // private
15071     startMove : function(){
15072         if(this.proxyDrag){
15073             this.proxy.show();
15074         }
15075         if(this.constraintoviewport !== false){
15076             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15077         }
15078     },
15079
15080     // private
15081     endMove : function(){
15082         if(!this.proxyDrag){
15083             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15084         }else{
15085             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15086             this.proxy.hide();
15087         }
15088         this.refreshSize();
15089         this.adjustAssets();
15090         this.focus();
15091         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15092     },
15093
15094     /**
15095      * Brings this dialog to the front of any other visible dialogs
15096      * @return {Roo.BasicDialog} this
15097      */
15098     toFront : function(){
15099         Roo.DialogManager.bringToFront(this);
15100         return this;
15101     },
15102
15103     /**
15104      * Sends this dialog to the back (under) of any other visible dialogs
15105      * @return {Roo.BasicDialog} this
15106      */
15107     toBack : function(){
15108         Roo.DialogManager.sendToBack(this);
15109         return this;
15110     },
15111
15112     /**
15113      * Centers this dialog in the viewport
15114      * @return {Roo.BasicDialog} this
15115      */
15116     center : function(){
15117         var xy = this.el.getCenterXY(true);
15118         this.moveTo(xy[0], xy[1]);
15119         return this;
15120     },
15121
15122     /**
15123      * Moves the dialog's top-left corner to the specified point
15124      * @param {Number} x
15125      * @param {Number} y
15126      * @return {Roo.BasicDialog} this
15127      */
15128     moveTo : function(x, y){
15129         this.xy = [x,y];
15130         if(this.isVisible()){
15131             this.el.setXY(this.xy);
15132             this.adjustAssets();
15133         }
15134         return this;
15135     },
15136
15137     /**
15138      * Aligns the dialog to the specified element
15139      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15140      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15141      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15142      * @return {Roo.BasicDialog} this
15143      */
15144     alignTo : function(element, position, offsets){
15145         this.xy = this.el.getAlignToXY(element, position, offsets);
15146         if(this.isVisible()){
15147             this.el.setXY(this.xy);
15148             this.adjustAssets();
15149         }
15150         return this;
15151     },
15152
15153     /**
15154      * Anchors an element to another element and realigns it when the window is resized.
15155      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15156      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15157      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15158      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15159      * is a number, it is used as the buffer delay (defaults to 50ms).
15160      * @return {Roo.BasicDialog} this
15161      */
15162     anchorTo : function(el, alignment, offsets, monitorScroll){
15163         var action = function(){
15164             this.alignTo(el, alignment, offsets);
15165         };
15166         Roo.EventManager.onWindowResize(action, this);
15167         var tm = typeof monitorScroll;
15168         if(tm != 'undefined'){
15169             Roo.EventManager.on(window, 'scroll', action, this,
15170                 {buffer: tm == 'number' ? monitorScroll : 50});
15171         }
15172         action.call(this);
15173         return this;
15174     },
15175
15176     /**
15177      * Returns true if the dialog is visible
15178      * @return {Boolean}
15179      */
15180     isVisible : function(){
15181         return this.el.isVisible();
15182     },
15183
15184     // private
15185     animHide : function(callback){
15186         var b = Roo.get(this.animateTarget).getBox();
15187         this.proxy.show();
15188         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15189         this.el.hide();
15190         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15191                     this.hideEl.createDelegate(this, [callback]));
15192     },
15193
15194     /**
15195      * Hides the dialog.
15196      * @param {Function} callback (optional) Function to call when the dialog is hidden
15197      * @return {Roo.BasicDialog} this
15198      */
15199     hide : function(callback){
15200         if (this.fireEvent("beforehide", this) === false){
15201             return;
15202         }
15203         if(this.shadow){
15204             this.shadow.hide();
15205         }
15206         if(this.shim) {
15207           this.shim.hide();
15208         }
15209         // sometimes animateTarget seems to get set.. causing problems...
15210         // this just double checks..
15211         if(this.animateTarget && Roo.get(this.animateTarget)) {
15212            this.animHide(callback);
15213         }else{
15214             this.el.hide();
15215             this.hideEl(callback);
15216         }
15217         return this;
15218     },
15219
15220     // private
15221     hideEl : function(callback){
15222         this.proxy.hide();
15223         if(this.modal){
15224             this.mask.hide();
15225             Roo.get(document.body).removeClass("x-body-masked");
15226         }
15227         this.fireEvent("hide", this);
15228         if(typeof callback == "function"){
15229             callback();
15230         }
15231     },
15232
15233     // private
15234     hideAction : function(){
15235         this.setLeft("-10000px");
15236         this.setTop("-10000px");
15237         this.setStyle("visibility", "hidden");
15238     },
15239
15240     // private
15241     refreshSize : function(){
15242         this.size = this.el.getSize();
15243         this.xy = this.el.getXY();
15244         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15245     },
15246
15247     // private
15248     // z-index is managed by the DialogManager and may be overwritten at any time
15249     setZIndex : function(index){
15250         if(this.modal){
15251             this.mask.setStyle("z-index", index);
15252         }
15253         if(this.shim){
15254             this.shim.setStyle("z-index", ++index);
15255         }
15256         if(this.shadow){
15257             this.shadow.setZIndex(++index);
15258         }
15259         this.el.setStyle("z-index", ++index);
15260         if(this.proxy){
15261             this.proxy.setStyle("z-index", ++index);
15262         }
15263         if(this.resizer){
15264             this.resizer.proxy.setStyle("z-index", ++index);
15265         }
15266
15267         this.lastZIndex = index;
15268     },
15269
15270     /**
15271      * Returns the element for this dialog
15272      * @return {Roo.Element} The underlying dialog Element
15273      */
15274     getEl : function(){
15275         return this.el;
15276     }
15277 });
15278
15279 /**
15280  * @class Roo.DialogManager
15281  * Provides global access to BasicDialogs that have been created and
15282  * support for z-indexing (layering) multiple open dialogs.
15283  */
15284 Roo.DialogManager = function(){
15285     var list = {};
15286     var accessList = [];
15287     var front = null;
15288
15289     // private
15290     var sortDialogs = function(d1, d2){
15291         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15292     };
15293
15294     // private
15295     var orderDialogs = function(){
15296         accessList.sort(sortDialogs);
15297         var seed = Roo.DialogManager.zseed;
15298         for(var i = 0, len = accessList.length; i < len; i++){
15299             var dlg = accessList[i];
15300             if(dlg){
15301                 dlg.setZIndex(seed + (i*10));
15302             }
15303         }
15304     };
15305
15306     return {
15307         /**
15308          * The starting z-index for BasicDialogs (defaults to 9000)
15309          * @type Number The z-index value
15310          */
15311         zseed : 9000,
15312
15313         // private
15314         register : function(dlg){
15315             list[dlg.id] = dlg;
15316             accessList.push(dlg);
15317         },
15318
15319         // private
15320         unregister : function(dlg){
15321             delete list[dlg.id];
15322             var i=0;
15323             var len=0;
15324             if(!accessList.indexOf){
15325                 for(  i = 0, len = accessList.length; i < len; i++){
15326                     if(accessList[i] == dlg){
15327                         accessList.splice(i, 1);
15328                         return;
15329                     }
15330                 }
15331             }else{
15332                  i = accessList.indexOf(dlg);
15333                 if(i != -1){
15334                     accessList.splice(i, 1);
15335                 }
15336             }
15337         },
15338
15339         /**
15340          * Gets a registered dialog by id
15341          * @param {String/Object} id The id of the dialog or a dialog
15342          * @return {Roo.BasicDialog} this
15343          */
15344         get : function(id){
15345             return typeof id == "object" ? id : list[id];
15346         },
15347
15348         /**
15349          * Brings the specified dialog to the front
15350          * @param {String/Object} dlg The id of the dialog or a dialog
15351          * @return {Roo.BasicDialog} this
15352          */
15353         bringToFront : function(dlg){
15354             dlg = this.get(dlg);
15355             if(dlg != front){
15356                 front = dlg;
15357                 dlg._lastAccess = new Date().getTime();
15358                 orderDialogs();
15359             }
15360             return dlg;
15361         },
15362
15363         /**
15364          * Sends the specified dialog to the back
15365          * @param {String/Object} dlg The id of the dialog or a dialog
15366          * @return {Roo.BasicDialog} this
15367          */
15368         sendToBack : function(dlg){
15369             dlg = this.get(dlg);
15370             dlg._lastAccess = -(new Date().getTime());
15371             orderDialogs();
15372             return dlg;
15373         },
15374
15375         /**
15376          * Hides all dialogs
15377          */
15378         hideAll : function(){
15379             for(var id in list){
15380                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15381                     list[id].hide();
15382                 }
15383             }
15384         }
15385     };
15386 }();
15387
15388 /**
15389  * @class Roo.LayoutDialog
15390  * @extends Roo.BasicDialog
15391  * Dialog which provides adjustments for working with a layout in a Dialog.
15392  * Add your necessary layout config options to the dialog's config.<br>
15393  * Example usage (including a nested layout):
15394  * <pre><code>
15395 if(!dialog){
15396     dialog = new Roo.LayoutDialog("download-dlg", {
15397         modal: true,
15398         width:600,
15399         height:450,
15400         shadow:true,
15401         minWidth:500,
15402         minHeight:350,
15403         autoTabs:true,
15404         proxyDrag:true,
15405         // layout config merges with the dialog config
15406         center:{
15407             tabPosition: "top",
15408             alwaysShowTabs: true
15409         }
15410     });
15411     dialog.addKeyListener(27, dialog.hide, dialog);
15412     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15413     dialog.addButton("Build It!", this.getDownload, this);
15414
15415     // we can even add nested layouts
15416     var innerLayout = new Roo.BorderLayout("dl-inner", {
15417         east: {
15418             initialSize: 200,
15419             autoScroll:true,
15420             split:true
15421         },
15422         center: {
15423             autoScroll:true
15424         }
15425     });
15426     innerLayout.beginUpdate();
15427     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15428     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15429     innerLayout.endUpdate(true);
15430
15431     var layout = dialog.getLayout();
15432     layout.beginUpdate();
15433     layout.add("center", new Roo.ContentPanel("standard-panel",
15434                         {title: "Download the Source", fitToFrame:true}));
15435     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15436                {title: "Build your own roo.js"}));
15437     layout.getRegion("center").showPanel(sp);
15438     layout.endUpdate();
15439 }
15440 </code></pre>
15441     * @constructor
15442     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15443     * @param {Object} config configuration options
15444   */
15445 Roo.LayoutDialog = function(el, cfg){
15446     
15447     var config=  cfg;
15448     if (typeof(cfg) == 'undefined') {
15449         config = Roo.apply({}, el);
15450         // not sure why we use documentElement here.. - it should always be body.
15451         // IE7 borks horribly if we use documentElement.
15452         // webkit also does not like documentElement - it creates a body element...
15453         el = Roo.get( document.body || document.documentElement ).createChild();
15454         //config.autoCreate = true;
15455     }
15456     
15457     
15458     config.autoTabs = false;
15459     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15460     this.body.setStyle({overflow:"hidden", position:"relative"});
15461     this.layout = new Roo.BorderLayout(this.body.dom, config);
15462     this.layout.monitorWindowResize = false;
15463     this.el.addClass("x-dlg-auto-layout");
15464     // fix case when center region overwrites center function
15465     this.center = Roo.BasicDialog.prototype.center;
15466     this.on("show", this.layout.layout, this.layout, true);
15467     if (config.items) {
15468         var xitems = config.items;
15469         delete config.items;
15470         Roo.each(xitems, this.addxtype, this);
15471     }
15472     
15473     
15474 };
15475 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15476     /**
15477      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15478      * @deprecated
15479      */
15480     endUpdate : function(){
15481         this.layout.endUpdate();
15482     },
15483
15484     /**
15485      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15486      *  @deprecated
15487      */
15488     beginUpdate : function(){
15489         this.layout.beginUpdate();
15490     },
15491
15492     /**
15493      * Get the BorderLayout for this dialog
15494      * @return {Roo.BorderLayout}
15495      */
15496     getLayout : function(){
15497         return this.layout;
15498     },
15499
15500     showEl : function(){
15501         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15502         if(Roo.isIE7){
15503             this.layout.layout();
15504         }
15505     },
15506
15507     // private
15508     // Use the syncHeightBeforeShow config option to control this automatically
15509     syncBodyHeight : function(){
15510         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15511         if(this.layout){this.layout.layout();}
15512     },
15513     
15514       /**
15515      * Add an xtype element (actually adds to the layout.)
15516      * @return {Object} xdata xtype object data.
15517      */
15518     
15519     addxtype : function(c) {
15520         return this.layout.addxtype(c);
15521     }
15522 });/*
15523  * Based on:
15524  * Ext JS Library 1.1.1
15525  * Copyright(c) 2006-2007, Ext JS, LLC.
15526  *
15527  * Originally Released Under LGPL - original licence link has changed is not relivant.
15528  *
15529  * Fork - LGPL
15530  * <script type="text/javascript">
15531  */
15532  
15533 /**
15534  * @class Roo.MessageBox
15535  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15536  * Example usage:
15537  *<pre><code>
15538 // Basic alert:
15539 Roo.Msg.alert('Status', 'Changes saved successfully.');
15540
15541 // Prompt for user data:
15542 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15543     if (btn == 'ok'){
15544         // process text value...
15545     }
15546 });
15547
15548 // Show a dialog using config options:
15549 Roo.Msg.show({
15550    title:'Save Changes?',
15551    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15552    buttons: Roo.Msg.YESNOCANCEL,
15553    fn: processResult,
15554    animEl: 'elId'
15555 });
15556 </code></pre>
15557  * @singleton
15558  */
15559 Roo.MessageBox = function(){
15560     var dlg, opt, mask, waitTimer;
15561     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15562     var buttons, activeTextEl, bwidth;
15563
15564     // private
15565     var handleButton = function(button){
15566         dlg.hide();
15567         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15568     };
15569
15570     // private
15571     var handleHide = function(){
15572         if(opt && opt.cls){
15573             dlg.el.removeClass(opt.cls);
15574         }
15575         if(waitTimer){
15576             Roo.TaskMgr.stop(waitTimer);
15577             waitTimer = null;
15578         }
15579     };
15580
15581     // private
15582     var updateButtons = function(b){
15583         var width = 0;
15584         if(!b){
15585             buttons["ok"].hide();
15586             buttons["cancel"].hide();
15587             buttons["yes"].hide();
15588             buttons["no"].hide();
15589             dlg.footer.dom.style.display = 'none';
15590             return width;
15591         }
15592         dlg.footer.dom.style.display = '';
15593         for(var k in buttons){
15594             if(typeof buttons[k] != "function"){
15595                 if(b[k]){
15596                     buttons[k].show();
15597                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15598                     width += buttons[k].el.getWidth()+15;
15599                 }else{
15600                     buttons[k].hide();
15601                 }
15602             }
15603         }
15604         return width;
15605     };
15606
15607     // private
15608     var handleEsc = function(d, k, e){
15609         if(opt && opt.closable !== false){
15610             dlg.hide();
15611         }
15612         if(e){
15613             e.stopEvent();
15614         }
15615     };
15616
15617     return {
15618         /**
15619          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15620          * @return {Roo.BasicDialog} The BasicDialog element
15621          */
15622         getDialog : function(){
15623            if(!dlg){
15624                 dlg = new Roo.BasicDialog("x-msg-box", {
15625                     autoCreate : true,
15626                     shadow: true,
15627                     draggable: true,
15628                     resizable:false,
15629                     constraintoviewport:false,
15630                     fixedcenter:true,
15631                     collapsible : false,
15632                     shim:true,
15633                     modal: true,
15634                     width:400, height:100,
15635                     buttonAlign:"center",
15636                     closeClick : function(){
15637                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15638                             handleButton("no");
15639                         }else{
15640                             handleButton("cancel");
15641                         }
15642                     }
15643                 });
15644                 dlg.on("hide", handleHide);
15645                 mask = dlg.mask;
15646                 dlg.addKeyListener(27, handleEsc);
15647                 buttons = {};
15648                 var bt = this.buttonText;
15649                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15650                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15651                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15652                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15653                 bodyEl = dlg.body.createChild({
15654
15655                     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>'
15656                 });
15657                 msgEl = bodyEl.dom.firstChild;
15658                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15659                 textboxEl.enableDisplayMode();
15660                 textboxEl.addKeyListener([10,13], function(){
15661                     if(dlg.isVisible() && opt && opt.buttons){
15662                         if(opt.buttons.ok){
15663                             handleButton("ok");
15664                         }else if(opt.buttons.yes){
15665                             handleButton("yes");
15666                         }
15667                     }
15668                 });
15669                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15670                 textareaEl.enableDisplayMode();
15671                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15672                 progressEl.enableDisplayMode();
15673                 var pf = progressEl.dom.firstChild;
15674                 if (pf) {
15675                     pp = Roo.get(pf.firstChild);
15676                     pp.setHeight(pf.offsetHeight);
15677                 }
15678                 
15679             }
15680             return dlg;
15681         },
15682
15683         /**
15684          * Updates the message box body text
15685          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15686          * the XHTML-compliant non-breaking space character '&amp;#160;')
15687          * @return {Roo.MessageBox} This message box
15688          */
15689         updateText : function(text){
15690             if(!dlg.isVisible() && !opt.width){
15691                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15692             }
15693             msgEl.innerHTML = text || '&#160;';
15694       
15695             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15696             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15697             var w = Math.max(
15698                     Math.min(opt.width || cw , this.maxWidth), 
15699                     Math.max(opt.minWidth || this.minWidth, bwidth)
15700             );
15701             if(opt.prompt){
15702                 activeTextEl.setWidth(w);
15703             }
15704             if(dlg.isVisible()){
15705                 dlg.fixedcenter = false;
15706             }
15707             // to big, make it scroll. = But as usual stupid IE does not support
15708             // !important..
15709             
15710             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15711                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15712                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15713             } else {
15714                 bodyEl.dom.style.height = '';
15715                 bodyEl.dom.style.overflowY = '';
15716             }
15717             if (cw > w) {
15718                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15719             } else {
15720                 bodyEl.dom.style.overflowX = '';
15721             }
15722             
15723             dlg.setContentSize(w, bodyEl.getHeight());
15724             if(dlg.isVisible()){
15725                 dlg.fixedcenter = true;
15726             }
15727             return this;
15728         },
15729
15730         /**
15731          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15732          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15733          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15734          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15735          * @return {Roo.MessageBox} This message box
15736          */
15737         updateProgress : function(value, text){
15738             if(text){
15739                 this.updateText(text);
15740             }
15741             if (pp) { // weird bug on my firefox - for some reason this is not defined
15742                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15743             }
15744             return this;
15745         },        
15746
15747         /**
15748          * Returns true if the message box is currently displayed
15749          * @return {Boolean} True if the message box is visible, else false
15750          */
15751         isVisible : function(){
15752             return dlg && dlg.isVisible();  
15753         },
15754
15755         /**
15756          * Hides the message box if it is displayed
15757          */
15758         hide : function(){
15759             if(this.isVisible()){
15760                 dlg.hide();
15761             }  
15762         },
15763
15764         /**
15765          * Displays a new message box, or reinitializes an existing message box, based on the config options
15766          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15767          * The following config object properties are supported:
15768          * <pre>
15769 Property    Type             Description
15770 ----------  ---------------  ------------------------------------------------------------------------------------
15771 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15772                                    closes (defaults to undefined)
15773 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15774                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15775 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15776                                    progress and wait dialogs will ignore this property and always hide the
15777                                    close button as they can only be closed programmatically.
15778 cls               String           A custom CSS class to apply to the message box element
15779 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15780                                    displayed (defaults to 75)
15781 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15782                                    function will be btn (the name of the button that was clicked, if applicable,
15783                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15784                                    Progress and wait dialogs will ignore this option since they do not respond to
15785                                    user actions and can only be closed programmatically, so any required function
15786                                    should be called by the same code after it closes the dialog.
15787 icon              String           A CSS class that provides a background image to be used as an icon for
15788                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15789 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15790 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15791 modal             Boolean          False to allow user interaction with the page while the message box is
15792                                    displayed (defaults to true)
15793 msg               String           A string that will replace the existing message box body text (defaults
15794                                    to the XHTML-compliant non-breaking space character '&#160;')
15795 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15796 progress          Boolean          True to display a progress bar (defaults to false)
15797 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15798 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15799 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15800 title             String           The title text
15801 value             String           The string value to set into the active textbox element if displayed
15802 wait              Boolean          True to display a progress bar (defaults to false)
15803 width             Number           The width of the dialog in pixels
15804 </pre>
15805          *
15806          * Example usage:
15807          * <pre><code>
15808 Roo.Msg.show({
15809    title: 'Address',
15810    msg: 'Please enter your address:',
15811    width: 300,
15812    buttons: Roo.MessageBox.OKCANCEL,
15813    multiline: true,
15814    fn: saveAddress,
15815    animEl: 'addAddressBtn'
15816 });
15817 </code></pre>
15818          * @param {Object} config Configuration options
15819          * @return {Roo.MessageBox} This message box
15820          */
15821         show : function(options)
15822         {
15823             
15824             // this causes nightmares if you show one dialog after another
15825             // especially on callbacks..
15826              
15827             if(this.isVisible()){
15828                 
15829                 this.hide();
15830                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15831                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15832                 Roo.log("New Dialog Message:" +  options.msg )
15833                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15834                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15835                 
15836             }
15837             var d = this.getDialog();
15838             opt = options;
15839             d.setTitle(opt.title || "&#160;");
15840             d.close.setDisplayed(opt.closable !== false);
15841             activeTextEl = textboxEl;
15842             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15843             if(opt.prompt){
15844                 if(opt.multiline){
15845                     textboxEl.hide();
15846                     textareaEl.show();
15847                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15848                         opt.multiline : this.defaultTextHeight);
15849                     activeTextEl = textareaEl;
15850                 }else{
15851                     textboxEl.show();
15852                     textareaEl.hide();
15853                 }
15854             }else{
15855                 textboxEl.hide();
15856                 textareaEl.hide();
15857             }
15858             progressEl.setDisplayed(opt.progress === true);
15859             this.updateProgress(0);
15860             activeTextEl.dom.value = opt.value || "";
15861             if(opt.prompt){
15862                 dlg.setDefaultButton(activeTextEl);
15863             }else{
15864                 var bs = opt.buttons;
15865                 var db = null;
15866                 if(bs && bs.ok){
15867                     db = buttons["ok"];
15868                 }else if(bs && bs.yes){
15869                     db = buttons["yes"];
15870                 }
15871                 dlg.setDefaultButton(db);
15872             }
15873             bwidth = updateButtons(opt.buttons);
15874             this.updateText(opt.msg);
15875             if(opt.cls){
15876                 d.el.addClass(opt.cls);
15877             }
15878             d.proxyDrag = opt.proxyDrag === true;
15879             d.modal = opt.modal !== false;
15880             d.mask = opt.modal !== false ? mask : false;
15881             if(!d.isVisible()){
15882                 // force it to the end of the z-index stack so it gets a cursor in FF
15883                 document.body.appendChild(dlg.el.dom);
15884                 d.animateTarget = null;
15885                 d.show(options.animEl);
15886             }
15887             return this;
15888         },
15889
15890         /**
15891          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15892          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15893          * and closing the message box when the process is complete.
15894          * @param {String} title The title bar text
15895          * @param {String} msg The message box body text
15896          * @return {Roo.MessageBox} This message box
15897          */
15898         progress : function(title, msg){
15899             this.show({
15900                 title : title,
15901                 msg : msg,
15902                 buttons: false,
15903                 progress:true,
15904                 closable:false,
15905                 minWidth: this.minProgressWidth,
15906                 modal : true
15907             });
15908             return this;
15909         },
15910
15911         /**
15912          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15913          * If a callback function is passed it will be called after the user clicks the button, and the
15914          * id of the button that was clicked will be passed as the only parameter to the callback
15915          * (could also be the top-right close button).
15916          * @param {String} title The title bar text
15917          * @param {String} msg The message box body text
15918          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15919          * @param {Object} scope (optional) The scope of the callback function
15920          * @return {Roo.MessageBox} This message box
15921          */
15922         alert : function(title, msg, fn, scope){
15923             this.show({
15924                 title : title,
15925                 msg : msg,
15926                 buttons: this.OK,
15927                 fn: fn,
15928                 scope : scope,
15929                 modal : true
15930             });
15931             return this;
15932         },
15933
15934         /**
15935          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15936          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15937          * You are responsible for closing the message box when the process is complete.
15938          * @param {String} msg The message box body text
15939          * @param {String} title (optional) The title bar text
15940          * @return {Roo.MessageBox} This message box
15941          */
15942         wait : function(msg, title){
15943             this.show({
15944                 title : title,
15945                 msg : msg,
15946                 buttons: false,
15947                 closable:false,
15948                 progress:true,
15949                 modal:true,
15950                 width:300,
15951                 wait:true
15952             });
15953             waitTimer = Roo.TaskMgr.start({
15954                 run: function(i){
15955                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15956                 },
15957                 interval: 1000
15958             });
15959             return this;
15960         },
15961
15962         /**
15963          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15964          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15965          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15966          * @param {String} title The title bar text
15967          * @param {String} msg The message box body text
15968          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15969          * @param {Object} scope (optional) The scope of the callback function
15970          * @return {Roo.MessageBox} This message box
15971          */
15972         confirm : function(title, msg, fn, scope){
15973             this.show({
15974                 title : title,
15975                 msg : msg,
15976                 buttons: this.YESNO,
15977                 fn: fn,
15978                 scope : scope,
15979                 modal : true
15980             });
15981             return this;
15982         },
15983
15984         /**
15985          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15986          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15987          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15988          * (could also be the top-right close button) and the text that was entered will be passed as the two
15989          * parameters to the callback.
15990          * @param {String} title The title bar text
15991          * @param {String} msg The message box body text
15992          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15993          * @param {Object} scope (optional) The scope of the callback function
15994          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15995          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15996          * @return {Roo.MessageBox} This message box
15997          */
15998         prompt : function(title, msg, fn, scope, multiline){
15999             this.show({
16000                 title : title,
16001                 msg : msg,
16002                 buttons: this.OKCANCEL,
16003                 fn: fn,
16004                 minWidth:250,
16005                 scope : scope,
16006                 prompt:true,
16007                 multiline: multiline,
16008                 modal : true
16009             });
16010             return this;
16011         },
16012
16013         /**
16014          * Button config that displays a single OK button
16015          * @type Object
16016          */
16017         OK : {ok:true},
16018         /**
16019          * Button config that displays Yes and No buttons
16020          * @type Object
16021          */
16022         YESNO : {yes:true, no:true},
16023         /**
16024          * Button config that displays OK and Cancel buttons
16025          * @type Object
16026          */
16027         OKCANCEL : {ok:true, cancel:true},
16028         /**
16029          * Button config that displays Yes, No and Cancel buttons
16030          * @type Object
16031          */
16032         YESNOCANCEL : {yes:true, no:true, cancel:true},
16033
16034         /**
16035          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16036          * @type Number
16037          */
16038         defaultTextHeight : 75,
16039         /**
16040          * The maximum width in pixels of the message box (defaults to 600)
16041          * @type Number
16042          */
16043         maxWidth : 600,
16044         /**
16045          * The minimum width in pixels of the message box (defaults to 100)
16046          * @type Number
16047          */
16048         minWidth : 100,
16049         /**
16050          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16051          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16052          * @type Number
16053          */
16054         minProgressWidth : 250,
16055         /**
16056          * An object containing the default button text strings that can be overriden for localized language support.
16057          * Supported properties are: ok, cancel, yes and no.
16058          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16059          * @type Object
16060          */
16061         buttonText : {
16062             ok : "OK",
16063             cancel : "Cancel",
16064             yes : "Yes",
16065             no : "No"
16066         }
16067     };
16068 }();
16069
16070 /**
16071  * Shorthand for {@link Roo.MessageBox}
16072  */
16073 Roo.Msg = Roo.MessageBox;/*
16074  * Based on:
16075  * Ext JS Library 1.1.1
16076  * Copyright(c) 2006-2007, Ext JS, LLC.
16077  *
16078  * Originally Released Under LGPL - original licence link has changed is not relivant.
16079  *
16080  * Fork - LGPL
16081  * <script type="text/javascript">
16082  */
16083 /**
16084  * @class Roo.QuickTips
16085  * Provides attractive and customizable tooltips for any element.
16086  * @singleton
16087  */
16088 Roo.QuickTips = function(){
16089     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16090     var ce, bd, xy, dd;
16091     var visible = false, disabled = true, inited = false;
16092     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16093     
16094     var onOver = function(e){
16095         if(disabled){
16096             return;
16097         }
16098         var t = e.getTarget();
16099         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16100             return;
16101         }
16102         if(ce && t == ce.el){
16103             clearTimeout(hideProc);
16104             return;
16105         }
16106         if(t && tagEls[t.id]){
16107             tagEls[t.id].el = t;
16108             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16109             return;
16110         }
16111         var ttp, et = Roo.fly(t);
16112         var ns = cfg.namespace;
16113         if(tm.interceptTitles && t.title){
16114             ttp = t.title;
16115             t.qtip = ttp;
16116             t.removeAttribute("title");
16117             e.preventDefault();
16118         }else{
16119             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16120         }
16121         if(ttp){
16122             showProc = show.defer(tm.showDelay, tm, [{
16123                 el: t, 
16124                 text: ttp, 
16125                 width: et.getAttributeNS(ns, cfg.width),
16126                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16127                 title: et.getAttributeNS(ns, cfg.title),
16128                     cls: et.getAttributeNS(ns, cfg.cls)
16129             }]);
16130         }
16131     };
16132     
16133     var onOut = function(e){
16134         clearTimeout(showProc);
16135         var t = e.getTarget();
16136         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16137             hideProc = setTimeout(hide, tm.hideDelay);
16138         }
16139     };
16140     
16141     var onMove = function(e){
16142         if(disabled){
16143             return;
16144         }
16145         xy = e.getXY();
16146         xy[1] += 18;
16147         if(tm.trackMouse && ce){
16148             el.setXY(xy);
16149         }
16150     };
16151     
16152     var onDown = function(e){
16153         clearTimeout(showProc);
16154         clearTimeout(hideProc);
16155         if(!e.within(el)){
16156             if(tm.hideOnClick){
16157                 hide();
16158                 tm.disable();
16159                 tm.enable.defer(100, tm);
16160             }
16161         }
16162     };
16163     
16164     var getPad = function(){
16165         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16166     };
16167
16168     var show = function(o){
16169         if(disabled){
16170             return;
16171         }
16172         clearTimeout(dismissProc);
16173         ce = o;
16174         if(removeCls){ // in case manually hidden
16175             el.removeClass(removeCls);
16176             removeCls = null;
16177         }
16178         if(ce.cls){
16179             el.addClass(ce.cls);
16180             removeCls = ce.cls;
16181         }
16182         if(ce.title){
16183             tipTitle.update(ce.title);
16184             tipTitle.show();
16185         }else{
16186             tipTitle.update('');
16187             tipTitle.hide();
16188         }
16189         el.dom.style.width  = tm.maxWidth+'px';
16190         //tipBody.dom.style.width = '';
16191         tipBodyText.update(o.text);
16192         var p = getPad(), w = ce.width;
16193         if(!w){
16194             var td = tipBodyText.dom;
16195             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16196             if(aw > tm.maxWidth){
16197                 w = tm.maxWidth;
16198             }else if(aw < tm.minWidth){
16199                 w = tm.minWidth;
16200             }else{
16201                 w = aw;
16202             }
16203         }
16204         //tipBody.setWidth(w);
16205         el.setWidth(parseInt(w, 10) + p);
16206         if(ce.autoHide === false){
16207             close.setDisplayed(true);
16208             if(dd){
16209                 dd.unlock();
16210             }
16211         }else{
16212             close.setDisplayed(false);
16213             if(dd){
16214                 dd.lock();
16215             }
16216         }
16217         if(xy){
16218             el.avoidY = xy[1]-18;
16219             el.setXY(xy);
16220         }
16221         if(tm.animate){
16222             el.setOpacity(.1);
16223             el.setStyle("visibility", "visible");
16224             el.fadeIn({callback: afterShow});
16225         }else{
16226             afterShow();
16227         }
16228     };
16229     
16230     var afterShow = function(){
16231         if(ce){
16232             el.show();
16233             esc.enable();
16234             if(tm.autoDismiss && ce.autoHide !== false){
16235                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16236             }
16237         }
16238     };
16239     
16240     var hide = function(noanim){
16241         clearTimeout(dismissProc);
16242         clearTimeout(hideProc);
16243         ce = null;
16244         if(el.isVisible()){
16245             esc.disable();
16246             if(noanim !== true && tm.animate){
16247                 el.fadeOut({callback: afterHide});
16248             }else{
16249                 afterHide();
16250             } 
16251         }
16252     };
16253     
16254     var afterHide = function(){
16255         el.hide();
16256         if(removeCls){
16257             el.removeClass(removeCls);
16258             removeCls = null;
16259         }
16260     };
16261     
16262     return {
16263         /**
16264         * @cfg {Number} minWidth
16265         * The minimum width of the quick tip (defaults to 40)
16266         */
16267        minWidth : 40,
16268         /**
16269         * @cfg {Number} maxWidth
16270         * The maximum width of the quick tip (defaults to 300)
16271         */
16272        maxWidth : 300,
16273         /**
16274         * @cfg {Boolean} interceptTitles
16275         * True to automatically use the element's DOM title value if available (defaults to false)
16276         */
16277        interceptTitles : false,
16278         /**
16279         * @cfg {Boolean} trackMouse
16280         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16281         */
16282        trackMouse : false,
16283         /**
16284         * @cfg {Boolean} hideOnClick
16285         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16286         */
16287        hideOnClick : true,
16288         /**
16289         * @cfg {Number} showDelay
16290         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16291         */
16292        showDelay : 500,
16293         /**
16294         * @cfg {Number} hideDelay
16295         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16296         */
16297        hideDelay : 200,
16298         /**
16299         * @cfg {Boolean} autoHide
16300         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16301         * Used in conjunction with hideDelay.
16302         */
16303        autoHide : true,
16304         /**
16305         * @cfg {Boolean}
16306         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16307         * (defaults to true).  Used in conjunction with autoDismissDelay.
16308         */
16309        autoDismiss : true,
16310         /**
16311         * @cfg {Number}
16312         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16313         */
16314        autoDismissDelay : 5000,
16315        /**
16316         * @cfg {Boolean} animate
16317         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16318         */
16319        animate : false,
16320
16321        /**
16322         * @cfg {String} title
16323         * Title text to display (defaults to '').  This can be any valid HTML markup.
16324         */
16325         title: '',
16326        /**
16327         * @cfg {String} text
16328         * Body text to display (defaults to '').  This can be any valid HTML markup.
16329         */
16330         text : '',
16331        /**
16332         * @cfg {String} cls
16333         * A CSS class to apply to the base quick tip element (defaults to '').
16334         */
16335         cls : '',
16336        /**
16337         * @cfg {Number} width
16338         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16339         * minWidth or maxWidth.
16340         */
16341         width : null,
16342
16343     /**
16344      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16345      * or display QuickTips in a page.
16346      */
16347        init : function(){
16348           tm = Roo.QuickTips;
16349           cfg = tm.tagConfig;
16350           if(!inited){
16351               if(!Roo.isReady){ // allow calling of init() before onReady
16352                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16353                   return;
16354               }
16355               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16356               el.fxDefaults = {stopFx: true};
16357               // maximum custom styling
16358               //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>');
16359               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>');              
16360               tipTitle = el.child('h3');
16361               tipTitle.enableDisplayMode("block");
16362               tipBody = el.child('div.x-tip-bd');
16363               tipBodyText = el.child('div.x-tip-bd-inner');
16364               //bdLeft = el.child('div.x-tip-bd-left');
16365               //bdRight = el.child('div.x-tip-bd-right');
16366               close = el.child('div.x-tip-close');
16367               close.enableDisplayMode("block");
16368               close.on("click", hide);
16369               var d = Roo.get(document);
16370               d.on("mousedown", onDown);
16371               d.on("mouseover", onOver);
16372               d.on("mouseout", onOut);
16373               d.on("mousemove", onMove);
16374               esc = d.addKeyListener(27, hide);
16375               esc.disable();
16376               if(Roo.dd.DD){
16377                   dd = el.initDD("default", null, {
16378                       onDrag : function(){
16379                           el.sync();  
16380                       }
16381                   });
16382                   dd.setHandleElId(tipTitle.id);
16383                   dd.lock();
16384               }
16385               inited = true;
16386           }
16387           this.enable(); 
16388        },
16389
16390     /**
16391      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16392      * are supported:
16393      * <pre>
16394 Property    Type                   Description
16395 ----------  ---------------------  ------------------------------------------------------------------------
16396 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16397      * </ul>
16398      * @param {Object} config The config object
16399      */
16400        register : function(config){
16401            var cs = config instanceof Array ? config : arguments;
16402            for(var i = 0, len = cs.length; i < len; i++) {
16403                var c = cs[i];
16404                var target = c.target;
16405                if(target){
16406                    if(target instanceof Array){
16407                        for(var j = 0, jlen = target.length; j < jlen; j++){
16408                            tagEls[target[j]] = c;
16409                        }
16410                    }else{
16411                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16412                    }
16413                }
16414            }
16415        },
16416
16417     /**
16418      * Removes this quick tip from its element and destroys it.
16419      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16420      */
16421        unregister : function(el){
16422            delete tagEls[Roo.id(el)];
16423        },
16424
16425     /**
16426      * Enable this quick tip.
16427      */
16428        enable : function(){
16429            if(inited && disabled){
16430                locks.pop();
16431                if(locks.length < 1){
16432                    disabled = false;
16433                }
16434            }
16435        },
16436
16437     /**
16438      * Disable this quick tip.
16439      */
16440        disable : function(){
16441           disabled = true;
16442           clearTimeout(showProc);
16443           clearTimeout(hideProc);
16444           clearTimeout(dismissProc);
16445           if(ce){
16446               hide(true);
16447           }
16448           locks.push(1);
16449        },
16450
16451     /**
16452      * Returns true if the quick tip is enabled, else false.
16453      */
16454        isEnabled : function(){
16455             return !disabled;
16456        },
16457
16458         // private
16459        tagConfig : {
16460            namespace : "ext",
16461            attribute : "qtip",
16462            width : "width",
16463            target : "target",
16464            title : "qtitle",
16465            hide : "hide",
16466            cls : "qclass"
16467        }
16468    };
16469 }();
16470
16471 // backwards compat
16472 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16473  * Based on:
16474  * Ext JS Library 1.1.1
16475  * Copyright(c) 2006-2007, Ext JS, LLC.
16476  *
16477  * Originally Released Under LGPL - original licence link has changed is not relivant.
16478  *
16479  * Fork - LGPL
16480  * <script type="text/javascript">
16481  */
16482  
16483
16484 /**
16485  * @class Roo.tree.TreePanel
16486  * @extends Roo.data.Tree
16487
16488  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16489  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16490  * @cfg {Boolean} enableDD true to enable drag and drop
16491  * @cfg {Boolean} enableDrag true to enable just drag
16492  * @cfg {Boolean} enableDrop true to enable just drop
16493  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16494  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16495  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16496  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16497  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16498  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16499  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16500  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16501  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16502  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16503  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16504  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16505  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16506  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16507  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16508  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16509  * 
16510  * @constructor
16511  * @param {String/HTMLElement/Element} el The container element
16512  * @param {Object} config
16513  */
16514 Roo.tree.TreePanel = function(el, config){
16515     var root = false;
16516     var loader = false;
16517     if (config.root) {
16518         root = config.root;
16519         delete config.root;
16520     }
16521     if (config.loader) {
16522         loader = config.loader;
16523         delete config.loader;
16524     }
16525     
16526     Roo.apply(this, config);
16527     Roo.tree.TreePanel.superclass.constructor.call(this);
16528     this.el = Roo.get(el);
16529     this.el.addClass('x-tree');
16530     //console.log(root);
16531     if (root) {
16532         this.setRootNode( Roo.factory(root, Roo.tree));
16533     }
16534     if (loader) {
16535         this.loader = Roo.factory(loader, Roo.tree);
16536     }
16537    /**
16538     * Read-only. The id of the container element becomes this TreePanel's id.
16539     */
16540     this.id = this.el.id;
16541     this.addEvents({
16542         /**
16543         * @event beforeload
16544         * Fires before a node is loaded, return false to cancel
16545         * @param {Node} node The node being loaded
16546         */
16547         "beforeload" : true,
16548         /**
16549         * @event load
16550         * Fires when a node is loaded
16551         * @param {Node} node The node that was loaded
16552         */
16553         "load" : true,
16554         /**
16555         * @event textchange
16556         * Fires when the text for a node is changed
16557         * @param {Node} node The node
16558         * @param {String} text The new text
16559         * @param {String} oldText The old text
16560         */
16561         "textchange" : true,
16562         /**
16563         * @event beforeexpand
16564         * Fires before a node is expanded, return false to cancel.
16565         * @param {Node} node The node
16566         * @param {Boolean} deep
16567         * @param {Boolean} anim
16568         */
16569         "beforeexpand" : true,
16570         /**
16571         * @event beforecollapse
16572         * Fires before a node is collapsed, return false to cancel.
16573         * @param {Node} node The node
16574         * @param {Boolean} deep
16575         * @param {Boolean} anim
16576         */
16577         "beforecollapse" : true,
16578         /**
16579         * @event expand
16580         * Fires when a node is expanded
16581         * @param {Node} node The node
16582         */
16583         "expand" : true,
16584         /**
16585         * @event disabledchange
16586         * Fires when the disabled status of a node changes
16587         * @param {Node} node The node
16588         * @param {Boolean} disabled
16589         */
16590         "disabledchange" : true,
16591         /**
16592         * @event collapse
16593         * Fires when a node is collapsed
16594         * @param {Node} node The node
16595         */
16596         "collapse" : true,
16597         /**
16598         * @event beforeclick
16599         * Fires before click processing on a node. Return false to cancel the default action.
16600         * @param {Node} node The node
16601         * @param {Roo.EventObject} e The event object
16602         */
16603         "beforeclick":true,
16604         /**
16605         * @event checkchange
16606         * Fires when a node with a checkbox's checked property changes
16607         * @param {Node} this This node
16608         * @param {Boolean} checked
16609         */
16610         "checkchange":true,
16611         /**
16612         * @event click
16613         * Fires when a node is clicked
16614         * @param {Node} node The node
16615         * @param {Roo.EventObject} e The event object
16616         */
16617         "click":true,
16618         /**
16619         * @event dblclick
16620         * Fires when a node is double clicked
16621         * @param {Node} node The node
16622         * @param {Roo.EventObject} e The event object
16623         */
16624         "dblclick":true,
16625         /**
16626         * @event contextmenu
16627         * Fires when a node is right clicked
16628         * @param {Node} node The node
16629         * @param {Roo.EventObject} e The event object
16630         */
16631         "contextmenu":true,
16632         /**
16633         * @event beforechildrenrendered
16634         * Fires right before the child nodes for a node are rendered
16635         * @param {Node} node The node
16636         */
16637         "beforechildrenrendered":true,
16638         /**
16639         * @event startdrag
16640         * Fires when a node starts being dragged
16641         * @param {Roo.tree.TreePanel} this
16642         * @param {Roo.tree.TreeNode} node
16643         * @param {event} e The raw browser event
16644         */ 
16645        "startdrag" : true,
16646        /**
16647         * @event enddrag
16648         * Fires when a drag operation is complete
16649         * @param {Roo.tree.TreePanel} this
16650         * @param {Roo.tree.TreeNode} node
16651         * @param {event} e The raw browser event
16652         */
16653        "enddrag" : true,
16654        /**
16655         * @event dragdrop
16656         * Fires when a dragged node is dropped on a valid DD target
16657         * @param {Roo.tree.TreePanel} this
16658         * @param {Roo.tree.TreeNode} node
16659         * @param {DD} dd The dd it was dropped on
16660         * @param {event} e The raw browser event
16661         */
16662        "dragdrop" : true,
16663        /**
16664         * @event beforenodedrop
16665         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16666         * passed to handlers has the following properties:<br />
16667         * <ul style="padding:5px;padding-left:16px;">
16668         * <li>tree - The TreePanel</li>
16669         * <li>target - The node being targeted for the drop</li>
16670         * <li>data - The drag data from the drag source</li>
16671         * <li>point - The point of the drop - append, above or below</li>
16672         * <li>source - The drag source</li>
16673         * <li>rawEvent - Raw mouse event</li>
16674         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16675         * to be inserted by setting them on this object.</li>
16676         * <li>cancel - Set this to true to cancel the drop.</li>
16677         * </ul>
16678         * @param {Object} dropEvent
16679         */
16680        "beforenodedrop" : true,
16681        /**
16682         * @event nodedrop
16683         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16684         * passed to handlers has the following properties:<br />
16685         * <ul style="padding:5px;padding-left:16px;">
16686         * <li>tree - The TreePanel</li>
16687         * <li>target - The node being targeted for the drop</li>
16688         * <li>data - The drag data from the drag source</li>
16689         * <li>point - The point of the drop - append, above or below</li>
16690         * <li>source - The drag source</li>
16691         * <li>rawEvent - Raw mouse event</li>
16692         * <li>dropNode - Dropped node(s).</li>
16693         * </ul>
16694         * @param {Object} dropEvent
16695         */
16696        "nodedrop" : true,
16697         /**
16698         * @event nodedragover
16699         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16700         * passed to handlers has the following properties:<br />
16701         * <ul style="padding:5px;padding-left:16px;">
16702         * <li>tree - The TreePanel</li>
16703         * <li>target - The node being targeted for the drop</li>
16704         * <li>data - The drag data from the drag source</li>
16705         * <li>point - The point of the drop - append, above or below</li>
16706         * <li>source - The drag source</li>
16707         * <li>rawEvent - Raw mouse event</li>
16708         * <li>dropNode - Drop node(s) provided by the source.</li>
16709         * <li>cancel - Set this to true to signal drop not allowed.</li>
16710         * </ul>
16711         * @param {Object} dragOverEvent
16712         */
16713        "nodedragover" : true
16714         
16715     });
16716     if(this.singleExpand){
16717        this.on("beforeexpand", this.restrictExpand, this);
16718     }
16719     if (this.editor) {
16720         this.editor.tree = this;
16721         this.editor = Roo.factory(this.editor, Roo.tree);
16722     }
16723     
16724     if (this.selModel) {
16725         this.selModel = Roo.factory(this.selModel, Roo.tree);
16726     }
16727    
16728 };
16729 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16730     rootVisible : true,
16731     animate: Roo.enableFx,
16732     lines : true,
16733     enableDD : false,
16734     hlDrop : Roo.enableFx,
16735   
16736     renderer: false,
16737     
16738     rendererTip: false,
16739     // private
16740     restrictExpand : function(node){
16741         var p = node.parentNode;
16742         if(p){
16743             if(p.expandedChild && p.expandedChild.parentNode == p){
16744                 p.expandedChild.collapse();
16745             }
16746             p.expandedChild = node;
16747         }
16748     },
16749
16750     // private override
16751     setRootNode : function(node){
16752         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16753         if(!this.rootVisible){
16754             node.ui = new Roo.tree.RootTreeNodeUI(node);
16755         }
16756         return node;
16757     },
16758
16759     /**
16760      * Returns the container element for this TreePanel
16761      */
16762     getEl : function(){
16763         return this.el;
16764     },
16765
16766     /**
16767      * Returns the default TreeLoader for this TreePanel
16768      */
16769     getLoader : function(){
16770         return this.loader;
16771     },
16772
16773     /**
16774      * Expand all nodes
16775      */
16776     expandAll : function(){
16777         this.root.expand(true);
16778     },
16779
16780     /**
16781      * Collapse all nodes
16782      */
16783     collapseAll : function(){
16784         this.root.collapse(true);
16785     },
16786
16787     /**
16788      * Returns the selection model used by this TreePanel
16789      */
16790     getSelectionModel : function(){
16791         if(!this.selModel){
16792             this.selModel = new Roo.tree.DefaultSelectionModel();
16793         }
16794         return this.selModel;
16795     },
16796
16797     /**
16798      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16799      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16800      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16801      * @return {Array}
16802      */
16803     getChecked : function(a, startNode){
16804         startNode = startNode || this.root;
16805         var r = [];
16806         var f = function(){
16807             if(this.attributes.checked){
16808                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16809             }
16810         }
16811         startNode.cascade(f);
16812         return r;
16813     },
16814
16815     /**
16816      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16817      * @param {String} path
16818      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16819      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16820      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16821      */
16822     expandPath : function(path, attr, callback){
16823         attr = attr || "id";
16824         var keys = path.split(this.pathSeparator);
16825         var curNode = this.root;
16826         if(curNode.attributes[attr] != keys[1]){ // invalid root
16827             if(callback){
16828                 callback(false, null);
16829             }
16830             return;
16831         }
16832         var index = 1;
16833         var f = function(){
16834             if(++index == keys.length){
16835                 if(callback){
16836                     callback(true, curNode);
16837                 }
16838                 return;
16839             }
16840             var c = curNode.findChild(attr, keys[index]);
16841             if(!c){
16842                 if(callback){
16843                     callback(false, curNode);
16844                 }
16845                 return;
16846             }
16847             curNode = c;
16848             c.expand(false, false, f);
16849         };
16850         curNode.expand(false, false, f);
16851     },
16852
16853     /**
16854      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16855      * @param {String} path
16856      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16857      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16858      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16859      */
16860     selectPath : function(path, attr, callback){
16861         attr = attr || "id";
16862         var keys = path.split(this.pathSeparator);
16863         var v = keys.pop();
16864         if(keys.length > 0){
16865             var f = function(success, node){
16866                 if(success && node){
16867                     var n = node.findChild(attr, v);
16868                     if(n){
16869                         n.select();
16870                         if(callback){
16871                             callback(true, n);
16872                         }
16873                     }else if(callback){
16874                         callback(false, n);
16875                     }
16876                 }else{
16877                     if(callback){
16878                         callback(false, n);
16879                     }
16880                 }
16881             };
16882             this.expandPath(keys.join(this.pathSeparator), attr, f);
16883         }else{
16884             this.root.select();
16885             if(callback){
16886                 callback(true, this.root);
16887             }
16888         }
16889     },
16890
16891     getTreeEl : function(){
16892         return this.el;
16893     },
16894
16895     /**
16896      * Trigger rendering of this TreePanel
16897      */
16898     render : function(){
16899         if (this.innerCt) {
16900             return this; // stop it rendering more than once!!
16901         }
16902         
16903         this.innerCt = this.el.createChild({tag:"ul",
16904                cls:"x-tree-root-ct " +
16905                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16906
16907         if(this.containerScroll){
16908             Roo.dd.ScrollManager.register(this.el);
16909         }
16910         if((this.enableDD || this.enableDrop) && !this.dropZone){
16911            /**
16912             * The dropZone used by this tree if drop is enabled
16913             * @type Roo.tree.TreeDropZone
16914             */
16915              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16916                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16917            });
16918         }
16919         if((this.enableDD || this.enableDrag) && !this.dragZone){
16920            /**
16921             * The dragZone used by this tree if drag is enabled
16922             * @type Roo.tree.TreeDragZone
16923             */
16924             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16925                ddGroup: this.ddGroup || "TreeDD",
16926                scroll: this.ddScroll
16927            });
16928         }
16929         this.getSelectionModel().init(this);
16930         if (!this.root) {
16931             console.log("ROOT not set in tree");
16932             return;
16933         }
16934         this.root.render();
16935         if(!this.rootVisible){
16936             this.root.renderChildren();
16937         }
16938         return this;
16939     }
16940 });/*
16941  * Based on:
16942  * Ext JS Library 1.1.1
16943  * Copyright(c) 2006-2007, Ext JS, LLC.
16944  *
16945  * Originally Released Under LGPL - original licence link has changed is not relivant.
16946  *
16947  * Fork - LGPL
16948  * <script type="text/javascript">
16949  */
16950  
16951
16952 /**
16953  * @class Roo.tree.DefaultSelectionModel
16954  * @extends Roo.util.Observable
16955  * The default single selection for a TreePanel.
16956  * @param {Object} cfg Configuration
16957  */
16958 Roo.tree.DefaultSelectionModel = function(cfg){
16959    this.selNode = null;
16960    
16961    
16962    
16963    this.addEvents({
16964        /**
16965         * @event selectionchange
16966         * Fires when the selected node changes
16967         * @param {DefaultSelectionModel} this
16968         * @param {TreeNode} node the new selection
16969         */
16970        "selectionchange" : true,
16971
16972        /**
16973         * @event beforeselect
16974         * Fires before the selected node changes, return false to cancel the change
16975         * @param {DefaultSelectionModel} this
16976         * @param {TreeNode} node the new selection
16977         * @param {TreeNode} node the old selection
16978         */
16979        "beforeselect" : true
16980    });
16981    
16982     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16983 };
16984
16985 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16986     init : function(tree){
16987         this.tree = tree;
16988         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16989         tree.on("click", this.onNodeClick, this);
16990     },
16991     
16992     onNodeClick : function(node, e){
16993         if (e.ctrlKey && this.selNode == node)  {
16994             this.unselect(node);
16995             return;
16996         }
16997         this.select(node);
16998     },
16999     
17000     /**
17001      * Select a node.
17002      * @param {TreeNode} node The node to select
17003      * @return {TreeNode} The selected node
17004      */
17005     select : function(node){
17006         var last = this.selNode;
17007         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17008             if(last){
17009                 last.ui.onSelectedChange(false);
17010             }
17011             this.selNode = node;
17012             node.ui.onSelectedChange(true);
17013             this.fireEvent("selectionchange", this, node, last);
17014         }
17015         return node;
17016     },
17017     
17018     /**
17019      * Deselect a node.
17020      * @param {TreeNode} node The node to unselect
17021      */
17022     unselect : function(node){
17023         if(this.selNode == node){
17024             this.clearSelections();
17025         }    
17026     },
17027     
17028     /**
17029      * Clear all selections
17030      */
17031     clearSelections : function(){
17032         var n = this.selNode;
17033         if(n){
17034             n.ui.onSelectedChange(false);
17035             this.selNode = null;
17036             this.fireEvent("selectionchange", this, null);
17037         }
17038         return n;
17039     },
17040     
17041     /**
17042      * Get the selected node
17043      * @return {TreeNode} The selected node
17044      */
17045     getSelectedNode : function(){
17046         return this.selNode;    
17047     },
17048     
17049     /**
17050      * Returns true if the node is selected
17051      * @param {TreeNode} node The node to check
17052      * @return {Boolean}
17053      */
17054     isSelected : function(node){
17055         return this.selNode == node;  
17056     },
17057
17058     /**
17059      * Selects the node above the selected node in the tree, intelligently walking the nodes
17060      * @return TreeNode The new selection
17061      */
17062     selectPrevious : function(){
17063         var s = this.selNode || this.lastSelNode;
17064         if(!s){
17065             return null;
17066         }
17067         var ps = s.previousSibling;
17068         if(ps){
17069             if(!ps.isExpanded() || ps.childNodes.length < 1){
17070                 return this.select(ps);
17071             } else{
17072                 var lc = ps.lastChild;
17073                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17074                     lc = lc.lastChild;
17075                 }
17076                 return this.select(lc);
17077             }
17078         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17079             return this.select(s.parentNode);
17080         }
17081         return null;
17082     },
17083
17084     /**
17085      * Selects the node above the selected node in the tree, intelligently walking the nodes
17086      * @return TreeNode The new selection
17087      */
17088     selectNext : function(){
17089         var s = this.selNode || this.lastSelNode;
17090         if(!s){
17091             return null;
17092         }
17093         if(s.firstChild && s.isExpanded()){
17094              return this.select(s.firstChild);
17095          }else if(s.nextSibling){
17096              return this.select(s.nextSibling);
17097          }else if(s.parentNode){
17098             var newS = null;
17099             s.parentNode.bubble(function(){
17100                 if(this.nextSibling){
17101                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17102                     return false;
17103                 }
17104             });
17105             return newS;
17106          }
17107         return null;
17108     },
17109
17110     onKeyDown : function(e){
17111         var s = this.selNode || this.lastSelNode;
17112         // undesirable, but required
17113         var sm = this;
17114         if(!s){
17115             return;
17116         }
17117         var k = e.getKey();
17118         switch(k){
17119              case e.DOWN:
17120                  e.stopEvent();
17121                  this.selectNext();
17122              break;
17123              case e.UP:
17124                  e.stopEvent();
17125                  this.selectPrevious();
17126              break;
17127              case e.RIGHT:
17128                  e.preventDefault();
17129                  if(s.hasChildNodes()){
17130                      if(!s.isExpanded()){
17131                          s.expand();
17132                      }else if(s.firstChild){
17133                          this.select(s.firstChild, e);
17134                      }
17135                  }
17136              break;
17137              case e.LEFT:
17138                  e.preventDefault();
17139                  if(s.hasChildNodes() && s.isExpanded()){
17140                      s.collapse();
17141                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17142                      this.select(s.parentNode, e);
17143                  }
17144              break;
17145         };
17146     }
17147 });
17148
17149 /**
17150  * @class Roo.tree.MultiSelectionModel
17151  * @extends Roo.util.Observable
17152  * Multi selection for a TreePanel.
17153  * @param {Object} cfg Configuration
17154  */
17155 Roo.tree.MultiSelectionModel = function(){
17156    this.selNodes = [];
17157    this.selMap = {};
17158    this.addEvents({
17159        /**
17160         * @event selectionchange
17161         * Fires when the selected nodes change
17162         * @param {MultiSelectionModel} this
17163         * @param {Array} nodes Array of the selected nodes
17164         */
17165        "selectionchange" : true
17166    });
17167    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17168    
17169 };
17170
17171 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17172     init : function(tree){
17173         this.tree = tree;
17174         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17175         tree.on("click", this.onNodeClick, this);
17176     },
17177     
17178     onNodeClick : function(node, e){
17179         this.select(node, e, e.ctrlKey);
17180     },
17181     
17182     /**
17183      * Select a node.
17184      * @param {TreeNode} node The node to select
17185      * @param {EventObject} e (optional) An event associated with the selection
17186      * @param {Boolean} keepExisting True to retain existing selections
17187      * @return {TreeNode} The selected node
17188      */
17189     select : function(node, e, keepExisting){
17190         if(keepExisting !== true){
17191             this.clearSelections(true);
17192         }
17193         if(this.isSelected(node)){
17194             this.lastSelNode = node;
17195             return node;
17196         }
17197         this.selNodes.push(node);
17198         this.selMap[node.id] = node;
17199         this.lastSelNode = node;
17200         node.ui.onSelectedChange(true);
17201         this.fireEvent("selectionchange", this, this.selNodes);
17202         return node;
17203     },
17204     
17205     /**
17206      * Deselect a node.
17207      * @param {TreeNode} node The node to unselect
17208      */
17209     unselect : function(node){
17210         if(this.selMap[node.id]){
17211             node.ui.onSelectedChange(false);
17212             var sn = this.selNodes;
17213             var index = -1;
17214             if(sn.indexOf){
17215                 index = sn.indexOf(node);
17216             }else{
17217                 for(var i = 0, len = sn.length; i < len; i++){
17218                     if(sn[i] == node){
17219                         index = i;
17220                         break;
17221                     }
17222                 }
17223             }
17224             if(index != -1){
17225                 this.selNodes.splice(index, 1);
17226             }
17227             delete this.selMap[node.id];
17228             this.fireEvent("selectionchange", this, this.selNodes);
17229         }
17230     },
17231     
17232     /**
17233      * Clear all selections
17234      */
17235     clearSelections : function(suppressEvent){
17236         var sn = this.selNodes;
17237         if(sn.length > 0){
17238             for(var i = 0, len = sn.length; i < len; i++){
17239                 sn[i].ui.onSelectedChange(false);
17240             }
17241             this.selNodes = [];
17242             this.selMap = {};
17243             if(suppressEvent !== true){
17244                 this.fireEvent("selectionchange", this, this.selNodes);
17245             }
17246         }
17247     },
17248     
17249     /**
17250      * Returns true if the node is selected
17251      * @param {TreeNode} node The node to check
17252      * @return {Boolean}
17253      */
17254     isSelected : function(node){
17255         return this.selMap[node.id] ? true : false;  
17256     },
17257     
17258     /**
17259      * Returns an array of the selected nodes
17260      * @return {Array}
17261      */
17262     getSelectedNodes : function(){
17263         return this.selNodes;    
17264     },
17265
17266     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17267
17268     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17269
17270     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17271 });/*
17272  * Based on:
17273  * Ext JS Library 1.1.1
17274  * Copyright(c) 2006-2007, Ext JS, LLC.
17275  *
17276  * Originally Released Under LGPL - original licence link has changed is not relivant.
17277  *
17278  * Fork - LGPL
17279  * <script type="text/javascript">
17280  */
17281  
17282 /**
17283  * @class Roo.tree.TreeNode
17284  * @extends Roo.data.Node
17285  * @cfg {String} text The text for this node
17286  * @cfg {Boolean} expanded true to start the node expanded
17287  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17288  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17289  * @cfg {Boolean} disabled true to start the node disabled
17290  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17291  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17292  * @cfg {String} cls A css class to be added to the node
17293  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17294  * @cfg {String} href URL of the link used for the node (defaults to #)
17295  * @cfg {String} hrefTarget target frame for the link
17296  * @cfg {String} qtip An Ext QuickTip for the node
17297  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17298  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17299  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17300  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17301  * (defaults to undefined with no checkbox rendered)
17302  * @constructor
17303  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17304  */
17305 Roo.tree.TreeNode = function(attributes){
17306     attributes = attributes || {};
17307     if(typeof attributes == "string"){
17308         attributes = {text: attributes};
17309     }
17310     this.childrenRendered = false;
17311     this.rendered = false;
17312     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17313     this.expanded = attributes.expanded === true;
17314     this.isTarget = attributes.isTarget !== false;
17315     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17316     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17317
17318     /**
17319      * Read-only. The text for this node. To change it use setText().
17320      * @type String
17321      */
17322     this.text = attributes.text;
17323     /**
17324      * True if this node is disabled.
17325      * @type Boolean
17326      */
17327     this.disabled = attributes.disabled === true;
17328
17329     this.addEvents({
17330         /**
17331         * @event textchange
17332         * Fires when the text for this node is changed
17333         * @param {Node} this This node
17334         * @param {String} text The new text
17335         * @param {String} oldText The old text
17336         */
17337         "textchange" : true,
17338         /**
17339         * @event beforeexpand
17340         * Fires before this node is expanded, return false to cancel.
17341         * @param {Node} this This node
17342         * @param {Boolean} deep
17343         * @param {Boolean} anim
17344         */
17345         "beforeexpand" : true,
17346         /**
17347         * @event beforecollapse
17348         * Fires before this node is collapsed, return false to cancel.
17349         * @param {Node} this This node
17350         * @param {Boolean} deep
17351         * @param {Boolean} anim
17352         */
17353         "beforecollapse" : true,
17354         /**
17355         * @event expand
17356         * Fires when this node is expanded
17357         * @param {Node} this This node
17358         */
17359         "expand" : true,
17360         /**
17361         * @event disabledchange
17362         * Fires when the disabled status of this node changes
17363         * @param {Node} this This node
17364         * @param {Boolean} disabled
17365         */
17366         "disabledchange" : true,
17367         /**
17368         * @event collapse
17369         * Fires when this node is collapsed
17370         * @param {Node} this This node
17371         */
17372         "collapse" : true,
17373         /**
17374         * @event beforeclick
17375         * Fires before click processing. Return false to cancel the default action.
17376         * @param {Node} this This node
17377         * @param {Roo.EventObject} e The event object
17378         */
17379         "beforeclick":true,
17380         /**
17381         * @event checkchange
17382         * Fires when a node with a checkbox's checked property changes
17383         * @param {Node} this This node
17384         * @param {Boolean} checked
17385         */
17386         "checkchange":true,
17387         /**
17388         * @event click
17389         * Fires when this node is clicked
17390         * @param {Node} this This node
17391         * @param {Roo.EventObject} e The event object
17392         */
17393         "click":true,
17394         /**
17395         * @event dblclick
17396         * Fires when this node is double clicked
17397         * @param {Node} this This node
17398         * @param {Roo.EventObject} e The event object
17399         */
17400         "dblclick":true,
17401         /**
17402         * @event contextmenu
17403         * Fires when this node is right clicked
17404         * @param {Node} this This node
17405         * @param {Roo.EventObject} e The event object
17406         */
17407         "contextmenu":true,
17408         /**
17409         * @event beforechildrenrendered
17410         * Fires right before the child nodes for this node are rendered
17411         * @param {Node} this This node
17412         */
17413         "beforechildrenrendered":true
17414     });
17415
17416     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17417
17418     /**
17419      * Read-only. The UI for this node
17420      * @type TreeNodeUI
17421      */
17422     this.ui = new uiClass(this);
17423     
17424     // finally support items[]
17425     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17426         return;
17427     }
17428     
17429     
17430     Roo.each(this.attributes.items, function(c) {
17431         this.appendChild(Roo.factory(c,Roo.Tree));
17432     }, this);
17433     delete this.attributes.items;
17434     
17435     
17436     
17437 };
17438 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17439     preventHScroll: true,
17440     /**
17441      * Returns true if this node is expanded
17442      * @return {Boolean}
17443      */
17444     isExpanded : function(){
17445         return this.expanded;
17446     },
17447
17448     /**
17449      * Returns the UI object for this node
17450      * @return {TreeNodeUI}
17451      */
17452     getUI : function(){
17453         return this.ui;
17454     },
17455
17456     // private override
17457     setFirstChild : function(node){
17458         var of = this.firstChild;
17459         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17460         if(this.childrenRendered && of && node != of){
17461             of.renderIndent(true, true);
17462         }
17463         if(this.rendered){
17464             this.renderIndent(true, true);
17465         }
17466     },
17467
17468     // private override
17469     setLastChild : function(node){
17470         var ol = this.lastChild;
17471         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17472         if(this.childrenRendered && ol && node != ol){
17473             ol.renderIndent(true, true);
17474         }
17475         if(this.rendered){
17476             this.renderIndent(true, true);
17477         }
17478     },
17479
17480     // these methods are overridden to provide lazy rendering support
17481     // private override
17482     appendChild : function()
17483     {
17484         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17485         if(node && this.childrenRendered){
17486             node.render();
17487         }
17488         this.ui.updateExpandIcon();
17489         return node;
17490     },
17491
17492     // private override
17493     removeChild : function(node){
17494         this.ownerTree.getSelectionModel().unselect(node);
17495         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17496         // if it's been rendered remove dom node
17497         if(this.childrenRendered){
17498             node.ui.remove();
17499         }
17500         if(this.childNodes.length < 1){
17501             this.collapse(false, false);
17502         }else{
17503             this.ui.updateExpandIcon();
17504         }
17505         if(!this.firstChild) {
17506             this.childrenRendered = false;
17507         }
17508         return node;
17509     },
17510
17511     // private override
17512     insertBefore : function(node, refNode){
17513         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17514         if(newNode && refNode && this.childrenRendered){
17515             node.render();
17516         }
17517         this.ui.updateExpandIcon();
17518         return newNode;
17519     },
17520
17521     /**
17522      * Sets the text for this node
17523      * @param {String} text
17524      */
17525     setText : function(text){
17526         var oldText = this.text;
17527         this.text = text;
17528         this.attributes.text = text;
17529         if(this.rendered){ // event without subscribing
17530             this.ui.onTextChange(this, text, oldText);
17531         }
17532         this.fireEvent("textchange", this, text, oldText);
17533     },
17534
17535     /**
17536      * Triggers selection of this node
17537      */
17538     select : function(){
17539         this.getOwnerTree().getSelectionModel().select(this);
17540     },
17541
17542     /**
17543      * Triggers deselection of this node
17544      */
17545     unselect : function(){
17546         this.getOwnerTree().getSelectionModel().unselect(this);
17547     },
17548
17549     /**
17550      * Returns true if this node is selected
17551      * @return {Boolean}
17552      */
17553     isSelected : function(){
17554         return this.getOwnerTree().getSelectionModel().isSelected(this);
17555     },
17556
17557     /**
17558      * Expand this node.
17559      * @param {Boolean} deep (optional) True to expand all children as well
17560      * @param {Boolean} anim (optional) false to cancel the default animation
17561      * @param {Function} callback (optional) A callback to be called when
17562      * expanding this node completes (does not wait for deep expand to complete).
17563      * Called with 1 parameter, this node.
17564      */
17565     expand : function(deep, anim, callback){
17566         if(!this.expanded){
17567             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17568                 return;
17569             }
17570             if(!this.childrenRendered){
17571                 this.renderChildren();
17572             }
17573             this.expanded = true;
17574             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17575                 this.ui.animExpand(function(){
17576                     this.fireEvent("expand", this);
17577                     if(typeof callback == "function"){
17578                         callback(this);
17579                     }
17580                     if(deep === true){
17581                         this.expandChildNodes(true);
17582                     }
17583                 }.createDelegate(this));
17584                 return;
17585             }else{
17586                 this.ui.expand();
17587                 this.fireEvent("expand", this);
17588                 if(typeof callback == "function"){
17589                     callback(this);
17590                 }
17591             }
17592         }else{
17593            if(typeof callback == "function"){
17594                callback(this);
17595            }
17596         }
17597         if(deep === true){
17598             this.expandChildNodes(true);
17599         }
17600     },
17601
17602     isHiddenRoot : function(){
17603         return this.isRoot && !this.getOwnerTree().rootVisible;
17604     },
17605
17606     /**
17607      * Collapse this node.
17608      * @param {Boolean} deep (optional) True to collapse all children as well
17609      * @param {Boolean} anim (optional) false to cancel the default animation
17610      */
17611     collapse : function(deep, anim){
17612         if(this.expanded && !this.isHiddenRoot()){
17613             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17614                 return;
17615             }
17616             this.expanded = false;
17617             if((this.getOwnerTree().animate && anim !== false) || anim){
17618                 this.ui.animCollapse(function(){
17619                     this.fireEvent("collapse", this);
17620                     if(deep === true){
17621                         this.collapseChildNodes(true);
17622                     }
17623                 }.createDelegate(this));
17624                 return;
17625             }else{
17626                 this.ui.collapse();
17627                 this.fireEvent("collapse", this);
17628             }
17629         }
17630         if(deep === true){
17631             var cs = this.childNodes;
17632             for(var i = 0, len = cs.length; i < len; i++) {
17633                 cs[i].collapse(true, false);
17634             }
17635         }
17636     },
17637
17638     // private
17639     delayedExpand : function(delay){
17640         if(!this.expandProcId){
17641             this.expandProcId = this.expand.defer(delay, this);
17642         }
17643     },
17644
17645     // private
17646     cancelExpand : function(){
17647         if(this.expandProcId){
17648             clearTimeout(this.expandProcId);
17649         }
17650         this.expandProcId = false;
17651     },
17652
17653     /**
17654      * Toggles expanded/collapsed state of the node
17655      */
17656     toggle : function(){
17657         if(this.expanded){
17658             this.collapse();
17659         }else{
17660             this.expand();
17661         }
17662     },
17663
17664     /**
17665      * Ensures all parent nodes are expanded
17666      */
17667     ensureVisible : function(callback){
17668         var tree = this.getOwnerTree();
17669         tree.expandPath(this.parentNode.getPath(), false, function(){
17670             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17671             Roo.callback(callback);
17672         }.createDelegate(this));
17673     },
17674
17675     /**
17676      * Expand all child nodes
17677      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17678      */
17679     expandChildNodes : function(deep){
17680         var cs = this.childNodes;
17681         for(var i = 0, len = cs.length; i < len; i++) {
17682                 cs[i].expand(deep);
17683         }
17684     },
17685
17686     /**
17687      * Collapse all child nodes
17688      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17689      */
17690     collapseChildNodes : function(deep){
17691         var cs = this.childNodes;
17692         for(var i = 0, len = cs.length; i < len; i++) {
17693                 cs[i].collapse(deep);
17694         }
17695     },
17696
17697     /**
17698      * Disables this node
17699      */
17700     disable : function(){
17701         this.disabled = true;
17702         this.unselect();
17703         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17704             this.ui.onDisableChange(this, true);
17705         }
17706         this.fireEvent("disabledchange", this, true);
17707     },
17708
17709     /**
17710      * Enables this node
17711      */
17712     enable : function(){
17713         this.disabled = false;
17714         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17715             this.ui.onDisableChange(this, false);
17716         }
17717         this.fireEvent("disabledchange", this, false);
17718     },
17719
17720     // private
17721     renderChildren : function(suppressEvent){
17722         if(suppressEvent !== false){
17723             this.fireEvent("beforechildrenrendered", this);
17724         }
17725         var cs = this.childNodes;
17726         for(var i = 0, len = cs.length; i < len; i++){
17727             cs[i].render(true);
17728         }
17729         this.childrenRendered = true;
17730     },
17731
17732     // private
17733     sort : function(fn, scope){
17734         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17735         if(this.childrenRendered){
17736             var cs = this.childNodes;
17737             for(var i = 0, len = cs.length; i < len; i++){
17738                 cs[i].render(true);
17739             }
17740         }
17741     },
17742
17743     // private
17744     render : function(bulkRender){
17745         this.ui.render(bulkRender);
17746         if(!this.rendered){
17747             this.rendered = true;
17748             if(this.expanded){
17749                 this.expanded = false;
17750                 this.expand(false, false);
17751             }
17752         }
17753     },
17754
17755     // private
17756     renderIndent : function(deep, refresh){
17757         if(refresh){
17758             this.ui.childIndent = null;
17759         }
17760         this.ui.renderIndent();
17761         if(deep === true && this.childrenRendered){
17762             var cs = this.childNodes;
17763             for(var i = 0, len = cs.length; i < len; i++){
17764                 cs[i].renderIndent(true, refresh);
17765             }
17766         }
17767     }
17768 });/*
17769  * Based on:
17770  * Ext JS Library 1.1.1
17771  * Copyright(c) 2006-2007, Ext JS, LLC.
17772  *
17773  * Originally Released Under LGPL - original licence link has changed is not relivant.
17774  *
17775  * Fork - LGPL
17776  * <script type="text/javascript">
17777  */
17778  
17779 /**
17780  * @class Roo.tree.AsyncTreeNode
17781  * @extends Roo.tree.TreeNode
17782  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17783  * @constructor
17784  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17785  */
17786  Roo.tree.AsyncTreeNode = function(config){
17787     this.loaded = false;
17788     this.loading = false;
17789     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17790     /**
17791     * @event beforeload
17792     * Fires before this node is loaded, return false to cancel
17793     * @param {Node} this This node
17794     */
17795     this.addEvents({'beforeload':true, 'load': true});
17796     /**
17797     * @event load
17798     * Fires when this node is loaded
17799     * @param {Node} this This node
17800     */
17801     /**
17802      * The loader used by this node (defaults to using the tree's defined loader)
17803      * @type TreeLoader
17804      * @property loader
17805      */
17806 };
17807 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17808     expand : function(deep, anim, callback){
17809         if(this.loading){ // if an async load is already running, waiting til it's done
17810             var timer;
17811             var f = function(){
17812                 if(!this.loading){ // done loading
17813                     clearInterval(timer);
17814                     this.expand(deep, anim, callback);
17815                 }
17816             }.createDelegate(this);
17817             timer = setInterval(f, 200);
17818             return;
17819         }
17820         if(!this.loaded){
17821             if(this.fireEvent("beforeload", this) === false){
17822                 return;
17823             }
17824             this.loading = true;
17825             this.ui.beforeLoad(this);
17826             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17827             if(loader){
17828                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17829                 return;
17830             }
17831         }
17832         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17833     },
17834     
17835     /**
17836      * Returns true if this node is currently loading
17837      * @return {Boolean}
17838      */
17839     isLoading : function(){
17840         return this.loading;  
17841     },
17842     
17843     loadComplete : function(deep, anim, callback){
17844         this.loading = false;
17845         this.loaded = true;
17846         this.ui.afterLoad(this);
17847         this.fireEvent("load", this);
17848         this.expand(deep, anim, callback);
17849     },
17850     
17851     /**
17852      * Returns true if this node has been loaded
17853      * @return {Boolean}
17854      */
17855     isLoaded : function(){
17856         return this.loaded;
17857     },
17858     
17859     hasChildNodes : function(){
17860         if(!this.isLeaf() && !this.loaded){
17861             return true;
17862         }else{
17863             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17864         }
17865     },
17866
17867     /**
17868      * Trigger a reload for this node
17869      * @param {Function} callback
17870      */
17871     reload : function(callback){
17872         this.collapse(false, false);
17873         while(this.firstChild){
17874             this.removeChild(this.firstChild);
17875         }
17876         this.childrenRendered = false;
17877         this.loaded = false;
17878         if(this.isHiddenRoot()){
17879             this.expanded = false;
17880         }
17881         this.expand(false, false, callback);
17882     }
17883 });/*
17884  * Based on:
17885  * Ext JS Library 1.1.1
17886  * Copyright(c) 2006-2007, Ext JS, LLC.
17887  *
17888  * Originally Released Under LGPL - original licence link has changed is not relivant.
17889  *
17890  * Fork - LGPL
17891  * <script type="text/javascript">
17892  */
17893  
17894 /**
17895  * @class Roo.tree.TreeNodeUI
17896  * @constructor
17897  * @param {Object} node The node to render
17898  * The TreeNode UI implementation is separate from the
17899  * tree implementation. Unless you are customizing the tree UI,
17900  * you should never have to use this directly.
17901  */
17902 Roo.tree.TreeNodeUI = function(node){
17903     this.node = node;
17904     this.rendered = false;
17905     this.animating = false;
17906     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17907 };
17908
17909 Roo.tree.TreeNodeUI.prototype = {
17910     removeChild : function(node){
17911         if(this.rendered){
17912             this.ctNode.removeChild(node.ui.getEl());
17913         }
17914     },
17915
17916     beforeLoad : function(){
17917          this.addClass("x-tree-node-loading");
17918     },
17919
17920     afterLoad : function(){
17921          this.removeClass("x-tree-node-loading");
17922     },
17923
17924     onTextChange : function(node, text, oldText){
17925         if(this.rendered){
17926             this.textNode.innerHTML = text;
17927         }
17928     },
17929
17930     onDisableChange : function(node, state){
17931         this.disabled = state;
17932         if(state){
17933             this.addClass("x-tree-node-disabled");
17934         }else{
17935             this.removeClass("x-tree-node-disabled");
17936         }
17937     },
17938
17939     onSelectedChange : function(state){
17940         if(state){
17941             this.focus();
17942             this.addClass("x-tree-selected");
17943         }else{
17944             //this.blur();
17945             this.removeClass("x-tree-selected");
17946         }
17947     },
17948
17949     onMove : function(tree, node, oldParent, newParent, index, refNode){
17950         this.childIndent = null;
17951         if(this.rendered){
17952             var targetNode = newParent.ui.getContainer();
17953             if(!targetNode){//target not rendered
17954                 this.holder = document.createElement("div");
17955                 this.holder.appendChild(this.wrap);
17956                 return;
17957             }
17958             var insertBefore = refNode ? refNode.ui.getEl() : null;
17959             if(insertBefore){
17960                 targetNode.insertBefore(this.wrap, insertBefore);
17961             }else{
17962                 targetNode.appendChild(this.wrap);
17963             }
17964             this.node.renderIndent(true);
17965         }
17966     },
17967
17968     addClass : function(cls){
17969         if(this.elNode){
17970             Roo.fly(this.elNode).addClass(cls);
17971         }
17972     },
17973
17974     removeClass : function(cls){
17975         if(this.elNode){
17976             Roo.fly(this.elNode).removeClass(cls);
17977         }
17978     },
17979
17980     remove : function(){
17981         if(this.rendered){
17982             this.holder = document.createElement("div");
17983             this.holder.appendChild(this.wrap);
17984         }
17985     },
17986
17987     fireEvent : function(){
17988         return this.node.fireEvent.apply(this.node, arguments);
17989     },
17990
17991     initEvents : function(){
17992         this.node.on("move", this.onMove, this);
17993         var E = Roo.EventManager;
17994         var a = this.anchor;
17995
17996         var el = Roo.fly(a, '_treeui');
17997
17998         if(Roo.isOpera){ // opera render bug ignores the CSS
17999             el.setStyle("text-decoration", "none");
18000         }
18001
18002         el.on("click", this.onClick, this);
18003         el.on("dblclick", this.onDblClick, this);
18004
18005         if(this.checkbox){
18006             Roo.EventManager.on(this.checkbox,
18007                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18008         }
18009
18010         el.on("contextmenu", this.onContextMenu, this);
18011
18012         var icon = Roo.fly(this.iconNode);
18013         icon.on("click", this.onClick, this);
18014         icon.on("dblclick", this.onDblClick, this);
18015         icon.on("contextmenu", this.onContextMenu, this);
18016         E.on(this.ecNode, "click", this.ecClick, this, true);
18017
18018         if(this.node.disabled){
18019             this.addClass("x-tree-node-disabled");
18020         }
18021         if(this.node.hidden){
18022             this.addClass("x-tree-node-disabled");
18023         }
18024         var ot = this.node.getOwnerTree();
18025         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18026         if(dd && (!this.node.isRoot || ot.rootVisible)){
18027             Roo.dd.Registry.register(this.elNode, {
18028                 node: this.node,
18029                 handles: this.getDDHandles(),
18030                 isHandle: false
18031             });
18032         }
18033     },
18034
18035     getDDHandles : function(){
18036         return [this.iconNode, this.textNode];
18037     },
18038
18039     hide : function(){
18040         if(this.rendered){
18041             this.wrap.style.display = "none";
18042         }
18043     },
18044
18045     show : function(){
18046         if(this.rendered){
18047             this.wrap.style.display = "";
18048         }
18049     },
18050
18051     onContextMenu : function(e){
18052         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18053             e.preventDefault();
18054             this.focus();
18055             this.fireEvent("contextmenu", this.node, e);
18056         }
18057     },
18058
18059     onClick : function(e){
18060         if(this.dropping){
18061             e.stopEvent();
18062             return;
18063         }
18064         if(this.fireEvent("beforeclick", this.node, e) !== false){
18065             if(!this.disabled && this.node.attributes.href){
18066                 this.fireEvent("click", this.node, e);
18067                 return;
18068             }
18069             e.preventDefault();
18070             if(this.disabled){
18071                 return;
18072             }
18073
18074             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18075                 this.node.toggle();
18076             }
18077
18078             this.fireEvent("click", this.node, e);
18079         }else{
18080             e.stopEvent();
18081         }
18082     },
18083
18084     onDblClick : function(e){
18085         e.preventDefault();
18086         if(this.disabled){
18087             return;
18088         }
18089         if(this.checkbox){
18090             this.toggleCheck();
18091         }
18092         if(!this.animating && this.node.hasChildNodes()){
18093             this.node.toggle();
18094         }
18095         this.fireEvent("dblclick", this.node, e);
18096     },
18097
18098     onCheckChange : function(){
18099         var checked = this.checkbox.checked;
18100         this.node.attributes.checked = checked;
18101         this.fireEvent('checkchange', this.node, checked);
18102     },
18103
18104     ecClick : function(e){
18105         if(!this.animating && this.node.hasChildNodes()){
18106             this.node.toggle();
18107         }
18108     },
18109
18110     startDrop : function(){
18111         this.dropping = true;
18112     },
18113
18114     // delayed drop so the click event doesn't get fired on a drop
18115     endDrop : function(){
18116        setTimeout(function(){
18117            this.dropping = false;
18118        }.createDelegate(this), 50);
18119     },
18120
18121     expand : function(){
18122         this.updateExpandIcon();
18123         this.ctNode.style.display = "";
18124     },
18125
18126     focus : function(){
18127         if(!this.node.preventHScroll){
18128             try{this.anchor.focus();
18129             }catch(e){}
18130         }else if(!Roo.isIE){
18131             try{
18132                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18133                 var l = noscroll.scrollLeft;
18134                 this.anchor.focus();
18135                 noscroll.scrollLeft = l;
18136             }catch(e){}
18137         }
18138     },
18139
18140     toggleCheck : function(value){
18141         var cb = this.checkbox;
18142         if(cb){
18143             cb.checked = (value === undefined ? !cb.checked : value);
18144         }
18145     },
18146
18147     blur : function(){
18148         try{
18149             this.anchor.blur();
18150         }catch(e){}
18151     },
18152
18153     animExpand : function(callback){
18154         var ct = Roo.get(this.ctNode);
18155         ct.stopFx();
18156         if(!this.node.hasChildNodes()){
18157             this.updateExpandIcon();
18158             this.ctNode.style.display = "";
18159             Roo.callback(callback);
18160             return;
18161         }
18162         this.animating = true;
18163         this.updateExpandIcon();
18164
18165         ct.slideIn('t', {
18166            callback : function(){
18167                this.animating = false;
18168                Roo.callback(callback);
18169             },
18170             scope: this,
18171             duration: this.node.ownerTree.duration || .25
18172         });
18173     },
18174
18175     highlight : function(){
18176         var tree = this.node.getOwnerTree();
18177         Roo.fly(this.wrap).highlight(
18178             tree.hlColor || "C3DAF9",
18179             {endColor: tree.hlBaseColor}
18180         );
18181     },
18182
18183     collapse : function(){
18184         this.updateExpandIcon();
18185         this.ctNode.style.display = "none";
18186     },
18187
18188     animCollapse : function(callback){
18189         var ct = Roo.get(this.ctNode);
18190         ct.enableDisplayMode('block');
18191         ct.stopFx();
18192
18193         this.animating = true;
18194         this.updateExpandIcon();
18195
18196         ct.slideOut('t', {
18197             callback : function(){
18198                this.animating = false;
18199                Roo.callback(callback);
18200             },
18201             scope: this,
18202             duration: this.node.ownerTree.duration || .25
18203         });
18204     },
18205
18206     getContainer : function(){
18207         return this.ctNode;
18208     },
18209
18210     getEl : function(){
18211         return this.wrap;
18212     },
18213
18214     appendDDGhost : function(ghostNode){
18215         ghostNode.appendChild(this.elNode.cloneNode(true));
18216     },
18217
18218     getDDRepairXY : function(){
18219         return Roo.lib.Dom.getXY(this.iconNode);
18220     },
18221
18222     onRender : function(){
18223         this.render();
18224     },
18225
18226     render : function(bulkRender){
18227         var n = this.node, a = n.attributes;
18228         var targetNode = n.parentNode ?
18229               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18230
18231         if(!this.rendered){
18232             this.rendered = true;
18233
18234             this.renderElements(n, a, targetNode, bulkRender);
18235
18236             if(a.qtip){
18237                if(this.textNode.setAttributeNS){
18238                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18239                    if(a.qtipTitle){
18240                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18241                    }
18242                }else{
18243                    this.textNode.setAttribute("ext:qtip", a.qtip);
18244                    if(a.qtipTitle){
18245                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18246                    }
18247                }
18248             }else if(a.qtipCfg){
18249                 a.qtipCfg.target = Roo.id(this.textNode);
18250                 Roo.QuickTips.register(a.qtipCfg);
18251             }
18252             this.initEvents();
18253             if(!this.node.expanded){
18254                 this.updateExpandIcon();
18255             }
18256         }else{
18257             if(bulkRender === true) {
18258                 targetNode.appendChild(this.wrap);
18259             }
18260         }
18261     },
18262
18263     renderElements : function(n, a, targetNode, bulkRender)
18264     {
18265         // add some indent caching, this helps performance when rendering a large tree
18266         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18267         var t = n.getOwnerTree();
18268         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18269         if (typeof(n.attributes.html) != 'undefined') {
18270             txt = n.attributes.html;
18271         }
18272         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18273         var cb = typeof a.checked == 'boolean';
18274         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18275         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18276             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18277             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18278             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18279             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18280             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18281              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18282                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18283             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18284             "</li>"];
18285
18286         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18287             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18288                                 n.nextSibling.ui.getEl(), buf.join(""));
18289         }else{
18290             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18291         }
18292
18293         this.elNode = this.wrap.childNodes[0];
18294         this.ctNode = this.wrap.childNodes[1];
18295         var cs = this.elNode.childNodes;
18296         this.indentNode = cs[0];
18297         this.ecNode = cs[1];
18298         this.iconNode = cs[2];
18299         var index = 3;
18300         if(cb){
18301             this.checkbox = cs[3];
18302             index++;
18303         }
18304         this.anchor = cs[index];
18305         this.textNode = cs[index].firstChild;
18306     },
18307
18308     getAnchor : function(){
18309         return this.anchor;
18310     },
18311
18312     getTextEl : function(){
18313         return this.textNode;
18314     },
18315
18316     getIconEl : function(){
18317         return this.iconNode;
18318     },
18319
18320     isChecked : function(){
18321         return this.checkbox ? this.checkbox.checked : false;
18322     },
18323
18324     updateExpandIcon : function(){
18325         if(this.rendered){
18326             var n = this.node, c1, c2;
18327             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18328             var hasChild = n.hasChildNodes();
18329             if(hasChild){
18330                 if(n.expanded){
18331                     cls += "-minus";
18332                     c1 = "x-tree-node-collapsed";
18333                     c2 = "x-tree-node-expanded";
18334                 }else{
18335                     cls += "-plus";
18336                     c1 = "x-tree-node-expanded";
18337                     c2 = "x-tree-node-collapsed";
18338                 }
18339                 if(this.wasLeaf){
18340                     this.removeClass("x-tree-node-leaf");
18341                     this.wasLeaf = false;
18342                 }
18343                 if(this.c1 != c1 || this.c2 != c2){
18344                     Roo.fly(this.elNode).replaceClass(c1, c2);
18345                     this.c1 = c1; this.c2 = c2;
18346                 }
18347             }else{
18348                 // this changes non-leafs into leafs if they have no children.
18349                 // it's not very rational behaviour..
18350                 
18351                 if(!this.wasLeaf && this.node.leaf){
18352                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18353                     delete this.c1;
18354                     delete this.c2;
18355                     this.wasLeaf = true;
18356                 }
18357             }
18358             var ecc = "x-tree-ec-icon "+cls;
18359             if(this.ecc != ecc){
18360                 this.ecNode.className = ecc;
18361                 this.ecc = ecc;
18362             }
18363         }
18364     },
18365
18366     getChildIndent : function(){
18367         if(!this.childIndent){
18368             var buf = [];
18369             var p = this.node;
18370             while(p){
18371                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18372                     if(!p.isLast()) {
18373                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18374                     } else {
18375                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18376                     }
18377                 }
18378                 p = p.parentNode;
18379             }
18380             this.childIndent = buf.join("");
18381         }
18382         return this.childIndent;
18383     },
18384
18385     renderIndent : function(){
18386         if(this.rendered){
18387             var indent = "";
18388             var p = this.node.parentNode;
18389             if(p){
18390                 indent = p.ui.getChildIndent();
18391             }
18392             if(this.indentMarkup != indent){ // don't rerender if not required
18393                 this.indentNode.innerHTML = indent;
18394                 this.indentMarkup = indent;
18395             }
18396             this.updateExpandIcon();
18397         }
18398     }
18399 };
18400
18401 Roo.tree.RootTreeNodeUI = function(){
18402     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18403 };
18404 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18405     render : function(){
18406         if(!this.rendered){
18407             var targetNode = this.node.ownerTree.innerCt.dom;
18408             this.node.expanded = true;
18409             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18410             this.wrap = this.ctNode = targetNode.firstChild;
18411         }
18412     },
18413     collapse : function(){
18414     },
18415     expand : function(){
18416     }
18417 });/*
18418  * Based on:
18419  * Ext JS Library 1.1.1
18420  * Copyright(c) 2006-2007, Ext JS, LLC.
18421  *
18422  * Originally Released Under LGPL - original licence link has changed is not relivant.
18423  *
18424  * Fork - LGPL
18425  * <script type="text/javascript">
18426  */
18427 /**
18428  * @class Roo.tree.TreeLoader
18429  * @extends Roo.util.Observable
18430  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18431  * nodes from a specified URL. The response must be a javascript Array definition
18432  * who's elements are node definition objects. eg:
18433  * <pre><code>
18434 {  success : true,
18435    data :      [
18436    
18437     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18438     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18439     ]
18440 }
18441
18442
18443 </code></pre>
18444  * <br><br>
18445  * The old style respose with just an array is still supported, but not recommended.
18446  * <br><br>
18447  *
18448  * A server request is sent, and child nodes are loaded only when a node is expanded.
18449  * The loading node's id is passed to the server under the parameter name "node" to
18450  * enable the server to produce the correct child nodes.
18451  * <br><br>
18452  * To pass extra parameters, an event handler may be attached to the "beforeload"
18453  * event, and the parameters specified in the TreeLoader's baseParams property:
18454  * <pre><code>
18455     myTreeLoader.on("beforeload", function(treeLoader, node) {
18456         this.baseParams.category = node.attributes.category;
18457     }, this);
18458 </code></pre><
18459  * This would pass an HTTP parameter called "category" to the server containing
18460  * the value of the Node's "category" attribute.
18461  * @constructor
18462  * Creates a new Treeloader.
18463  * @param {Object} config A config object containing config properties.
18464  */
18465 Roo.tree.TreeLoader = function(config){
18466     this.baseParams = {};
18467     this.requestMethod = "POST";
18468     Roo.apply(this, config);
18469
18470     this.addEvents({
18471     
18472         /**
18473          * @event beforeload
18474          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18475          * @param {Object} This TreeLoader object.
18476          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18477          * @param {Object} callback The callback function specified in the {@link #load} call.
18478          */
18479         beforeload : true,
18480         /**
18481          * @event load
18482          * Fires when the node has been successfuly loaded.
18483          * @param {Object} This TreeLoader object.
18484          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18485          * @param {Object} response The response object containing the data from the server.
18486          */
18487         load : true,
18488         /**
18489          * @event loadexception
18490          * Fires if the network request failed.
18491          * @param {Object} This TreeLoader object.
18492          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18493          * @param {Object} response The response object containing the data from the server.
18494          */
18495         loadexception : true,
18496         /**
18497          * @event create
18498          * Fires before a node is created, enabling you to return custom Node types 
18499          * @param {Object} This TreeLoader object.
18500          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18501          */
18502         create : true
18503     });
18504
18505     Roo.tree.TreeLoader.superclass.constructor.call(this);
18506 };
18507
18508 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18509     /**
18510     * @cfg {String} dataUrl The URL from which to request a Json string which
18511     * specifies an array of node definition object representing the child nodes
18512     * to be loaded.
18513     */
18514     /**
18515     * @cfg {String} requestMethod either GET or POST
18516     * defaults to POST (due to BC)
18517     * to be loaded.
18518     */
18519     /**
18520     * @cfg {Object} baseParams (optional) An object containing properties which
18521     * specify HTTP parameters to be passed to each request for child nodes.
18522     */
18523     /**
18524     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18525     * created by this loader. If the attributes sent by the server have an attribute in this object,
18526     * they take priority.
18527     */
18528     /**
18529     * @cfg {Object} uiProviders (optional) An object containing properties which
18530     * 
18531     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18532     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18533     * <i>uiProvider</i> attribute of a returned child node is a string rather
18534     * than a reference to a TreeNodeUI implementation, this that string value
18535     * is used as a property name in the uiProviders object. You can define the provider named
18536     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18537     */
18538     uiProviders : {},
18539
18540     /**
18541     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18542     * child nodes before loading.
18543     */
18544     clearOnLoad : true,
18545
18546     /**
18547     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18548     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18549     * Grid query { data : [ .....] }
18550     */
18551     
18552     root : false,
18553      /**
18554     * @cfg {String} queryParam (optional) 
18555     * Name of the query as it will be passed on the querystring (defaults to 'node')
18556     * eg. the request will be ?node=[id]
18557     */
18558     
18559     
18560     queryParam: false,
18561     
18562     /**
18563      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18564      * This is called automatically when a node is expanded, but may be used to reload
18565      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18566      * @param {Roo.tree.TreeNode} node
18567      * @param {Function} callback
18568      */
18569     load : function(node, callback){
18570         if(this.clearOnLoad){
18571             while(node.firstChild){
18572                 node.removeChild(node.firstChild);
18573             }
18574         }
18575         if(node.attributes.children){ // preloaded json children
18576             var cs = node.attributes.children;
18577             for(var i = 0, len = cs.length; i < len; i++){
18578                 node.appendChild(this.createNode(cs[i]));
18579             }
18580             if(typeof callback == "function"){
18581                 callback();
18582             }
18583         }else if(this.dataUrl){
18584             this.requestData(node, callback);
18585         }
18586     },
18587
18588     getParams: function(node){
18589         var buf = [], bp = this.baseParams;
18590         for(var key in bp){
18591             if(typeof bp[key] != "function"){
18592                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18593             }
18594         }
18595         var n = this.queryParam === false ? 'node' : this.queryParam;
18596         buf.push(n + "=", encodeURIComponent(node.id));
18597         return buf.join("");
18598     },
18599
18600     requestData : function(node, callback){
18601         if(this.fireEvent("beforeload", this, node, callback) !== false){
18602             this.transId = Roo.Ajax.request({
18603                 method:this.requestMethod,
18604                 url: this.dataUrl||this.url,
18605                 success: this.handleResponse,
18606                 failure: this.handleFailure,
18607                 scope: this,
18608                 argument: {callback: callback, node: node},
18609                 params: this.getParams(node)
18610             });
18611         }else{
18612             // if the load is cancelled, make sure we notify
18613             // the node that we are done
18614             if(typeof callback == "function"){
18615                 callback();
18616             }
18617         }
18618     },
18619
18620     isLoading : function(){
18621         return this.transId ? true : false;
18622     },
18623
18624     abort : function(){
18625         if(this.isLoading()){
18626             Roo.Ajax.abort(this.transId);
18627         }
18628     },
18629
18630     // private
18631     createNode : function(attr)
18632     {
18633         // apply baseAttrs, nice idea Corey!
18634         if(this.baseAttrs){
18635             Roo.applyIf(attr, this.baseAttrs);
18636         }
18637         if(this.applyLoader !== false){
18638             attr.loader = this;
18639         }
18640         // uiProvider = depreciated..
18641         
18642         if(typeof(attr.uiProvider) == 'string'){
18643            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18644                 /**  eval:var:attr */ eval(attr.uiProvider);
18645         }
18646         if(typeof(this.uiProviders['default']) != 'undefined') {
18647             attr.uiProvider = this.uiProviders['default'];
18648         }
18649         
18650         this.fireEvent('create', this, attr);
18651         
18652         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18653         return(attr.leaf ?
18654                         new Roo.tree.TreeNode(attr) :
18655                         new Roo.tree.AsyncTreeNode(attr));
18656     },
18657
18658     processResponse : function(response, node, callback)
18659     {
18660         var json = response.responseText;
18661         try {
18662             
18663             var o = Roo.decode(json);
18664             
18665             if (this.root === false && typeof(o.success) != undefined) {
18666                 this.root = 'data'; // the default behaviour for list like data..
18667                 }
18668                 
18669             if (this.root !== false &&  !o.success) {
18670                 // it's a failure condition.
18671                 var a = response.argument;
18672                 this.fireEvent("loadexception", this, a.node, response);
18673                 Roo.log("Load failed - should have a handler really");
18674                 return;
18675             }
18676             
18677             
18678             
18679             if (this.root !== false) {
18680                  o = o[this.root];
18681             }
18682             
18683             for(var i = 0, len = o.length; i < len; i++){
18684                 var n = this.createNode(o[i]);
18685                 if(n){
18686                     node.appendChild(n);
18687                 }
18688             }
18689             if(typeof callback == "function"){
18690                 callback(this, node);
18691             }
18692         }catch(e){
18693             this.handleFailure(response);
18694         }
18695     },
18696
18697     handleResponse : function(response){
18698         this.transId = false;
18699         var a = response.argument;
18700         this.processResponse(response, a.node, a.callback);
18701         this.fireEvent("load", this, a.node, response);
18702     },
18703
18704     handleFailure : function(response)
18705     {
18706         // should handle failure better..
18707         this.transId = false;
18708         var a = response.argument;
18709         this.fireEvent("loadexception", this, a.node, response);
18710         if(typeof a.callback == "function"){
18711             a.callback(this, a.node);
18712         }
18713     }
18714 });/*
18715  * Based on:
18716  * Ext JS Library 1.1.1
18717  * Copyright(c) 2006-2007, Ext JS, LLC.
18718  *
18719  * Originally Released Under LGPL - original licence link has changed is not relivant.
18720  *
18721  * Fork - LGPL
18722  * <script type="text/javascript">
18723  */
18724
18725 /**
18726 * @class Roo.tree.TreeFilter
18727 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18728 * @param {TreePanel} tree
18729 * @param {Object} config (optional)
18730  */
18731 Roo.tree.TreeFilter = function(tree, config){
18732     this.tree = tree;
18733     this.filtered = {};
18734     Roo.apply(this, config);
18735 };
18736
18737 Roo.tree.TreeFilter.prototype = {
18738     clearBlank:false,
18739     reverse:false,
18740     autoClear:false,
18741     remove:false,
18742
18743      /**
18744      * Filter the data by a specific attribute.
18745      * @param {String/RegExp} value Either string that the attribute value
18746      * should start with or a RegExp to test against the attribute
18747      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18748      * @param {TreeNode} startNode (optional) The node to start the filter at.
18749      */
18750     filter : function(value, attr, startNode){
18751         attr = attr || "text";
18752         var f;
18753         if(typeof value == "string"){
18754             var vlen = value.length;
18755             // auto clear empty filter
18756             if(vlen == 0 && this.clearBlank){
18757                 this.clear();
18758                 return;
18759             }
18760             value = value.toLowerCase();
18761             f = function(n){
18762                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18763             };
18764         }else if(value.exec){ // regex?
18765             f = function(n){
18766                 return value.test(n.attributes[attr]);
18767             };
18768         }else{
18769             throw 'Illegal filter type, must be string or regex';
18770         }
18771         this.filterBy(f, null, startNode);
18772         },
18773
18774     /**
18775      * Filter by a function. The passed function will be called with each
18776      * node in the tree (or from the startNode). If the function returns true, the node is kept
18777      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18778      * @param {Function} fn The filter function
18779      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18780      */
18781     filterBy : function(fn, scope, startNode){
18782         startNode = startNode || this.tree.root;
18783         if(this.autoClear){
18784             this.clear();
18785         }
18786         var af = this.filtered, rv = this.reverse;
18787         var f = function(n){
18788             if(n == startNode){
18789                 return true;
18790             }
18791             if(af[n.id]){
18792                 return false;
18793             }
18794             var m = fn.call(scope || n, n);
18795             if(!m || rv){
18796                 af[n.id] = n;
18797                 n.ui.hide();
18798                 return false;
18799             }
18800             return true;
18801         };
18802         startNode.cascade(f);
18803         if(this.remove){
18804            for(var id in af){
18805                if(typeof id != "function"){
18806                    var n = af[id];
18807                    if(n && n.parentNode){
18808                        n.parentNode.removeChild(n);
18809                    }
18810                }
18811            }
18812         }
18813     },
18814
18815     /**
18816      * Clears the current filter. Note: with the "remove" option
18817      * set a filter cannot be cleared.
18818      */
18819     clear : function(){
18820         var t = this.tree;
18821         var af = this.filtered;
18822         for(var id in af){
18823             if(typeof id != "function"){
18824                 var n = af[id];
18825                 if(n){
18826                     n.ui.show();
18827                 }
18828             }
18829         }
18830         this.filtered = {};
18831     }
18832 };
18833 /*
18834  * Based on:
18835  * Ext JS Library 1.1.1
18836  * Copyright(c) 2006-2007, Ext JS, LLC.
18837  *
18838  * Originally Released Under LGPL - original licence link has changed is not relivant.
18839  *
18840  * Fork - LGPL
18841  * <script type="text/javascript">
18842  */
18843  
18844
18845 /**
18846  * @class Roo.tree.TreeSorter
18847  * Provides sorting of nodes in a TreePanel
18848  * 
18849  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18850  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18851  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18852  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18853  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18854  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18855  * @constructor
18856  * @param {TreePanel} tree
18857  * @param {Object} config
18858  */
18859 Roo.tree.TreeSorter = function(tree, config){
18860     Roo.apply(this, config);
18861     tree.on("beforechildrenrendered", this.doSort, this);
18862     tree.on("append", this.updateSort, this);
18863     tree.on("insert", this.updateSort, this);
18864     
18865     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18866     var p = this.property || "text";
18867     var sortType = this.sortType;
18868     var fs = this.folderSort;
18869     var cs = this.caseSensitive === true;
18870     var leafAttr = this.leafAttr || 'leaf';
18871
18872     this.sortFn = function(n1, n2){
18873         if(fs){
18874             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18875                 return 1;
18876             }
18877             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18878                 return -1;
18879             }
18880         }
18881         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18882         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18883         if(v1 < v2){
18884                         return dsc ? +1 : -1;
18885                 }else if(v1 > v2){
18886                         return dsc ? -1 : +1;
18887         }else{
18888                 return 0;
18889         }
18890     };
18891 };
18892
18893 Roo.tree.TreeSorter.prototype = {
18894     doSort : function(node){
18895         node.sort(this.sortFn);
18896     },
18897     
18898     compareNodes : function(n1, n2){
18899         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18900     },
18901     
18902     updateSort : function(tree, node){
18903         if(node.childrenRendered){
18904             this.doSort.defer(1, this, [node]);
18905         }
18906     }
18907 };/*
18908  * Based on:
18909  * Ext JS Library 1.1.1
18910  * Copyright(c) 2006-2007, Ext JS, LLC.
18911  *
18912  * Originally Released Under LGPL - original licence link has changed is not relivant.
18913  *
18914  * Fork - LGPL
18915  * <script type="text/javascript">
18916  */
18917
18918 if(Roo.dd.DropZone){
18919     
18920 Roo.tree.TreeDropZone = function(tree, config){
18921     this.allowParentInsert = false;
18922     this.allowContainerDrop = false;
18923     this.appendOnly = false;
18924     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18925     this.tree = tree;
18926     this.lastInsertClass = "x-tree-no-status";
18927     this.dragOverData = {};
18928 };
18929
18930 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18931     ddGroup : "TreeDD",
18932     
18933     expandDelay : 1000,
18934     
18935     expandNode : function(node){
18936         if(node.hasChildNodes() && !node.isExpanded()){
18937             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18938         }
18939     },
18940     
18941     queueExpand : function(node){
18942         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18943     },
18944     
18945     cancelExpand : function(){
18946         if(this.expandProcId){
18947             clearTimeout(this.expandProcId);
18948             this.expandProcId = false;
18949         }
18950     },
18951     
18952     isValidDropPoint : function(n, pt, dd, e, data){
18953         if(!n || !data){ return false; }
18954         var targetNode = n.node;
18955         var dropNode = data.node;
18956         // default drop rules
18957         if(!(targetNode && targetNode.isTarget && pt)){
18958             return false;
18959         }
18960         if(pt == "append" && targetNode.allowChildren === false){
18961             return false;
18962         }
18963         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18964             return false;
18965         }
18966         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18967             return false;
18968         }
18969         // reuse the object
18970         var overEvent = this.dragOverData;
18971         overEvent.tree = this.tree;
18972         overEvent.target = targetNode;
18973         overEvent.data = data;
18974         overEvent.point = pt;
18975         overEvent.source = dd;
18976         overEvent.rawEvent = e;
18977         overEvent.dropNode = dropNode;
18978         overEvent.cancel = false;  
18979         var result = this.tree.fireEvent("nodedragover", overEvent);
18980         return overEvent.cancel === false && result !== false;
18981     },
18982     
18983     getDropPoint : function(e, n, dd){
18984         var tn = n.node;
18985         if(tn.isRoot){
18986             return tn.allowChildren !== false ? "append" : false; // always append for root
18987         }
18988         var dragEl = n.ddel;
18989         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18990         var y = Roo.lib.Event.getPageY(e);
18991         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18992         
18993         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18994         var noAppend = tn.allowChildren === false;
18995         if(this.appendOnly || tn.parentNode.allowChildren === false){
18996             return noAppend ? false : "append";
18997         }
18998         var noBelow = false;
18999         if(!this.allowParentInsert){
19000             noBelow = tn.hasChildNodes() && tn.isExpanded();
19001         }
19002         var q = (b - t) / (noAppend ? 2 : 3);
19003         if(y >= t && y < (t + q)){
19004             return "above";
19005         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19006             return "below";
19007         }else{
19008             return "append";
19009         }
19010     },
19011     
19012     onNodeEnter : function(n, dd, e, data){
19013         this.cancelExpand();
19014     },
19015     
19016     onNodeOver : function(n, dd, e, data){
19017         var pt = this.getDropPoint(e, n, dd);
19018         var node = n.node;
19019         
19020         // auto node expand check
19021         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19022             this.queueExpand(node);
19023         }else if(pt != "append"){
19024             this.cancelExpand();
19025         }
19026         
19027         // set the insert point style on the target node
19028         var returnCls = this.dropNotAllowed;
19029         if(this.isValidDropPoint(n, pt, dd, e, data)){
19030            if(pt){
19031                var el = n.ddel;
19032                var cls;
19033                if(pt == "above"){
19034                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19035                    cls = "x-tree-drag-insert-above";
19036                }else if(pt == "below"){
19037                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19038                    cls = "x-tree-drag-insert-below";
19039                }else{
19040                    returnCls = "x-tree-drop-ok-append";
19041                    cls = "x-tree-drag-append";
19042                }
19043                if(this.lastInsertClass != cls){
19044                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19045                    this.lastInsertClass = cls;
19046                }
19047            }
19048        }
19049        return returnCls;
19050     },
19051     
19052     onNodeOut : function(n, dd, e, data){
19053         this.cancelExpand();
19054         this.removeDropIndicators(n);
19055     },
19056     
19057     onNodeDrop : function(n, dd, e, data){
19058         var point = this.getDropPoint(e, n, dd);
19059         var targetNode = n.node;
19060         targetNode.ui.startDrop();
19061         if(!this.isValidDropPoint(n, point, dd, e, data)){
19062             targetNode.ui.endDrop();
19063             return false;
19064         }
19065         // first try to find the drop node
19066         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19067         var dropEvent = {
19068             tree : this.tree,
19069             target: targetNode,
19070             data: data,
19071             point: point,
19072             source: dd,
19073             rawEvent: e,
19074             dropNode: dropNode,
19075             cancel: !dropNode   
19076         };
19077         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19078         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19079             targetNode.ui.endDrop();
19080             return false;
19081         }
19082         // allow target changing
19083         targetNode = dropEvent.target;
19084         if(point == "append" && !targetNode.isExpanded()){
19085             targetNode.expand(false, null, function(){
19086                 this.completeDrop(dropEvent);
19087             }.createDelegate(this));
19088         }else{
19089             this.completeDrop(dropEvent);
19090         }
19091         return true;
19092     },
19093     
19094     completeDrop : function(de){
19095         var ns = de.dropNode, p = de.point, t = de.target;
19096         if(!(ns instanceof Array)){
19097             ns = [ns];
19098         }
19099         var n;
19100         for(var i = 0, len = ns.length; i < len; i++){
19101             n = ns[i];
19102             if(p == "above"){
19103                 t.parentNode.insertBefore(n, t);
19104             }else if(p == "below"){
19105                 t.parentNode.insertBefore(n, t.nextSibling);
19106             }else{
19107                 t.appendChild(n);
19108             }
19109         }
19110         n.ui.focus();
19111         if(this.tree.hlDrop){
19112             n.ui.highlight();
19113         }
19114         t.ui.endDrop();
19115         this.tree.fireEvent("nodedrop", de);
19116     },
19117     
19118     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19119         if(this.tree.hlDrop){
19120             dropNode.ui.focus();
19121             dropNode.ui.highlight();
19122         }
19123         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19124     },
19125     
19126     getTree : function(){
19127         return this.tree;
19128     },
19129     
19130     removeDropIndicators : function(n){
19131         if(n && n.ddel){
19132             var el = n.ddel;
19133             Roo.fly(el).removeClass([
19134                     "x-tree-drag-insert-above",
19135                     "x-tree-drag-insert-below",
19136                     "x-tree-drag-append"]);
19137             this.lastInsertClass = "_noclass";
19138         }
19139     },
19140     
19141     beforeDragDrop : function(target, e, id){
19142         this.cancelExpand();
19143         return true;
19144     },
19145     
19146     afterRepair : function(data){
19147         if(data && Roo.enableFx){
19148             data.node.ui.highlight();
19149         }
19150         this.hideProxy();
19151     }    
19152 });
19153
19154 }
19155 /*
19156  * Based on:
19157  * Ext JS Library 1.1.1
19158  * Copyright(c) 2006-2007, Ext JS, LLC.
19159  *
19160  * Originally Released Under LGPL - original licence link has changed is not relivant.
19161  *
19162  * Fork - LGPL
19163  * <script type="text/javascript">
19164  */
19165  
19166
19167 if(Roo.dd.DragZone){
19168 Roo.tree.TreeDragZone = function(tree, config){
19169     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19170     this.tree = tree;
19171 };
19172
19173 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19174     ddGroup : "TreeDD",
19175     
19176     onBeforeDrag : function(data, e){
19177         var n = data.node;
19178         return n && n.draggable && !n.disabled;
19179     },
19180     
19181     onInitDrag : function(e){
19182         var data = this.dragData;
19183         this.tree.getSelectionModel().select(data.node);
19184         this.proxy.update("");
19185         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19186         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19187     },
19188     
19189     getRepairXY : function(e, data){
19190         return data.node.ui.getDDRepairXY();
19191     },
19192     
19193     onEndDrag : function(data, e){
19194         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19195     },
19196     
19197     onValidDrop : function(dd, e, id){
19198         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19199         this.hideProxy();
19200     },
19201     
19202     beforeInvalidDrop : function(e, id){
19203         // this scrolls the original position back into view
19204         var sm = this.tree.getSelectionModel();
19205         sm.clearSelections();
19206         sm.select(this.dragData.node);
19207     }
19208 });
19209 }/*
19210  * Based on:
19211  * Ext JS Library 1.1.1
19212  * Copyright(c) 2006-2007, Ext JS, LLC.
19213  *
19214  * Originally Released Under LGPL - original licence link has changed is not relivant.
19215  *
19216  * Fork - LGPL
19217  * <script type="text/javascript">
19218  */
19219 /**
19220  * @class Roo.tree.TreeEditor
19221  * @extends Roo.Editor
19222  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19223  * as the editor field.
19224  * @constructor
19225  * @param {Object} config (used to be the tree panel.)
19226  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19227  * 
19228  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19229  * @cfg {Roo.form.TextField|Object} field The field configuration
19230  *
19231  * 
19232  */
19233 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19234     var tree = config;
19235     var field;
19236     if (oldconfig) { // old style..
19237         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19238     } else {
19239         // new style..
19240         tree = config.tree;
19241         config.field = config.field  || {};
19242         config.field.xtype = 'TextField';
19243         field = Roo.factory(config.field, Roo.form);
19244     }
19245     config = config || {};
19246     
19247     
19248     this.addEvents({
19249         /**
19250          * @event beforenodeedit
19251          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19252          * false from the handler of this event.
19253          * @param {Editor} this
19254          * @param {Roo.tree.Node} node 
19255          */
19256         "beforenodeedit" : true
19257     });
19258     
19259     //Roo.log(config);
19260     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19261
19262     this.tree = tree;
19263
19264     tree.on('beforeclick', this.beforeNodeClick, this);
19265     tree.getTreeEl().on('mousedown', this.hide, this);
19266     this.on('complete', this.updateNode, this);
19267     this.on('beforestartedit', this.fitToTree, this);
19268     this.on('startedit', this.bindScroll, this, {delay:10});
19269     this.on('specialkey', this.onSpecialKey, this);
19270 };
19271
19272 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19273     /**
19274      * @cfg {String} alignment
19275      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19276      */
19277     alignment: "l-l",
19278     // inherit
19279     autoSize: false,
19280     /**
19281      * @cfg {Boolean} hideEl
19282      * True to hide the bound element while the editor is displayed (defaults to false)
19283      */
19284     hideEl : false,
19285     /**
19286      * @cfg {String} cls
19287      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19288      */
19289     cls: "x-small-editor x-tree-editor",
19290     /**
19291      * @cfg {Boolean} shim
19292      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19293      */
19294     shim:false,
19295     // inherit
19296     shadow:"frame",
19297     /**
19298      * @cfg {Number} maxWidth
19299      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19300      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19301      * scroll and client offsets into account prior to each edit.
19302      */
19303     maxWidth: 250,
19304
19305     editDelay : 350,
19306
19307     // private
19308     fitToTree : function(ed, el){
19309         var td = this.tree.getTreeEl().dom, nd = el.dom;
19310         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19311             td.scrollLeft = nd.offsetLeft;
19312         }
19313         var w = Math.min(
19314                 this.maxWidth,
19315                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19316         this.setSize(w, '');
19317         
19318         return this.fireEvent('beforenodeedit', this, this.editNode);
19319         
19320     },
19321
19322     // private
19323     triggerEdit : function(node){
19324         this.completeEdit();
19325         this.editNode = node;
19326         this.startEdit(node.ui.textNode, node.text);
19327     },
19328
19329     // private
19330     bindScroll : function(){
19331         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19332     },
19333
19334     // private
19335     beforeNodeClick : function(node, e){
19336         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19337         this.lastClick = new Date();
19338         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19339             e.stopEvent();
19340             this.triggerEdit(node);
19341             return false;
19342         }
19343         return true;
19344     },
19345
19346     // private
19347     updateNode : function(ed, value){
19348         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19349         this.editNode.setText(value);
19350     },
19351
19352     // private
19353     onHide : function(){
19354         Roo.tree.TreeEditor.superclass.onHide.call(this);
19355         if(this.editNode){
19356             this.editNode.ui.focus();
19357         }
19358     },
19359
19360     // private
19361     onSpecialKey : function(field, e){
19362         var k = e.getKey();
19363         if(k == e.ESC){
19364             e.stopEvent();
19365             this.cancelEdit();
19366         }else if(k == e.ENTER && !e.hasModifier()){
19367             e.stopEvent();
19368             this.completeEdit();
19369         }
19370     }
19371 });//<Script type="text/javascript">
19372 /*
19373  * Based on:
19374  * Ext JS Library 1.1.1
19375  * Copyright(c) 2006-2007, Ext JS, LLC.
19376  *
19377  * Originally Released Under LGPL - original licence link has changed is not relivant.
19378  *
19379  * Fork - LGPL
19380  * <script type="text/javascript">
19381  */
19382  
19383 /**
19384  * Not documented??? - probably should be...
19385  */
19386
19387 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19388     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19389     
19390     renderElements : function(n, a, targetNode, bulkRender){
19391         //consel.log("renderElements?");
19392         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19393
19394         var t = n.getOwnerTree();
19395         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19396         
19397         var cols = t.columns;
19398         var bw = t.borderWidth;
19399         var c = cols[0];
19400         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19401          var cb = typeof a.checked == "boolean";
19402         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19403         var colcls = 'x-t-' + tid + '-c0';
19404         var buf = [
19405             '<li class="x-tree-node">',
19406             
19407                 
19408                 '<div class="x-tree-node-el ', a.cls,'">',
19409                     // extran...
19410                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19411                 
19412                 
19413                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19414                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19415                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19416                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19417                            (a.iconCls ? ' '+a.iconCls : ''),
19418                            '" unselectable="on" />',
19419                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19420                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19421                              
19422                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19423                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19424                             '<span unselectable="on" qtip="' + tx + '">',
19425                              tx,
19426                              '</span></a>' ,
19427                     '</div>',
19428                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19429                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19430                  ];
19431         for(var i = 1, len = cols.length; i < len; i++){
19432             c = cols[i];
19433             colcls = 'x-t-' + tid + '-c' +i;
19434             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19435             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19436                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19437                       "</div>");
19438          }
19439          
19440          buf.push(
19441             '</a>',
19442             '<div class="x-clear"></div></div>',
19443             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19444             "</li>");
19445         
19446         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19447             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19448                                 n.nextSibling.ui.getEl(), buf.join(""));
19449         }else{
19450             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19451         }
19452         var el = this.wrap.firstChild;
19453         this.elRow = el;
19454         this.elNode = el.firstChild;
19455         this.ranchor = el.childNodes[1];
19456         this.ctNode = this.wrap.childNodes[1];
19457         var cs = el.firstChild.childNodes;
19458         this.indentNode = cs[0];
19459         this.ecNode = cs[1];
19460         this.iconNode = cs[2];
19461         var index = 3;
19462         if(cb){
19463             this.checkbox = cs[3];
19464             index++;
19465         }
19466         this.anchor = cs[index];
19467         
19468         this.textNode = cs[index].firstChild;
19469         
19470         //el.on("click", this.onClick, this);
19471         //el.on("dblclick", this.onDblClick, this);
19472         
19473         
19474        // console.log(this);
19475     },
19476     initEvents : function(){
19477         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19478         
19479             
19480         var a = this.ranchor;
19481
19482         var el = Roo.get(a);
19483
19484         if(Roo.isOpera){ // opera render bug ignores the CSS
19485             el.setStyle("text-decoration", "none");
19486         }
19487
19488         el.on("click", this.onClick, this);
19489         el.on("dblclick", this.onDblClick, this);
19490         el.on("contextmenu", this.onContextMenu, this);
19491         
19492     },
19493     
19494     /*onSelectedChange : function(state){
19495         if(state){
19496             this.focus();
19497             this.addClass("x-tree-selected");
19498         }else{
19499             //this.blur();
19500             this.removeClass("x-tree-selected");
19501         }
19502     },*/
19503     addClass : function(cls){
19504         if(this.elRow){
19505             Roo.fly(this.elRow).addClass(cls);
19506         }
19507         
19508     },
19509     
19510     
19511     removeClass : function(cls){
19512         if(this.elRow){
19513             Roo.fly(this.elRow).removeClass(cls);
19514         }
19515     }
19516
19517     
19518     
19519 });//<Script type="text/javascript">
19520
19521 /*
19522  * Based on:
19523  * Ext JS Library 1.1.1
19524  * Copyright(c) 2006-2007, Ext JS, LLC.
19525  *
19526  * Originally Released Under LGPL - original licence link has changed is not relivant.
19527  *
19528  * Fork - LGPL
19529  * <script type="text/javascript">
19530  */
19531  
19532
19533 /**
19534  * @class Roo.tree.ColumnTree
19535  * @extends Roo.data.TreePanel
19536  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19537  * @cfg {int} borderWidth  compined right/left border allowance
19538  * @constructor
19539  * @param {String/HTMLElement/Element} el The container element
19540  * @param {Object} config
19541  */
19542 Roo.tree.ColumnTree =  function(el, config)
19543 {
19544    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19545    this.addEvents({
19546         /**
19547         * @event resize
19548         * Fire this event on a container when it resizes
19549         * @param {int} w Width
19550         * @param {int} h Height
19551         */
19552        "resize" : true
19553     });
19554     this.on('resize', this.onResize, this);
19555 };
19556
19557 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19558     //lines:false,
19559     
19560     
19561     borderWidth: Roo.isBorderBox ? 0 : 2, 
19562     headEls : false,
19563     
19564     render : function(){
19565         // add the header.....
19566        
19567         Roo.tree.ColumnTree.superclass.render.apply(this);
19568         
19569         this.el.addClass('x-column-tree');
19570         
19571         this.headers = this.el.createChild(
19572             {cls:'x-tree-headers'},this.innerCt.dom);
19573    
19574         var cols = this.columns, c;
19575         var totalWidth = 0;
19576         this.headEls = [];
19577         var  len = cols.length;
19578         for(var i = 0; i < len; i++){
19579              c = cols[i];
19580              totalWidth += c.width;
19581             this.headEls.push(this.headers.createChild({
19582                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19583                  cn: {
19584                      cls:'x-tree-hd-text',
19585                      html: c.header
19586                  },
19587                  style:'width:'+(c.width-this.borderWidth)+'px;'
19588              }));
19589         }
19590         this.headers.createChild({cls:'x-clear'});
19591         // prevent floats from wrapping when clipped
19592         this.headers.setWidth(totalWidth);
19593         //this.innerCt.setWidth(totalWidth);
19594         this.innerCt.setStyle({ overflow: 'auto' });
19595         this.onResize(this.width, this.height);
19596              
19597         
19598     },
19599     onResize : function(w,h)
19600     {
19601         this.height = h;
19602         this.width = w;
19603         // resize cols..
19604         this.innerCt.setWidth(this.width);
19605         this.innerCt.setHeight(this.height-20);
19606         
19607         // headers...
19608         var cols = this.columns, c;
19609         var totalWidth = 0;
19610         var expEl = false;
19611         var len = cols.length;
19612         for(var i = 0; i < len; i++){
19613             c = cols[i];
19614             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19615                 // it's the expander..
19616                 expEl  = this.headEls[i];
19617                 continue;
19618             }
19619             totalWidth += c.width;
19620             
19621         }
19622         if (expEl) {
19623             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19624         }
19625         this.headers.setWidth(w-20);
19626
19627         
19628         
19629         
19630     }
19631 });
19632 /*
19633  * Based on:
19634  * Ext JS Library 1.1.1
19635  * Copyright(c) 2006-2007, Ext JS, LLC.
19636  *
19637  * Originally Released Under LGPL - original licence link has changed is not relivant.
19638  *
19639  * Fork - LGPL
19640  * <script type="text/javascript">
19641  */
19642  
19643 /**
19644  * @class Roo.menu.Menu
19645  * @extends Roo.util.Observable
19646  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19647  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19648  * @constructor
19649  * Creates a new Menu
19650  * @param {Object} config Configuration options
19651  */
19652 Roo.menu.Menu = function(config){
19653     Roo.apply(this, config);
19654     this.id = this.id || Roo.id();
19655     this.addEvents({
19656         /**
19657          * @event beforeshow
19658          * Fires before this menu is displayed
19659          * @param {Roo.menu.Menu} this
19660          */
19661         beforeshow : true,
19662         /**
19663          * @event beforehide
19664          * Fires before this menu is hidden
19665          * @param {Roo.menu.Menu} this
19666          */
19667         beforehide : true,
19668         /**
19669          * @event show
19670          * Fires after this menu is displayed
19671          * @param {Roo.menu.Menu} this
19672          */
19673         show : true,
19674         /**
19675          * @event hide
19676          * Fires after this menu is hidden
19677          * @param {Roo.menu.Menu} this
19678          */
19679         hide : true,
19680         /**
19681          * @event click
19682          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19683          * @param {Roo.menu.Menu} this
19684          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19685          * @param {Roo.EventObject} e
19686          */
19687         click : true,
19688         /**
19689          * @event mouseover
19690          * Fires when the mouse is hovering over this menu
19691          * @param {Roo.menu.Menu} this
19692          * @param {Roo.EventObject} e
19693          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19694          */
19695         mouseover : true,
19696         /**
19697          * @event mouseout
19698          * Fires when the mouse exits this menu
19699          * @param {Roo.menu.Menu} this
19700          * @param {Roo.EventObject} e
19701          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19702          */
19703         mouseout : true,
19704         /**
19705          * @event itemclick
19706          * Fires when a menu item contained in this menu is clicked
19707          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19708          * @param {Roo.EventObject} e
19709          */
19710         itemclick: true
19711     });
19712     if (this.registerMenu) {
19713         Roo.menu.MenuMgr.register(this);
19714     }
19715     
19716     var mis = this.items;
19717     this.items = new Roo.util.MixedCollection();
19718     if(mis){
19719         this.add.apply(this, mis);
19720     }
19721 };
19722
19723 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19724     /**
19725      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19726      */
19727     minWidth : 120,
19728     /**
19729      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19730      * for bottom-right shadow (defaults to "sides")
19731      */
19732     shadow : "sides",
19733     /**
19734      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19735      * this menu (defaults to "tl-tr?")
19736      */
19737     subMenuAlign : "tl-tr?",
19738     /**
19739      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19740      * relative to its element of origin (defaults to "tl-bl?")
19741      */
19742     defaultAlign : "tl-bl?",
19743     /**
19744      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19745      */
19746     allowOtherMenus : false,
19747     /**
19748      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19749      */
19750     registerMenu : true,
19751
19752     hidden:true,
19753
19754     // private
19755     render : function(){
19756         if(this.el){
19757             return;
19758         }
19759         var el = this.el = new Roo.Layer({
19760             cls: "x-menu",
19761             shadow:this.shadow,
19762             constrain: false,
19763             parentEl: this.parentEl || document.body,
19764             zindex:15000
19765         });
19766
19767         this.keyNav = new Roo.menu.MenuNav(this);
19768
19769         if(this.plain){
19770             el.addClass("x-menu-plain");
19771         }
19772         if(this.cls){
19773             el.addClass(this.cls);
19774         }
19775         // generic focus element
19776         this.focusEl = el.createChild({
19777             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19778         });
19779         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19780         ul.on("click", this.onClick, this);
19781         ul.on("mouseover", this.onMouseOver, this);
19782         ul.on("mouseout", this.onMouseOut, this);
19783         this.items.each(function(item){
19784             var li = document.createElement("li");
19785             li.className = "x-menu-list-item";
19786             ul.dom.appendChild(li);
19787             item.render(li, this);
19788         }, this);
19789         this.ul = ul;
19790         this.autoWidth();
19791     },
19792
19793     // private
19794     autoWidth : function(){
19795         var el = this.el, ul = this.ul;
19796         if(!el){
19797             return;
19798         }
19799         var w = this.width;
19800         if(w){
19801             el.setWidth(w);
19802         }else if(Roo.isIE){
19803             el.setWidth(this.minWidth);
19804             var t = el.dom.offsetWidth; // force recalc
19805             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19806         }
19807     },
19808
19809     // private
19810     delayAutoWidth : function(){
19811         if(this.rendered){
19812             if(!this.awTask){
19813                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19814             }
19815             this.awTask.delay(20);
19816         }
19817     },
19818
19819     // private
19820     findTargetItem : function(e){
19821         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19822         if(t && t.menuItemId){
19823             return this.items.get(t.menuItemId);
19824         }
19825     },
19826
19827     // private
19828     onClick : function(e){
19829         var t;
19830         if(t = this.findTargetItem(e)){
19831             t.onClick(e);
19832             this.fireEvent("click", this, t, e);
19833         }
19834     },
19835
19836     // private
19837     setActiveItem : function(item, autoExpand){
19838         if(item != this.activeItem){
19839             if(this.activeItem){
19840                 this.activeItem.deactivate();
19841             }
19842             this.activeItem = item;
19843             item.activate(autoExpand);
19844         }else if(autoExpand){
19845             item.expandMenu();
19846         }
19847     },
19848
19849     // private
19850     tryActivate : function(start, step){
19851         var items = this.items;
19852         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19853             var item = items.get(i);
19854             if(!item.disabled && item.canActivate){
19855                 this.setActiveItem(item, false);
19856                 return item;
19857             }
19858         }
19859         return false;
19860     },
19861
19862     // private
19863     onMouseOver : function(e){
19864         var t;
19865         if(t = this.findTargetItem(e)){
19866             if(t.canActivate && !t.disabled){
19867                 this.setActiveItem(t, true);
19868             }
19869         }
19870         this.fireEvent("mouseover", this, e, t);
19871     },
19872
19873     // private
19874     onMouseOut : function(e){
19875         var t;
19876         if(t = this.findTargetItem(e)){
19877             if(t == this.activeItem && t.shouldDeactivate(e)){
19878                 this.activeItem.deactivate();
19879                 delete this.activeItem;
19880             }
19881         }
19882         this.fireEvent("mouseout", this, e, t);
19883     },
19884
19885     /**
19886      * Read-only.  Returns true if the menu is currently displayed, else false.
19887      * @type Boolean
19888      */
19889     isVisible : function(){
19890         return this.el && !this.hidden;
19891     },
19892
19893     /**
19894      * Displays this menu relative to another element
19895      * @param {String/HTMLElement/Roo.Element} element The element to align to
19896      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19897      * the element (defaults to this.defaultAlign)
19898      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19899      */
19900     show : function(el, pos, parentMenu){
19901         this.parentMenu = parentMenu;
19902         if(!this.el){
19903             this.render();
19904         }
19905         this.fireEvent("beforeshow", this);
19906         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19907     },
19908
19909     /**
19910      * Displays this menu at a specific xy position
19911      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19912      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19913      */
19914     showAt : function(xy, parentMenu, /* private: */_e){
19915         this.parentMenu = parentMenu;
19916         if(!this.el){
19917             this.render();
19918         }
19919         if(_e !== false){
19920             this.fireEvent("beforeshow", this);
19921             xy = this.el.adjustForConstraints(xy);
19922         }
19923         this.el.setXY(xy);
19924         this.el.show();
19925         this.hidden = false;
19926         this.focus();
19927         this.fireEvent("show", this);
19928     },
19929
19930     focus : function(){
19931         if(!this.hidden){
19932             this.doFocus.defer(50, this);
19933         }
19934     },
19935
19936     doFocus : function(){
19937         if(!this.hidden){
19938             this.focusEl.focus();
19939         }
19940     },
19941
19942     /**
19943      * Hides this menu and optionally all parent menus
19944      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19945      */
19946     hide : function(deep){
19947         if(this.el && this.isVisible()){
19948             this.fireEvent("beforehide", this);
19949             if(this.activeItem){
19950                 this.activeItem.deactivate();
19951                 this.activeItem = null;
19952             }
19953             this.el.hide();
19954             this.hidden = true;
19955             this.fireEvent("hide", this);
19956         }
19957         if(deep === true && this.parentMenu){
19958             this.parentMenu.hide(true);
19959         }
19960     },
19961
19962     /**
19963      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19964      * Any of the following are valid:
19965      * <ul>
19966      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19967      * <li>An HTMLElement object which will be converted to a menu item</li>
19968      * <li>A menu item config object that will be created as a new menu item</li>
19969      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19970      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19971      * </ul>
19972      * Usage:
19973      * <pre><code>
19974 // Create the menu
19975 var menu = new Roo.menu.Menu();
19976
19977 // Create a menu item to add by reference
19978 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19979
19980 // Add a bunch of items at once using different methods.
19981 // Only the last item added will be returned.
19982 var item = menu.add(
19983     menuItem,                // add existing item by ref
19984     'Dynamic Item',          // new TextItem
19985     '-',                     // new separator
19986     { text: 'Config Item' }  // new item by config
19987 );
19988 </code></pre>
19989      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19990      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19991      */
19992     add : function(){
19993         var a = arguments, l = a.length, item;
19994         for(var i = 0; i < l; i++){
19995             var el = a[i];
19996             if ((typeof(el) == "object") && el.xtype && el.xns) {
19997                 el = Roo.factory(el, Roo.menu);
19998             }
19999             
20000             if(el.render){ // some kind of Item
20001                 item = this.addItem(el);
20002             }else if(typeof el == "string"){ // string
20003                 if(el == "separator" || el == "-"){
20004                     item = this.addSeparator();
20005                 }else{
20006                     item = this.addText(el);
20007                 }
20008             }else if(el.tagName || el.el){ // element
20009                 item = this.addElement(el);
20010             }else if(typeof el == "object"){ // must be menu item config?
20011                 item = this.addMenuItem(el);
20012             }
20013         }
20014         return item;
20015     },
20016
20017     /**
20018      * Returns this menu's underlying {@link Roo.Element} object
20019      * @return {Roo.Element} The element
20020      */
20021     getEl : function(){
20022         if(!this.el){
20023             this.render();
20024         }
20025         return this.el;
20026     },
20027
20028     /**
20029      * Adds a separator bar to the menu
20030      * @return {Roo.menu.Item} The menu item that was added
20031      */
20032     addSeparator : function(){
20033         return this.addItem(new Roo.menu.Separator());
20034     },
20035
20036     /**
20037      * Adds an {@link Roo.Element} object to the menu
20038      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20039      * @return {Roo.menu.Item} The menu item that was added
20040      */
20041     addElement : function(el){
20042         return this.addItem(new Roo.menu.BaseItem(el));
20043     },
20044
20045     /**
20046      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20047      * @param {Roo.menu.Item} item The menu item to add
20048      * @return {Roo.menu.Item} The menu item that was added
20049      */
20050     addItem : function(item){
20051         this.items.add(item);
20052         if(this.ul){
20053             var li = document.createElement("li");
20054             li.className = "x-menu-list-item";
20055             this.ul.dom.appendChild(li);
20056             item.render(li, this);
20057             this.delayAutoWidth();
20058         }
20059         return item;
20060     },
20061
20062     /**
20063      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20064      * @param {Object} config A MenuItem config object
20065      * @return {Roo.menu.Item} The menu item that was added
20066      */
20067     addMenuItem : function(config){
20068         if(!(config instanceof Roo.menu.Item)){
20069             if(typeof config.checked == "boolean"){ // must be check menu item config?
20070                 config = new Roo.menu.CheckItem(config);
20071             }else{
20072                 config = new Roo.menu.Item(config);
20073             }
20074         }
20075         return this.addItem(config);
20076     },
20077
20078     /**
20079      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20080      * @param {String} text The text to display in the menu item
20081      * @return {Roo.menu.Item} The menu item that was added
20082      */
20083     addText : function(text){
20084         return this.addItem(new Roo.menu.TextItem({ text : text }));
20085     },
20086
20087     /**
20088      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20089      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20090      * @param {Roo.menu.Item} item The menu item to add
20091      * @return {Roo.menu.Item} The menu item that was added
20092      */
20093     insert : function(index, item){
20094         this.items.insert(index, item);
20095         if(this.ul){
20096             var li = document.createElement("li");
20097             li.className = "x-menu-list-item";
20098             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20099             item.render(li, this);
20100             this.delayAutoWidth();
20101         }
20102         return item;
20103     },
20104
20105     /**
20106      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20107      * @param {Roo.menu.Item} item The menu item to remove
20108      */
20109     remove : function(item){
20110         this.items.removeKey(item.id);
20111         item.destroy();
20112     },
20113
20114     /**
20115      * Removes and destroys all items in the menu
20116      */
20117     removeAll : function(){
20118         var f;
20119         while(f = this.items.first()){
20120             this.remove(f);
20121         }
20122     }
20123 });
20124
20125 // MenuNav is a private utility class used internally by the Menu
20126 Roo.menu.MenuNav = function(menu){
20127     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20128     this.scope = this.menu = menu;
20129 };
20130
20131 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20132     doRelay : function(e, h){
20133         var k = e.getKey();
20134         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20135             this.menu.tryActivate(0, 1);
20136             return false;
20137         }
20138         return h.call(this.scope || this, e, this.menu);
20139     },
20140
20141     up : function(e, m){
20142         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20143             m.tryActivate(m.items.length-1, -1);
20144         }
20145     },
20146
20147     down : function(e, m){
20148         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20149             m.tryActivate(0, 1);
20150         }
20151     },
20152
20153     right : function(e, m){
20154         if(m.activeItem){
20155             m.activeItem.expandMenu(true);
20156         }
20157     },
20158
20159     left : function(e, m){
20160         m.hide();
20161         if(m.parentMenu && m.parentMenu.activeItem){
20162             m.parentMenu.activeItem.activate();
20163         }
20164     },
20165
20166     enter : function(e, m){
20167         if(m.activeItem){
20168             e.stopPropagation();
20169             m.activeItem.onClick(e);
20170             m.fireEvent("click", this, m.activeItem);
20171             return true;
20172         }
20173     }
20174 });/*
20175  * Based on:
20176  * Ext JS Library 1.1.1
20177  * Copyright(c) 2006-2007, Ext JS, LLC.
20178  *
20179  * Originally Released Under LGPL - original licence link has changed is not relivant.
20180  *
20181  * Fork - LGPL
20182  * <script type="text/javascript">
20183  */
20184  
20185 /**
20186  * @class Roo.menu.MenuMgr
20187  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20188  * @singleton
20189  */
20190 Roo.menu.MenuMgr = function(){
20191    var menus, active, groups = {}, attached = false, lastShow = new Date();
20192
20193    // private - called when first menu is created
20194    function init(){
20195        menus = {};
20196        active = new Roo.util.MixedCollection();
20197        Roo.get(document).addKeyListener(27, function(){
20198            if(active.length > 0){
20199                hideAll();
20200            }
20201        });
20202    }
20203
20204    // private
20205    function hideAll(){
20206        if(active && active.length > 0){
20207            var c = active.clone();
20208            c.each(function(m){
20209                m.hide();
20210            });
20211        }
20212    }
20213
20214    // private
20215    function onHide(m){
20216        active.remove(m);
20217        if(active.length < 1){
20218            Roo.get(document).un("mousedown", onMouseDown);
20219            attached = false;
20220        }
20221    }
20222
20223    // private
20224    function onShow(m){
20225        var last = active.last();
20226        lastShow = new Date();
20227        active.add(m);
20228        if(!attached){
20229            Roo.get(document).on("mousedown", onMouseDown);
20230            attached = true;
20231        }
20232        if(m.parentMenu){
20233           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20234           m.parentMenu.activeChild = m;
20235        }else if(last && last.isVisible()){
20236           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20237        }
20238    }
20239
20240    // private
20241    function onBeforeHide(m){
20242        if(m.activeChild){
20243            m.activeChild.hide();
20244        }
20245        if(m.autoHideTimer){
20246            clearTimeout(m.autoHideTimer);
20247            delete m.autoHideTimer;
20248        }
20249    }
20250
20251    // private
20252    function onBeforeShow(m){
20253        var pm = m.parentMenu;
20254        if(!pm && !m.allowOtherMenus){
20255            hideAll();
20256        }else if(pm && pm.activeChild && active != m){
20257            pm.activeChild.hide();
20258        }
20259    }
20260
20261    // private
20262    function onMouseDown(e){
20263        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20264            hideAll();
20265        }
20266    }
20267
20268    // private
20269    function onBeforeCheck(mi, state){
20270        if(state){
20271            var g = groups[mi.group];
20272            for(var i = 0, l = g.length; i < l; i++){
20273                if(g[i] != mi){
20274                    g[i].setChecked(false);
20275                }
20276            }
20277        }
20278    }
20279
20280    return {
20281
20282        /**
20283         * Hides all menus that are currently visible
20284         */
20285        hideAll : function(){
20286             hideAll();  
20287        },
20288
20289        // private
20290        register : function(menu){
20291            if(!menus){
20292                init();
20293            }
20294            menus[menu.id] = menu;
20295            menu.on("beforehide", onBeforeHide);
20296            menu.on("hide", onHide);
20297            menu.on("beforeshow", onBeforeShow);
20298            menu.on("show", onShow);
20299            var g = menu.group;
20300            if(g && menu.events["checkchange"]){
20301                if(!groups[g]){
20302                    groups[g] = [];
20303                }
20304                groups[g].push(menu);
20305                menu.on("checkchange", onCheck);
20306            }
20307        },
20308
20309         /**
20310          * Returns a {@link Roo.menu.Menu} object
20311          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20312          * be used to generate and return a new Menu instance.
20313          */
20314        get : function(menu){
20315            if(typeof menu == "string"){ // menu id
20316                return menus[menu];
20317            }else if(menu.events){  // menu instance
20318                return menu;
20319            }else if(typeof menu.length == 'number'){ // array of menu items?
20320                return new Roo.menu.Menu({items:menu});
20321            }else{ // otherwise, must be a config
20322                return new Roo.menu.Menu(menu);
20323            }
20324        },
20325
20326        // private
20327        unregister : function(menu){
20328            delete menus[menu.id];
20329            menu.un("beforehide", onBeforeHide);
20330            menu.un("hide", onHide);
20331            menu.un("beforeshow", onBeforeShow);
20332            menu.un("show", onShow);
20333            var g = menu.group;
20334            if(g && menu.events["checkchange"]){
20335                groups[g].remove(menu);
20336                menu.un("checkchange", onCheck);
20337            }
20338        },
20339
20340        // private
20341        registerCheckable : function(menuItem){
20342            var g = menuItem.group;
20343            if(g){
20344                if(!groups[g]){
20345                    groups[g] = [];
20346                }
20347                groups[g].push(menuItem);
20348                menuItem.on("beforecheckchange", onBeforeCheck);
20349            }
20350        },
20351
20352        // private
20353        unregisterCheckable : function(menuItem){
20354            var g = menuItem.group;
20355            if(g){
20356                groups[g].remove(menuItem);
20357                menuItem.un("beforecheckchange", onBeforeCheck);
20358            }
20359        }
20360    };
20361 }();/*
20362  * Based on:
20363  * Ext JS Library 1.1.1
20364  * Copyright(c) 2006-2007, Ext JS, LLC.
20365  *
20366  * Originally Released Under LGPL - original licence link has changed is not relivant.
20367  *
20368  * Fork - LGPL
20369  * <script type="text/javascript">
20370  */
20371  
20372
20373 /**
20374  * @class Roo.menu.BaseItem
20375  * @extends Roo.Component
20376  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20377  * management and base configuration options shared by all menu components.
20378  * @constructor
20379  * Creates a new BaseItem
20380  * @param {Object} config Configuration options
20381  */
20382 Roo.menu.BaseItem = function(config){
20383     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20384
20385     this.addEvents({
20386         /**
20387          * @event click
20388          * Fires when this item is clicked
20389          * @param {Roo.menu.BaseItem} this
20390          * @param {Roo.EventObject} e
20391          */
20392         click: true,
20393         /**
20394          * @event activate
20395          * Fires when this item is activated
20396          * @param {Roo.menu.BaseItem} this
20397          */
20398         activate : true,
20399         /**
20400          * @event deactivate
20401          * Fires when this item is deactivated
20402          * @param {Roo.menu.BaseItem} this
20403          */
20404         deactivate : true
20405     });
20406
20407     if(this.handler){
20408         this.on("click", this.handler, this.scope, true);
20409     }
20410 };
20411
20412 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20413     /**
20414      * @cfg {Function} handler
20415      * A function that will handle the click event of this menu item (defaults to undefined)
20416      */
20417     /**
20418      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20419      */
20420     canActivate : false,
20421     /**
20422      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20423      */
20424     activeClass : "x-menu-item-active",
20425     /**
20426      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20427      */
20428     hideOnClick : true,
20429     /**
20430      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20431      */
20432     hideDelay : 100,
20433
20434     // private
20435     ctype: "Roo.menu.BaseItem",
20436
20437     // private
20438     actionMode : "container",
20439
20440     // private
20441     render : function(container, parentMenu){
20442         this.parentMenu = parentMenu;
20443         Roo.menu.BaseItem.superclass.render.call(this, container);
20444         this.container.menuItemId = this.id;
20445     },
20446
20447     // private
20448     onRender : function(container, position){
20449         this.el = Roo.get(this.el);
20450         container.dom.appendChild(this.el.dom);
20451     },
20452
20453     // private
20454     onClick : function(e){
20455         if(!this.disabled && this.fireEvent("click", this, e) !== false
20456                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20457             this.handleClick(e);
20458         }else{
20459             e.stopEvent();
20460         }
20461     },
20462
20463     // private
20464     activate : function(){
20465         if(this.disabled){
20466             return false;
20467         }
20468         var li = this.container;
20469         li.addClass(this.activeClass);
20470         this.region = li.getRegion().adjust(2, 2, -2, -2);
20471         this.fireEvent("activate", this);
20472         return true;
20473     },
20474
20475     // private
20476     deactivate : function(){
20477         this.container.removeClass(this.activeClass);
20478         this.fireEvent("deactivate", this);
20479     },
20480
20481     // private
20482     shouldDeactivate : function(e){
20483         return !this.region || !this.region.contains(e.getPoint());
20484     },
20485
20486     // private
20487     handleClick : function(e){
20488         if(this.hideOnClick){
20489             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20490         }
20491     },
20492
20493     // private
20494     expandMenu : function(autoActivate){
20495         // do nothing
20496     },
20497
20498     // private
20499     hideMenu : function(){
20500         // do nothing
20501     }
20502 });/*
20503  * Based on:
20504  * Ext JS Library 1.1.1
20505  * Copyright(c) 2006-2007, Ext JS, LLC.
20506  *
20507  * Originally Released Under LGPL - original licence link has changed is not relivant.
20508  *
20509  * Fork - LGPL
20510  * <script type="text/javascript">
20511  */
20512  
20513 /**
20514  * @class Roo.menu.Adapter
20515  * @extends Roo.menu.BaseItem
20516  * 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.
20517  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20518  * @constructor
20519  * Creates a new Adapter
20520  * @param {Object} config Configuration options
20521  */
20522 Roo.menu.Adapter = function(component, config){
20523     Roo.menu.Adapter.superclass.constructor.call(this, config);
20524     this.component = component;
20525 };
20526 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20527     // private
20528     canActivate : true,
20529
20530     // private
20531     onRender : function(container, position){
20532         this.component.render(container);
20533         this.el = this.component.getEl();
20534     },
20535
20536     // private
20537     activate : function(){
20538         if(this.disabled){
20539             return false;
20540         }
20541         this.component.focus();
20542         this.fireEvent("activate", this);
20543         return true;
20544     },
20545
20546     // private
20547     deactivate : function(){
20548         this.fireEvent("deactivate", this);
20549     },
20550
20551     // private
20552     disable : function(){
20553         this.component.disable();
20554         Roo.menu.Adapter.superclass.disable.call(this);
20555     },
20556
20557     // private
20558     enable : function(){
20559         this.component.enable();
20560         Roo.menu.Adapter.superclass.enable.call(this);
20561     }
20562 });/*
20563  * Based on:
20564  * Ext JS Library 1.1.1
20565  * Copyright(c) 2006-2007, Ext JS, LLC.
20566  *
20567  * Originally Released Under LGPL - original licence link has changed is not relivant.
20568  *
20569  * Fork - LGPL
20570  * <script type="text/javascript">
20571  */
20572
20573 /**
20574  * @class Roo.menu.TextItem
20575  * @extends Roo.menu.BaseItem
20576  * Adds a static text string to a menu, usually used as either a heading or group separator.
20577  * Note: old style constructor with text is still supported.
20578  * 
20579  * @constructor
20580  * Creates a new TextItem
20581  * @param {Object} cfg Configuration
20582  */
20583 Roo.menu.TextItem = function(cfg){
20584     if (typeof(cfg) == 'string') {
20585         this.text = cfg;
20586     } else {
20587         Roo.apply(this,cfg);
20588     }
20589     
20590     Roo.menu.TextItem.superclass.constructor.call(this);
20591 };
20592
20593 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20594     /**
20595      * @cfg {Boolean} text Text to show on item.
20596      */
20597     text : '',
20598     
20599     /**
20600      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20601      */
20602     hideOnClick : false,
20603     /**
20604      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20605      */
20606     itemCls : "x-menu-text",
20607
20608     // private
20609     onRender : function(){
20610         var s = document.createElement("span");
20611         s.className = this.itemCls;
20612         s.innerHTML = this.text;
20613         this.el = s;
20614         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20615     }
20616 });/*
20617  * Based on:
20618  * Ext JS Library 1.1.1
20619  * Copyright(c) 2006-2007, Ext JS, LLC.
20620  *
20621  * Originally Released Under LGPL - original licence link has changed is not relivant.
20622  *
20623  * Fork - LGPL
20624  * <script type="text/javascript">
20625  */
20626
20627 /**
20628  * @class Roo.menu.Separator
20629  * @extends Roo.menu.BaseItem
20630  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20631  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20632  * @constructor
20633  * @param {Object} config Configuration options
20634  */
20635 Roo.menu.Separator = function(config){
20636     Roo.menu.Separator.superclass.constructor.call(this, config);
20637 };
20638
20639 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20640     /**
20641      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20642      */
20643     itemCls : "x-menu-sep",
20644     /**
20645      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20646      */
20647     hideOnClick : false,
20648
20649     // private
20650     onRender : function(li){
20651         var s = document.createElement("span");
20652         s.className = this.itemCls;
20653         s.innerHTML = "&#160;";
20654         this.el = s;
20655         li.addClass("x-menu-sep-li");
20656         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20657     }
20658 });/*
20659  * Based on:
20660  * Ext JS Library 1.1.1
20661  * Copyright(c) 2006-2007, Ext JS, LLC.
20662  *
20663  * Originally Released Under LGPL - original licence link has changed is not relivant.
20664  *
20665  * Fork - LGPL
20666  * <script type="text/javascript">
20667  */
20668 /**
20669  * @class Roo.menu.Item
20670  * @extends Roo.menu.BaseItem
20671  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20672  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20673  * activation and click handling.
20674  * @constructor
20675  * Creates a new Item
20676  * @param {Object} config Configuration options
20677  */
20678 Roo.menu.Item = function(config){
20679     Roo.menu.Item.superclass.constructor.call(this, config);
20680     if(this.menu){
20681         this.menu = Roo.menu.MenuMgr.get(this.menu);
20682     }
20683 };
20684 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20685     
20686     /**
20687      * @cfg {String} text
20688      * The text to show on the menu item.
20689      */
20690     text: '',
20691      /**
20692      * @cfg {String} HTML to render in menu
20693      * The text to show on the menu item (HTML version).
20694      */
20695     html: '',
20696     /**
20697      * @cfg {String} icon
20698      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20699      */
20700     icon: undefined,
20701     /**
20702      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20703      */
20704     itemCls : "x-menu-item",
20705     /**
20706      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20707      */
20708     canActivate : true,
20709     /**
20710      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20711      */
20712     showDelay: 200,
20713     // doc'd in BaseItem
20714     hideDelay: 200,
20715
20716     // private
20717     ctype: "Roo.menu.Item",
20718     
20719     // private
20720     onRender : function(container, position){
20721         var el = document.createElement("a");
20722         el.hideFocus = true;
20723         el.unselectable = "on";
20724         el.href = this.href || "#";
20725         if(this.hrefTarget){
20726             el.target = this.hrefTarget;
20727         }
20728         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20729         
20730         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20731         
20732         el.innerHTML = String.format(
20733                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20734                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20735         this.el = el;
20736         Roo.menu.Item.superclass.onRender.call(this, container, position);
20737     },
20738
20739     /**
20740      * Sets the text to display in this menu item
20741      * @param {String} text The text to display
20742      * @param {Boolean} isHTML true to indicate text is pure html.
20743      */
20744     setText : function(text, isHTML){
20745         if (isHTML) {
20746             this.html = text;
20747         } else {
20748             this.text = text;
20749             this.html = '';
20750         }
20751         if(this.rendered){
20752             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20753      
20754             this.el.update(String.format(
20755                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20756                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20757             this.parentMenu.autoWidth();
20758         }
20759     },
20760
20761     // private
20762     handleClick : function(e){
20763         if(!this.href){ // if no link defined, stop the event automatically
20764             e.stopEvent();
20765         }
20766         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20767     },
20768
20769     // private
20770     activate : function(autoExpand){
20771         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20772             this.focus();
20773             if(autoExpand){
20774                 this.expandMenu();
20775             }
20776         }
20777         return true;
20778     },
20779
20780     // private
20781     shouldDeactivate : function(e){
20782         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20783             if(this.menu && this.menu.isVisible()){
20784                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20785             }
20786             return true;
20787         }
20788         return false;
20789     },
20790
20791     // private
20792     deactivate : function(){
20793         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20794         this.hideMenu();
20795     },
20796
20797     // private
20798     expandMenu : function(autoActivate){
20799         if(!this.disabled && this.menu){
20800             clearTimeout(this.hideTimer);
20801             delete this.hideTimer;
20802             if(!this.menu.isVisible() && !this.showTimer){
20803                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20804             }else if (this.menu.isVisible() && autoActivate){
20805                 this.menu.tryActivate(0, 1);
20806             }
20807         }
20808     },
20809
20810     // private
20811     deferExpand : function(autoActivate){
20812         delete this.showTimer;
20813         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20814         if(autoActivate){
20815             this.menu.tryActivate(0, 1);
20816         }
20817     },
20818
20819     // private
20820     hideMenu : function(){
20821         clearTimeout(this.showTimer);
20822         delete this.showTimer;
20823         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20824             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20825         }
20826     },
20827
20828     // private
20829     deferHide : function(){
20830         delete this.hideTimer;
20831         this.menu.hide();
20832     }
20833 });/*
20834  * Based on:
20835  * Ext JS Library 1.1.1
20836  * Copyright(c) 2006-2007, Ext JS, LLC.
20837  *
20838  * Originally Released Under LGPL - original licence link has changed is not relivant.
20839  *
20840  * Fork - LGPL
20841  * <script type="text/javascript">
20842  */
20843  
20844 /**
20845  * @class Roo.menu.CheckItem
20846  * @extends Roo.menu.Item
20847  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20848  * @constructor
20849  * Creates a new CheckItem
20850  * @param {Object} config Configuration options
20851  */
20852 Roo.menu.CheckItem = function(config){
20853     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20854     this.addEvents({
20855         /**
20856          * @event beforecheckchange
20857          * Fires before the checked value is set, providing an opportunity to cancel if needed
20858          * @param {Roo.menu.CheckItem} this
20859          * @param {Boolean} checked The new checked value that will be set
20860          */
20861         "beforecheckchange" : true,
20862         /**
20863          * @event checkchange
20864          * Fires after the checked value has been set
20865          * @param {Roo.menu.CheckItem} this
20866          * @param {Boolean} checked The checked value that was set
20867          */
20868         "checkchange" : true
20869     });
20870     if(this.checkHandler){
20871         this.on('checkchange', this.checkHandler, this.scope);
20872     }
20873 };
20874 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20875     /**
20876      * @cfg {String} group
20877      * All check items with the same group name will automatically be grouped into a single-select
20878      * radio button group (defaults to '')
20879      */
20880     /**
20881      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20882      */
20883     itemCls : "x-menu-item x-menu-check-item",
20884     /**
20885      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20886      */
20887     groupClass : "x-menu-group-item",
20888
20889     /**
20890      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20891      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20892      * initialized with checked = true will be rendered as checked.
20893      */
20894     checked: false,
20895
20896     // private
20897     ctype: "Roo.menu.CheckItem",
20898
20899     // private
20900     onRender : function(c){
20901         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20902         if(this.group){
20903             this.el.addClass(this.groupClass);
20904         }
20905         Roo.menu.MenuMgr.registerCheckable(this);
20906         if(this.checked){
20907             this.checked = false;
20908             this.setChecked(true, true);
20909         }
20910     },
20911
20912     // private
20913     destroy : function(){
20914         if(this.rendered){
20915             Roo.menu.MenuMgr.unregisterCheckable(this);
20916         }
20917         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20918     },
20919
20920     /**
20921      * Set the checked state of this item
20922      * @param {Boolean} checked The new checked value
20923      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20924      */
20925     setChecked : function(state, suppressEvent){
20926         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20927             if(this.container){
20928                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20929             }
20930             this.checked = state;
20931             if(suppressEvent !== true){
20932                 this.fireEvent("checkchange", this, state);
20933             }
20934         }
20935     },
20936
20937     // private
20938     handleClick : function(e){
20939        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20940            this.setChecked(!this.checked);
20941        }
20942        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20943     }
20944 });/*
20945  * Based on:
20946  * Ext JS Library 1.1.1
20947  * Copyright(c) 2006-2007, Ext JS, LLC.
20948  *
20949  * Originally Released Under LGPL - original licence link has changed is not relivant.
20950  *
20951  * Fork - LGPL
20952  * <script type="text/javascript">
20953  */
20954  
20955 /**
20956  * @class Roo.menu.DateItem
20957  * @extends Roo.menu.Adapter
20958  * A menu item that wraps the {@link Roo.DatPicker} component.
20959  * @constructor
20960  * Creates a new DateItem
20961  * @param {Object} config Configuration options
20962  */
20963 Roo.menu.DateItem = function(config){
20964     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20965     /** The Roo.DatePicker object @type Roo.DatePicker */
20966     this.picker = this.component;
20967     this.addEvents({select: true});
20968     
20969     this.picker.on("render", function(picker){
20970         picker.getEl().swallowEvent("click");
20971         picker.container.addClass("x-menu-date-item");
20972     });
20973
20974     this.picker.on("select", this.onSelect, this);
20975 };
20976
20977 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20978     // private
20979     onSelect : function(picker, date){
20980         this.fireEvent("select", this, date, picker);
20981         Roo.menu.DateItem.superclass.handleClick.call(this);
20982     }
20983 });/*
20984  * Based on:
20985  * Ext JS Library 1.1.1
20986  * Copyright(c) 2006-2007, Ext JS, LLC.
20987  *
20988  * Originally Released Under LGPL - original licence link has changed is not relivant.
20989  *
20990  * Fork - LGPL
20991  * <script type="text/javascript">
20992  */
20993  
20994 /**
20995  * @class Roo.menu.ColorItem
20996  * @extends Roo.menu.Adapter
20997  * A menu item that wraps the {@link Roo.ColorPalette} component.
20998  * @constructor
20999  * Creates a new ColorItem
21000  * @param {Object} config Configuration options
21001  */
21002 Roo.menu.ColorItem = function(config){
21003     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21004     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21005     this.palette = this.component;
21006     this.relayEvents(this.palette, ["select"]);
21007     if(this.selectHandler){
21008         this.on('select', this.selectHandler, this.scope);
21009     }
21010 };
21011 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21012  * Based on:
21013  * Ext JS Library 1.1.1
21014  * Copyright(c) 2006-2007, Ext JS, LLC.
21015  *
21016  * Originally Released Under LGPL - original licence link has changed is not relivant.
21017  *
21018  * Fork - LGPL
21019  * <script type="text/javascript">
21020  */
21021  
21022
21023 /**
21024  * @class Roo.menu.DateMenu
21025  * @extends Roo.menu.Menu
21026  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21027  * @constructor
21028  * Creates a new DateMenu
21029  * @param {Object} config Configuration options
21030  */
21031 Roo.menu.DateMenu = function(config){
21032     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21033     this.plain = true;
21034     var di = new Roo.menu.DateItem(config);
21035     this.add(di);
21036     /**
21037      * The {@link Roo.DatePicker} instance for this DateMenu
21038      * @type DatePicker
21039      */
21040     this.picker = di.picker;
21041     /**
21042      * @event select
21043      * @param {DatePicker} picker
21044      * @param {Date} date
21045      */
21046     this.relayEvents(di, ["select"]);
21047
21048     this.on('beforeshow', function(){
21049         if(this.picker){
21050             this.picker.hideMonthPicker(true);
21051         }
21052     }, this);
21053 };
21054 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21055     cls:'x-date-menu'
21056 });/*
21057  * Based on:
21058  * Ext JS Library 1.1.1
21059  * Copyright(c) 2006-2007, Ext JS, LLC.
21060  *
21061  * Originally Released Under LGPL - original licence link has changed is not relivant.
21062  *
21063  * Fork - LGPL
21064  * <script type="text/javascript">
21065  */
21066  
21067
21068 /**
21069  * @class Roo.menu.ColorMenu
21070  * @extends Roo.menu.Menu
21071  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21072  * @constructor
21073  * Creates a new ColorMenu
21074  * @param {Object} config Configuration options
21075  */
21076 Roo.menu.ColorMenu = function(config){
21077     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21078     this.plain = true;
21079     var ci = new Roo.menu.ColorItem(config);
21080     this.add(ci);
21081     /**
21082      * The {@link Roo.ColorPalette} instance for this ColorMenu
21083      * @type ColorPalette
21084      */
21085     this.palette = ci.palette;
21086     /**
21087      * @event select
21088      * @param {ColorPalette} palette
21089      * @param {String} color
21090      */
21091     this.relayEvents(ci, ["select"]);
21092 };
21093 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21094  * Based on:
21095  * Ext JS Library 1.1.1
21096  * Copyright(c) 2006-2007, Ext JS, LLC.
21097  *
21098  * Originally Released Under LGPL - original licence link has changed is not relivant.
21099  *
21100  * Fork - LGPL
21101  * <script type="text/javascript">
21102  */
21103  
21104 /**
21105  * @class Roo.form.Field
21106  * @extends Roo.BoxComponent
21107  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21108  * @constructor
21109  * Creates a new Field
21110  * @param {Object} config Configuration options
21111  */
21112 Roo.form.Field = function(config){
21113     Roo.form.Field.superclass.constructor.call(this, config);
21114 };
21115
21116 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21117     /**
21118      * @cfg {String} fieldLabel Label to use when rendering a form.
21119      */
21120        /**
21121      * @cfg {String} qtip Mouse over tip
21122      */
21123      
21124     /**
21125      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21126      */
21127     invalidClass : "x-form-invalid",
21128     /**
21129      * @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")
21130      */
21131     invalidText : "The value in this field is invalid",
21132     /**
21133      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21134      */
21135     focusClass : "x-form-focus",
21136     /**
21137      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21138       automatic validation (defaults to "keyup").
21139      */
21140     validationEvent : "keyup",
21141     /**
21142      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21143      */
21144     validateOnBlur : true,
21145     /**
21146      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21147      */
21148     validationDelay : 250,
21149     /**
21150      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21151      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21152      */
21153     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21154     /**
21155      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21156      */
21157     fieldClass : "x-form-field",
21158     /**
21159      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21160      *<pre>
21161 Value         Description
21162 -----------   ----------------------------------------------------------------------
21163 qtip          Display a quick tip when the user hovers over the field
21164 title         Display a default browser title attribute popup
21165 under         Add a block div beneath the field containing the error text
21166 side          Add an error icon to the right of the field with a popup on hover
21167 [element id]  Add the error text directly to the innerHTML of the specified element
21168 </pre>
21169      */
21170     msgTarget : 'qtip',
21171     /**
21172      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21173      */
21174     msgFx : 'normal',
21175
21176     /**
21177      * @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.
21178      */
21179     readOnly : false,
21180
21181     /**
21182      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21183      */
21184     disabled : false,
21185
21186     /**
21187      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21188      */
21189     inputType : undefined,
21190     
21191     /**
21192      * @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).
21193          */
21194         tabIndex : undefined,
21195         
21196     // private
21197     isFormField : true,
21198
21199     // private
21200     hasFocus : false,
21201     /**
21202      * @property {Roo.Element} fieldEl
21203      * Element Containing the rendered Field (with label etc.)
21204      */
21205     /**
21206      * @cfg {Mixed} value A value to initialize this field with.
21207      */
21208     value : undefined,
21209
21210     /**
21211      * @cfg {String} name The field's HTML name attribute.
21212      */
21213     /**
21214      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21215      */
21216
21217         // private ??
21218         initComponent : function(){
21219         Roo.form.Field.superclass.initComponent.call(this);
21220         this.addEvents({
21221             /**
21222              * @event focus
21223              * Fires when this field receives input focus.
21224              * @param {Roo.form.Field} this
21225              */
21226             focus : true,
21227             /**
21228              * @event blur
21229              * Fires when this field loses input focus.
21230              * @param {Roo.form.Field} this
21231              */
21232             blur : true,
21233             /**
21234              * @event specialkey
21235              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21236              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21237              * @param {Roo.form.Field} this
21238              * @param {Roo.EventObject} e The event object
21239              */
21240             specialkey : true,
21241             /**
21242              * @event change
21243              * Fires just before the field blurs if the field value has changed.
21244              * @param {Roo.form.Field} this
21245              * @param {Mixed} newValue The new value
21246              * @param {Mixed} oldValue The original value
21247              */
21248             change : true,
21249             /**
21250              * @event invalid
21251              * Fires after the field has been marked as invalid.
21252              * @param {Roo.form.Field} this
21253              * @param {String} msg The validation message
21254              */
21255             invalid : true,
21256             /**
21257              * @event valid
21258              * Fires after the field has been validated with no errors.
21259              * @param {Roo.form.Field} this
21260              */
21261             valid : true,
21262              /**
21263              * @event keyup
21264              * Fires after the key up
21265              * @param {Roo.form.Field} this
21266              * @param {Roo.EventObject}  e The event Object
21267              */
21268             keyup : true
21269         });
21270     },
21271
21272     /**
21273      * Returns the name attribute of the field if available
21274      * @return {String} name The field name
21275      */
21276     getName: function(){
21277          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21278     },
21279
21280     // private
21281     onRender : function(ct, position){
21282         Roo.form.Field.superclass.onRender.call(this, ct, position);
21283         if(!this.el){
21284             var cfg = this.getAutoCreate();
21285             if(!cfg.name){
21286                 cfg.name = this.name || this.id;
21287             }
21288             if(this.inputType){
21289                 cfg.type = this.inputType;
21290             }
21291             this.el = ct.createChild(cfg, position);
21292         }
21293         var type = this.el.dom.type;
21294         if(type){
21295             if(type == 'password'){
21296                 type = 'text';
21297             }
21298             this.el.addClass('x-form-'+type);
21299         }
21300         if(this.readOnly){
21301             this.el.dom.readOnly = true;
21302         }
21303         if(this.tabIndex !== undefined){
21304             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21305         }
21306
21307         this.el.addClass([this.fieldClass, this.cls]);
21308         this.initValue();
21309     },
21310
21311     /**
21312      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21313      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21314      * @return {Roo.form.Field} this
21315      */
21316     applyTo : function(target){
21317         this.allowDomMove = false;
21318         this.el = Roo.get(target);
21319         this.render(this.el.dom.parentNode);
21320         return this;
21321     },
21322
21323     // private
21324     initValue : function(){
21325         if(this.value !== undefined){
21326             this.setValue(this.value);
21327         }else if(this.el.dom.value.length > 0){
21328             this.setValue(this.el.dom.value);
21329         }
21330     },
21331
21332     /**
21333      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21334      */
21335     isDirty : function() {
21336         if(this.disabled) {
21337             return false;
21338         }
21339         return String(this.getValue()) !== String(this.originalValue);
21340     },
21341
21342     // private
21343     afterRender : function(){
21344         Roo.form.Field.superclass.afterRender.call(this);
21345         this.initEvents();
21346     },
21347
21348     // private
21349     fireKey : function(e){
21350         //Roo.log('field ' + e.getKey());
21351         if(e.isNavKeyPress()){
21352             this.fireEvent("specialkey", this, e);
21353         }
21354     },
21355
21356     /**
21357      * Resets the current field value to the originally loaded value and clears any validation messages
21358      */
21359     reset : function(){
21360         this.setValue(this.originalValue);
21361         this.clearInvalid();
21362     },
21363
21364     // private
21365     initEvents : function(){
21366         // safari killled keypress - so keydown is now used..
21367         this.el.on("keydown" , this.fireKey,  this);
21368         this.el.on("focus", this.onFocus,  this);
21369         this.el.on("blur", this.onBlur,  this);
21370         this.el.relayEvent('keyup', this);
21371
21372         // reference to original value for reset
21373         this.originalValue = this.getValue();
21374     },
21375
21376     // private
21377     onFocus : function(){
21378         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21379             this.el.addClass(this.focusClass);
21380         }
21381         if(!this.hasFocus){
21382             this.hasFocus = true;
21383             this.startValue = this.getValue();
21384             this.fireEvent("focus", this);
21385         }
21386     },
21387
21388     beforeBlur : Roo.emptyFn,
21389
21390     // private
21391     onBlur : function(){
21392         this.beforeBlur();
21393         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21394             this.el.removeClass(this.focusClass);
21395         }
21396         this.hasFocus = false;
21397         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21398             this.validate();
21399         }
21400         var v = this.getValue();
21401         if(String(v) !== String(this.startValue)){
21402             this.fireEvent('change', this, v, this.startValue);
21403         }
21404         this.fireEvent("blur", this);
21405     },
21406
21407     /**
21408      * Returns whether or not the field value is currently valid
21409      * @param {Boolean} preventMark True to disable marking the field invalid
21410      * @return {Boolean} True if the value is valid, else false
21411      */
21412     isValid : function(preventMark){
21413         if(this.disabled){
21414             return true;
21415         }
21416         var restore = this.preventMark;
21417         this.preventMark = preventMark === true;
21418         var v = this.validateValue(this.processValue(this.getRawValue()));
21419         this.preventMark = restore;
21420         return v;
21421     },
21422
21423     /**
21424      * Validates the field value
21425      * @return {Boolean} True if the value is valid, else false
21426      */
21427     validate : function(){
21428         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21429             this.clearInvalid();
21430             return true;
21431         }
21432         return false;
21433     },
21434
21435     processValue : function(value){
21436         return value;
21437     },
21438
21439     // private
21440     // Subclasses should provide the validation implementation by overriding this
21441     validateValue : function(value){
21442         return true;
21443     },
21444
21445     /**
21446      * Mark this field as invalid
21447      * @param {String} msg The validation message
21448      */
21449     markInvalid : function(msg){
21450         if(!this.rendered || this.preventMark){ // not rendered
21451             return;
21452         }
21453         this.el.addClass(this.invalidClass);
21454         msg = msg || this.invalidText;
21455         switch(this.msgTarget){
21456             case 'qtip':
21457                 this.el.dom.qtip = msg;
21458                 this.el.dom.qclass = 'x-form-invalid-tip';
21459                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21460                     Roo.QuickTips.enable();
21461                 }
21462                 break;
21463             case 'title':
21464                 this.el.dom.title = msg;
21465                 break;
21466             case 'under':
21467                 if(!this.errorEl){
21468                     var elp = this.el.findParent('.x-form-element', 5, true);
21469                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21470                     this.errorEl.setWidth(elp.getWidth(true)-20);
21471                 }
21472                 this.errorEl.update(msg);
21473                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21474                 break;
21475             case 'side':
21476                 if(!this.errorIcon){
21477                     var elp = this.el.findParent('.x-form-element', 5, true);
21478                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21479                 }
21480                 this.alignErrorIcon();
21481                 this.errorIcon.dom.qtip = msg;
21482                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21483                 this.errorIcon.show();
21484                 this.on('resize', this.alignErrorIcon, this);
21485                 break;
21486             default:
21487                 var t = Roo.getDom(this.msgTarget);
21488                 t.innerHTML = msg;
21489                 t.style.display = this.msgDisplay;
21490                 break;
21491         }
21492         this.fireEvent('invalid', this, msg);
21493     },
21494
21495     // private
21496     alignErrorIcon : function(){
21497         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21498     },
21499
21500     /**
21501      * Clear any invalid styles/messages for this field
21502      */
21503     clearInvalid : function(){
21504         if(!this.rendered || this.preventMark){ // not rendered
21505             return;
21506         }
21507         this.el.removeClass(this.invalidClass);
21508         switch(this.msgTarget){
21509             case 'qtip':
21510                 this.el.dom.qtip = '';
21511                 break;
21512             case 'title':
21513                 this.el.dom.title = '';
21514                 break;
21515             case 'under':
21516                 if(this.errorEl){
21517                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21518                 }
21519                 break;
21520             case 'side':
21521                 if(this.errorIcon){
21522                     this.errorIcon.dom.qtip = '';
21523                     this.errorIcon.hide();
21524                     this.un('resize', this.alignErrorIcon, this);
21525                 }
21526                 break;
21527             default:
21528                 var t = Roo.getDom(this.msgTarget);
21529                 t.innerHTML = '';
21530                 t.style.display = 'none';
21531                 break;
21532         }
21533         this.fireEvent('valid', this);
21534     },
21535
21536     /**
21537      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21538      * @return {Mixed} value The field value
21539      */
21540     getRawValue : function(){
21541         var v = this.el.getValue();
21542         if(v === this.emptyText){
21543             v = '';
21544         }
21545         return v;
21546     },
21547
21548     /**
21549      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21550      * @return {Mixed} value The field value
21551      */
21552     getValue : function(){
21553         var v = this.el.getValue();
21554         if(v === this.emptyText || v === undefined){
21555             v = '';
21556         }
21557         return v;
21558     },
21559
21560     /**
21561      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21562      * @param {Mixed} value The value to set
21563      */
21564     setRawValue : function(v){
21565         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21566     },
21567
21568     /**
21569      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21570      * @param {Mixed} value The value to set
21571      */
21572     setValue : function(v){
21573         this.value = v;
21574         if(this.rendered){
21575             this.el.dom.value = (v === null || v === undefined ? '' : v);
21576              this.validate();
21577         }
21578     },
21579
21580     adjustSize : function(w, h){
21581         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21582         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21583         return s;
21584     },
21585
21586     adjustWidth : function(tag, w){
21587         tag = tag.toLowerCase();
21588         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21589             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21590                 if(tag == 'input'){
21591                     return w + 2;
21592                 }
21593                 if(tag = 'textarea'){
21594                     return w-2;
21595                 }
21596             }else if(Roo.isOpera){
21597                 if(tag == 'input'){
21598                     return w + 2;
21599                 }
21600                 if(tag = 'textarea'){
21601                     return w-2;
21602                 }
21603             }
21604         }
21605         return w;
21606     }
21607 });
21608
21609
21610 // anything other than normal should be considered experimental
21611 Roo.form.Field.msgFx = {
21612     normal : {
21613         show: function(msgEl, f){
21614             msgEl.setDisplayed('block');
21615         },
21616
21617         hide : function(msgEl, f){
21618             msgEl.setDisplayed(false).update('');
21619         }
21620     },
21621
21622     slide : {
21623         show: function(msgEl, f){
21624             msgEl.slideIn('t', {stopFx:true});
21625         },
21626
21627         hide : function(msgEl, f){
21628             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21629         }
21630     },
21631
21632     slideRight : {
21633         show: function(msgEl, f){
21634             msgEl.fixDisplay();
21635             msgEl.alignTo(f.el, 'tl-tr');
21636             msgEl.slideIn('l', {stopFx:true});
21637         },
21638
21639         hide : function(msgEl, f){
21640             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21641         }
21642     }
21643 };/*
21644  * Based on:
21645  * Ext JS Library 1.1.1
21646  * Copyright(c) 2006-2007, Ext JS, LLC.
21647  *
21648  * Originally Released Under LGPL - original licence link has changed is not relivant.
21649  *
21650  * Fork - LGPL
21651  * <script type="text/javascript">
21652  */
21653  
21654
21655 /**
21656  * @class Roo.form.TextField
21657  * @extends Roo.form.Field
21658  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21659  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21660  * @constructor
21661  * Creates a new TextField
21662  * @param {Object} config Configuration options
21663  */
21664 Roo.form.TextField = function(config){
21665     Roo.form.TextField.superclass.constructor.call(this, config);
21666     this.addEvents({
21667         /**
21668          * @event autosize
21669          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21670          * according to the default logic, but this event provides a hook for the developer to apply additional
21671          * logic at runtime to resize the field if needed.
21672              * @param {Roo.form.Field} this This text field
21673              * @param {Number} width The new field width
21674              */
21675         autosize : true
21676     });
21677 };
21678
21679 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21680     /**
21681      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21682      */
21683     grow : false,
21684     /**
21685      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21686      */
21687     growMin : 30,
21688     /**
21689      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21690      */
21691     growMax : 800,
21692     /**
21693      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21694      */
21695     vtype : null,
21696     /**
21697      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21698      */
21699     maskRe : null,
21700     /**
21701      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21702      */
21703     disableKeyFilter : false,
21704     /**
21705      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21706      */
21707     allowBlank : true,
21708     /**
21709      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21710      */
21711     minLength : 0,
21712     /**
21713      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21714      */
21715     maxLength : Number.MAX_VALUE,
21716     /**
21717      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21718      */
21719     minLengthText : "The minimum length for this field is {0}",
21720     /**
21721      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21722      */
21723     maxLengthText : "The maximum length for this field is {0}",
21724     /**
21725      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21726      */
21727     selectOnFocus : false,
21728     /**
21729      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21730      */
21731     blankText : "This field is required",
21732     /**
21733      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21734      * If available, this function will be called only after the basic validators all return true, and will be passed the
21735      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21736      */
21737     validator : null,
21738     /**
21739      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21740      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21741      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21742      */
21743     regex : null,
21744     /**
21745      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21746      */
21747     regexText : "",
21748     /**
21749      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21750      */
21751     emptyText : null,
21752     /**
21753      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21754      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21755      */
21756     emptyClass : 'x-form-empty-field',
21757
21758     // private
21759     initEvents : function(){
21760         Roo.form.TextField.superclass.initEvents.call(this);
21761         if(this.validationEvent == 'keyup'){
21762             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21763             this.el.on('keyup', this.filterValidation, this);
21764         }
21765         else if(this.validationEvent !== false){
21766             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21767         }
21768         if(this.selectOnFocus || this.emptyText){
21769             this.on("focus", this.preFocus, this);
21770             if(this.emptyText){
21771                 this.on('blur', this.postBlur, this);
21772                 this.applyEmptyText();
21773             }
21774         }
21775         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21776             this.el.on("keypress", this.filterKeys, this);
21777         }
21778         if(this.grow){
21779             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21780             this.el.on("click", this.autoSize,  this);
21781         }
21782     },
21783
21784     processValue : function(value){
21785         if(this.stripCharsRe){
21786             var newValue = value.replace(this.stripCharsRe, '');
21787             if(newValue !== value){
21788                 this.setRawValue(newValue);
21789                 return newValue;
21790             }
21791         }
21792         return value;
21793     },
21794
21795     filterValidation : function(e){
21796         if(!e.isNavKeyPress()){
21797             this.validationTask.delay(this.validationDelay);
21798         }
21799     },
21800
21801     // private
21802     onKeyUp : function(e){
21803         if(!e.isNavKeyPress()){
21804             this.autoSize();
21805         }
21806     },
21807
21808     /**
21809      * Resets the current field value to the originally-loaded value and clears any validation messages.
21810      * Also adds emptyText and emptyClass if the original value was blank.
21811      */
21812     reset : function(){
21813         Roo.form.TextField.superclass.reset.call(this);
21814         this.applyEmptyText();
21815     },
21816
21817     applyEmptyText : function(){
21818         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21819             this.setRawValue(this.emptyText);
21820             this.el.addClass(this.emptyClass);
21821         }
21822     },
21823
21824     // private
21825     preFocus : function(){
21826         if(this.emptyText){
21827             if(this.el.dom.value == this.emptyText){
21828                 this.setRawValue('');
21829             }
21830             this.el.removeClass(this.emptyClass);
21831         }
21832         if(this.selectOnFocus){
21833             this.el.dom.select();
21834         }
21835     },
21836
21837     // private
21838     postBlur : function(){
21839         this.applyEmptyText();
21840     },
21841
21842     // private
21843     filterKeys : function(e){
21844         var k = e.getKey();
21845         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21846             return;
21847         }
21848         var c = e.getCharCode(), cc = String.fromCharCode(c);
21849         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21850             return;
21851         }
21852         if(!this.maskRe.test(cc)){
21853             e.stopEvent();
21854         }
21855     },
21856
21857     setValue : function(v){
21858         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21859             this.el.removeClass(this.emptyClass);
21860         }
21861         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21862         this.applyEmptyText();
21863         this.autoSize();
21864     },
21865
21866     /**
21867      * Validates a value according to the field's validation rules and marks the field as invalid
21868      * if the validation fails
21869      * @param {Mixed} value The value to validate
21870      * @return {Boolean} True if the value is valid, else false
21871      */
21872     validateValue : function(value){
21873         if(value.length < 1 || value === this.emptyText){ // if it's blank
21874              if(this.allowBlank){
21875                 this.clearInvalid();
21876                 return true;
21877              }else{
21878                 this.markInvalid(this.blankText);
21879                 return false;
21880              }
21881         }
21882         if(value.length < this.minLength){
21883             this.markInvalid(String.format(this.minLengthText, this.minLength));
21884             return false;
21885         }
21886         if(value.length > this.maxLength){
21887             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21888             return false;
21889         }
21890         if(this.vtype){
21891             var vt = Roo.form.VTypes;
21892             if(!vt[this.vtype](value, this)){
21893                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21894                 return false;
21895             }
21896         }
21897         if(typeof this.validator == "function"){
21898             var msg = this.validator(value);
21899             if(msg !== true){
21900                 this.markInvalid(msg);
21901                 return false;
21902             }
21903         }
21904         if(this.regex && !this.regex.test(value)){
21905             this.markInvalid(this.regexText);
21906             return false;
21907         }
21908         return true;
21909     },
21910
21911     /**
21912      * Selects text in this field
21913      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21914      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21915      */
21916     selectText : function(start, end){
21917         var v = this.getRawValue();
21918         if(v.length > 0){
21919             start = start === undefined ? 0 : start;
21920             end = end === undefined ? v.length : end;
21921             var d = this.el.dom;
21922             if(d.setSelectionRange){
21923                 d.setSelectionRange(start, end);
21924             }else if(d.createTextRange){
21925                 var range = d.createTextRange();
21926                 range.moveStart("character", start);
21927                 range.moveEnd("character", v.length-end);
21928                 range.select();
21929             }
21930         }
21931     },
21932
21933     /**
21934      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21935      * This only takes effect if grow = true, and fires the autosize event.
21936      */
21937     autoSize : function(){
21938         if(!this.grow || !this.rendered){
21939             return;
21940         }
21941         if(!this.metrics){
21942             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21943         }
21944         var el = this.el;
21945         var v = el.dom.value;
21946         var d = document.createElement('div');
21947         d.appendChild(document.createTextNode(v));
21948         v = d.innerHTML;
21949         d = null;
21950         v += "&#160;";
21951         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21952         this.el.setWidth(w);
21953         this.fireEvent("autosize", this, w);
21954     }
21955 });/*
21956  * Based on:
21957  * Ext JS Library 1.1.1
21958  * Copyright(c) 2006-2007, Ext JS, LLC.
21959  *
21960  * Originally Released Under LGPL - original licence link has changed is not relivant.
21961  *
21962  * Fork - LGPL
21963  * <script type="text/javascript">
21964  */
21965  
21966 /**
21967  * @class Roo.form.Hidden
21968  * @extends Roo.form.TextField
21969  * Simple Hidden element used on forms 
21970  * 
21971  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21972  * 
21973  * @constructor
21974  * Creates a new Hidden form element.
21975  * @param {Object} config Configuration options
21976  */
21977
21978
21979
21980 // easy hidden field...
21981 Roo.form.Hidden = function(config){
21982     Roo.form.Hidden.superclass.constructor.call(this, config);
21983 };
21984   
21985 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21986     fieldLabel:      '',
21987     inputType:      'hidden',
21988     width:          50,
21989     allowBlank:     true,
21990     labelSeparator: '',
21991     hidden:         true,
21992     itemCls :       'x-form-item-display-none'
21993
21994
21995 });
21996
21997
21998 /*
21999  * Based on:
22000  * Ext JS Library 1.1.1
22001  * Copyright(c) 2006-2007, Ext JS, LLC.
22002  *
22003  * Originally Released Under LGPL - original licence link has changed is not relivant.
22004  *
22005  * Fork - LGPL
22006  * <script type="text/javascript">
22007  */
22008  
22009 /**
22010  * @class Roo.form.TriggerField
22011  * @extends Roo.form.TextField
22012  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22013  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22014  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22015  * for which you can provide a custom implementation.  For example:
22016  * <pre><code>
22017 var trigger = new Roo.form.TriggerField();
22018 trigger.onTriggerClick = myTriggerFn;
22019 trigger.applyTo('my-field');
22020 </code></pre>
22021  *
22022  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22023  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22024  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22025  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22026  * @constructor
22027  * Create a new TriggerField.
22028  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22029  * to the base TextField)
22030  */
22031 Roo.form.TriggerField = function(config){
22032     this.mimicing = false;
22033     Roo.form.TriggerField.superclass.constructor.call(this, config);
22034 };
22035
22036 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22037     /**
22038      * @cfg {String} triggerClass A CSS class to apply to the trigger
22039      */
22040     /**
22041      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22042      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22043      */
22044     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22045     /**
22046      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22047      */
22048     hideTrigger:false,
22049
22050     /** @cfg {Boolean} grow @hide */
22051     /** @cfg {Number} growMin @hide */
22052     /** @cfg {Number} growMax @hide */
22053
22054     /**
22055      * @hide 
22056      * @method
22057      */
22058     autoSize: Roo.emptyFn,
22059     // private
22060     monitorTab : true,
22061     // private
22062     deferHeight : true,
22063
22064     
22065     actionMode : 'wrap',
22066     // private
22067     onResize : function(w, h){
22068         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22069         if(typeof w == 'number'){
22070             var x = w - this.trigger.getWidth();
22071             this.el.setWidth(this.adjustWidth('input', x));
22072             this.trigger.setStyle('left', x+'px');
22073         }
22074     },
22075
22076     // private
22077     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22078
22079     // private
22080     getResizeEl : function(){
22081         return this.wrap;
22082     },
22083
22084     // private
22085     getPositionEl : function(){
22086         return this.wrap;
22087     },
22088
22089     // private
22090     alignErrorIcon : function(){
22091         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22092     },
22093
22094     // private
22095     onRender : function(ct, position){
22096         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22097         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22098         this.trigger = this.wrap.createChild(this.triggerConfig ||
22099                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22100         if(this.hideTrigger){
22101             this.trigger.setDisplayed(false);
22102         }
22103         this.initTrigger();
22104         if(!this.width){
22105             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22106         }
22107     },
22108
22109     // private
22110     initTrigger : function(){
22111         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22112         this.trigger.addClassOnOver('x-form-trigger-over');
22113         this.trigger.addClassOnClick('x-form-trigger-click');
22114     },
22115
22116     // private
22117     onDestroy : function(){
22118         if(this.trigger){
22119             this.trigger.removeAllListeners();
22120             this.trigger.remove();
22121         }
22122         if(this.wrap){
22123             this.wrap.remove();
22124         }
22125         Roo.form.TriggerField.superclass.onDestroy.call(this);
22126     },
22127
22128     // private
22129     onFocus : function(){
22130         Roo.form.TriggerField.superclass.onFocus.call(this);
22131         if(!this.mimicing){
22132             this.wrap.addClass('x-trigger-wrap-focus');
22133             this.mimicing = true;
22134             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22135             if(this.monitorTab){
22136                 this.el.on("keydown", this.checkTab, this);
22137             }
22138         }
22139     },
22140
22141     // private
22142     checkTab : function(e){
22143         if(e.getKey() == e.TAB){
22144             this.triggerBlur();
22145         }
22146     },
22147
22148     // private
22149     onBlur : function(){
22150         // do nothing
22151     },
22152
22153     // private
22154     mimicBlur : function(e, t){
22155         if(!this.wrap.contains(t) && this.validateBlur()){
22156             this.triggerBlur();
22157         }
22158     },
22159
22160     // private
22161     triggerBlur : function(){
22162         this.mimicing = false;
22163         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22164         if(this.monitorTab){
22165             this.el.un("keydown", this.checkTab, this);
22166         }
22167         this.wrap.removeClass('x-trigger-wrap-focus');
22168         Roo.form.TriggerField.superclass.onBlur.call(this);
22169     },
22170
22171     // private
22172     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22173     validateBlur : function(e, t){
22174         return true;
22175     },
22176
22177     // private
22178     onDisable : function(){
22179         Roo.form.TriggerField.superclass.onDisable.call(this);
22180         if(this.wrap){
22181             this.wrap.addClass('x-item-disabled');
22182         }
22183     },
22184
22185     // private
22186     onEnable : function(){
22187         Roo.form.TriggerField.superclass.onEnable.call(this);
22188         if(this.wrap){
22189             this.wrap.removeClass('x-item-disabled');
22190         }
22191     },
22192
22193     // private
22194     onShow : function(){
22195         var ae = this.getActionEl();
22196         
22197         if(ae){
22198             ae.dom.style.display = '';
22199             ae.dom.style.visibility = 'visible';
22200         }
22201     },
22202
22203     // private
22204     
22205     onHide : function(){
22206         var ae = this.getActionEl();
22207         ae.dom.style.display = 'none';
22208     },
22209
22210     /**
22211      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22212      * by an implementing function.
22213      * @method
22214      * @param {EventObject} e
22215      */
22216     onTriggerClick : Roo.emptyFn
22217 });
22218
22219 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22220 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22221 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22222 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22223     initComponent : function(){
22224         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22225
22226         this.triggerConfig = {
22227             tag:'span', cls:'x-form-twin-triggers', cn:[
22228             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22229             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22230         ]};
22231     },
22232
22233     getTrigger : function(index){
22234         return this.triggers[index];
22235     },
22236
22237     initTrigger : function(){
22238         var ts = this.trigger.select('.x-form-trigger', true);
22239         this.wrap.setStyle('overflow', 'hidden');
22240         var triggerField = this;
22241         ts.each(function(t, all, index){
22242             t.hide = function(){
22243                 var w = triggerField.wrap.getWidth();
22244                 this.dom.style.display = 'none';
22245                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22246             };
22247             t.show = function(){
22248                 var w = triggerField.wrap.getWidth();
22249                 this.dom.style.display = '';
22250                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22251             };
22252             var triggerIndex = 'Trigger'+(index+1);
22253
22254             if(this['hide'+triggerIndex]){
22255                 t.dom.style.display = 'none';
22256             }
22257             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22258             t.addClassOnOver('x-form-trigger-over');
22259             t.addClassOnClick('x-form-trigger-click');
22260         }, this);
22261         this.triggers = ts.elements;
22262     },
22263
22264     onTrigger1Click : Roo.emptyFn,
22265     onTrigger2Click : Roo.emptyFn
22266 });/*
22267  * Based on:
22268  * Ext JS Library 1.1.1
22269  * Copyright(c) 2006-2007, Ext JS, LLC.
22270  *
22271  * Originally Released Under LGPL - original licence link has changed is not relivant.
22272  *
22273  * Fork - LGPL
22274  * <script type="text/javascript">
22275  */
22276  
22277 /**
22278  * @class Roo.form.TextArea
22279  * @extends Roo.form.TextField
22280  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22281  * support for auto-sizing.
22282  * @constructor
22283  * Creates a new TextArea
22284  * @param {Object} config Configuration options
22285  */
22286 Roo.form.TextArea = function(config){
22287     Roo.form.TextArea.superclass.constructor.call(this, config);
22288     // these are provided exchanges for backwards compat
22289     // minHeight/maxHeight were replaced by growMin/growMax to be
22290     // compatible with TextField growing config values
22291     if(this.minHeight !== undefined){
22292         this.growMin = this.minHeight;
22293     }
22294     if(this.maxHeight !== undefined){
22295         this.growMax = this.maxHeight;
22296     }
22297 };
22298
22299 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22300     /**
22301      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22302      */
22303     growMin : 60,
22304     /**
22305      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22306      */
22307     growMax: 1000,
22308     /**
22309      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22310      * in the field (equivalent to setting overflow: hidden, defaults to false)
22311      */
22312     preventScrollbars: false,
22313     /**
22314      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22315      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22316      */
22317
22318     // private
22319     onRender : function(ct, position){
22320         if(!this.el){
22321             this.defaultAutoCreate = {
22322                 tag: "textarea",
22323                 style:"width:300px;height:60px;",
22324                 autocomplete: "off"
22325             };
22326         }
22327         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22328         if(this.grow){
22329             this.textSizeEl = Roo.DomHelper.append(document.body, {
22330                 tag: "pre", cls: "x-form-grow-sizer"
22331             });
22332             if(this.preventScrollbars){
22333                 this.el.setStyle("overflow", "hidden");
22334             }
22335             this.el.setHeight(this.growMin);
22336         }
22337     },
22338
22339     onDestroy : function(){
22340         if(this.textSizeEl){
22341             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22342         }
22343         Roo.form.TextArea.superclass.onDestroy.call(this);
22344     },
22345
22346     // private
22347     onKeyUp : function(e){
22348         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22349             this.autoSize();
22350         }
22351     },
22352
22353     /**
22354      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22355      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22356      */
22357     autoSize : function(){
22358         if(!this.grow || !this.textSizeEl){
22359             return;
22360         }
22361         var el = this.el;
22362         var v = el.dom.value;
22363         var ts = this.textSizeEl;
22364
22365         ts.innerHTML = '';
22366         ts.appendChild(document.createTextNode(v));
22367         v = ts.innerHTML;
22368
22369         Roo.fly(ts).setWidth(this.el.getWidth());
22370         if(v.length < 1){
22371             v = "&#160;&#160;";
22372         }else{
22373             if(Roo.isIE){
22374                 v = v.replace(/\n/g, '<p>&#160;</p>');
22375             }
22376             v += "&#160;\n&#160;";
22377         }
22378         ts.innerHTML = v;
22379         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22380         if(h != this.lastHeight){
22381             this.lastHeight = h;
22382             this.el.setHeight(h);
22383             this.fireEvent("autosize", this, h);
22384         }
22385     }
22386 });/*
22387  * Based on:
22388  * Ext JS Library 1.1.1
22389  * Copyright(c) 2006-2007, Ext JS, LLC.
22390  *
22391  * Originally Released Under LGPL - original licence link has changed is not relivant.
22392  *
22393  * Fork - LGPL
22394  * <script type="text/javascript">
22395  */
22396  
22397
22398 /**
22399  * @class Roo.form.NumberField
22400  * @extends Roo.form.TextField
22401  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22402  * @constructor
22403  * Creates a new NumberField
22404  * @param {Object} config Configuration options
22405  */
22406 Roo.form.NumberField = function(config){
22407     Roo.form.NumberField.superclass.constructor.call(this, config);
22408 };
22409
22410 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22411     /**
22412      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22413      */
22414     fieldClass: "x-form-field x-form-num-field",
22415     /**
22416      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22417      */
22418     allowDecimals : true,
22419     /**
22420      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22421      */
22422     decimalSeparator : ".",
22423     /**
22424      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22425      */
22426     decimalPrecision : 2,
22427     /**
22428      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22429      */
22430     allowNegative : true,
22431     /**
22432      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22433      */
22434     minValue : Number.NEGATIVE_INFINITY,
22435     /**
22436      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22437      */
22438     maxValue : Number.MAX_VALUE,
22439     /**
22440      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22441      */
22442     minText : "The minimum value for this field is {0}",
22443     /**
22444      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22445      */
22446     maxText : "The maximum value for this field is {0}",
22447     /**
22448      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22449      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22450      */
22451     nanText : "{0} is not a valid number",
22452
22453     // private
22454     initEvents : function(){
22455         Roo.form.NumberField.superclass.initEvents.call(this);
22456         var allowed = "0123456789";
22457         if(this.allowDecimals){
22458             allowed += this.decimalSeparator;
22459         }
22460         if(this.allowNegative){
22461             allowed += "-";
22462         }
22463         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22464         var keyPress = function(e){
22465             var k = e.getKey();
22466             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22467                 return;
22468             }
22469             var c = e.getCharCode();
22470             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22471                 e.stopEvent();
22472             }
22473         };
22474         this.el.on("keypress", keyPress, this);
22475     },
22476
22477     // private
22478     validateValue : function(value){
22479         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22480             return false;
22481         }
22482         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22483              return true;
22484         }
22485         var num = this.parseValue(value);
22486         if(isNaN(num)){
22487             this.markInvalid(String.format(this.nanText, value));
22488             return false;
22489         }
22490         if(num < this.minValue){
22491             this.markInvalid(String.format(this.minText, this.minValue));
22492             return false;
22493         }
22494         if(num > this.maxValue){
22495             this.markInvalid(String.format(this.maxText, this.maxValue));
22496             return false;
22497         }
22498         return true;
22499     },
22500
22501     getValue : function(){
22502         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22503     },
22504
22505     // private
22506     parseValue : function(value){
22507         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22508         return isNaN(value) ? '' : value;
22509     },
22510
22511     // private
22512     fixPrecision : function(value){
22513         var nan = isNaN(value);
22514         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22515             return nan ? '' : value;
22516         }
22517         return parseFloat(value).toFixed(this.decimalPrecision);
22518     },
22519
22520     setValue : function(v){
22521         v = this.fixPrecision(v);
22522         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22523     },
22524
22525     // private
22526     decimalPrecisionFcn : function(v){
22527         return Math.floor(v);
22528     },
22529
22530     beforeBlur : function(){
22531         var v = this.parseValue(this.getRawValue());
22532         if(v){
22533             this.setValue(v);
22534         }
22535     }
22536 });/*
22537  * Based on:
22538  * Ext JS Library 1.1.1
22539  * Copyright(c) 2006-2007, Ext JS, LLC.
22540  *
22541  * Originally Released Under LGPL - original licence link has changed is not relivant.
22542  *
22543  * Fork - LGPL
22544  * <script type="text/javascript">
22545  */
22546  
22547 /**
22548  * @class Roo.form.DateField
22549  * @extends Roo.form.TriggerField
22550  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22551 * @constructor
22552 * Create a new DateField
22553 * @param {Object} config
22554  */
22555 Roo.form.DateField = function(config){
22556     Roo.form.DateField.superclass.constructor.call(this, config);
22557     
22558       this.addEvents({
22559          
22560         /**
22561          * @event select
22562          * Fires when a date is selected
22563              * @param {Roo.form.DateField} combo This combo box
22564              * @param {Date} date The date selected
22565              */
22566         'select' : true
22567          
22568     });
22569     
22570     
22571     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22572     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22573     this.ddMatch = null;
22574     if(this.disabledDates){
22575         var dd = this.disabledDates;
22576         var re = "(?:";
22577         for(var i = 0; i < dd.length; i++){
22578             re += dd[i];
22579             if(i != dd.length-1) re += "|";
22580         }
22581         this.ddMatch = new RegExp(re + ")");
22582     }
22583 };
22584
22585 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22586     /**
22587      * @cfg {String} format
22588      * The default date format string which can be overriden for localization support.  The format must be
22589      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22590      */
22591     format : "m/d/y",
22592     /**
22593      * @cfg {String} altFormats
22594      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22595      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22596      */
22597     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22598     /**
22599      * @cfg {Array} disabledDays
22600      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22601      */
22602     disabledDays : null,
22603     /**
22604      * @cfg {String} disabledDaysText
22605      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22606      */
22607     disabledDaysText : "Disabled",
22608     /**
22609      * @cfg {Array} disabledDates
22610      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22611      * expression so they are very powerful. Some examples:
22612      * <ul>
22613      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22614      * <li>["03/08", "09/16"] would disable those days for every year</li>
22615      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22616      * <li>["03/../2006"] would disable every day in March 2006</li>
22617      * <li>["^03"] would disable every day in every March</li>
22618      * </ul>
22619      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22620      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22621      */
22622     disabledDates : null,
22623     /**
22624      * @cfg {String} disabledDatesText
22625      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22626      */
22627     disabledDatesText : "Disabled",
22628     /**
22629      * @cfg {Date/String} minValue
22630      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22631      * valid format (defaults to null).
22632      */
22633     minValue : null,
22634     /**
22635      * @cfg {Date/String} maxValue
22636      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22637      * valid format (defaults to null).
22638      */
22639     maxValue : null,
22640     /**
22641      * @cfg {String} minText
22642      * The error text to display when the date in the cell is before minValue (defaults to
22643      * 'The date in this field must be after {minValue}').
22644      */
22645     minText : "The date in this field must be equal to or after {0}",
22646     /**
22647      * @cfg {String} maxText
22648      * The error text to display when the date in the cell is after maxValue (defaults to
22649      * 'The date in this field must be before {maxValue}').
22650      */
22651     maxText : "The date in this field must be equal to or before {0}",
22652     /**
22653      * @cfg {String} invalidText
22654      * The error text to display when the date in the field is invalid (defaults to
22655      * '{value} is not a valid date - it must be in the format {format}').
22656      */
22657     invalidText : "{0} is not a valid date - it must be in the format {1}",
22658     /**
22659      * @cfg {String} triggerClass
22660      * An additional CSS class used to style the trigger button.  The trigger will always get the
22661      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22662      * which displays a calendar icon).
22663      */
22664     triggerClass : 'x-form-date-trigger',
22665     
22666
22667     /**
22668      * @cfg {bool} useIso
22669      * if enabled, then the date field will use a hidden field to store the 
22670      * real value as iso formated date. default (false)
22671      */ 
22672     useIso : false,
22673     /**
22674      * @cfg {String/Object} autoCreate
22675      * A DomHelper element spec, or true for a default element spec (defaults to
22676      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22677      */ 
22678     // private
22679     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22680     
22681     // private
22682     hiddenField: false,
22683     
22684     onRender : function(ct, position)
22685     {
22686         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22687         if (this.useIso) {
22688             this.el.dom.removeAttribute('name'); 
22689             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22690                     'before', true);
22691             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22692             // prevent input submission
22693             this.hiddenName = this.name;
22694         }
22695             
22696             
22697     },
22698     
22699     // private
22700     validateValue : function(value)
22701     {
22702         value = this.formatDate(value);
22703         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22704             return false;
22705         }
22706         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22707              return true;
22708         }
22709         var svalue = value;
22710         value = this.parseDate(value);
22711         if(!value){
22712             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22713             return false;
22714         }
22715         var time = value.getTime();
22716         if(this.minValue && time < this.minValue.getTime()){
22717             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22718             return false;
22719         }
22720         if(this.maxValue && time > this.maxValue.getTime()){
22721             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22722             return false;
22723         }
22724         if(this.disabledDays){
22725             var day = value.getDay();
22726             for(var i = 0; i < this.disabledDays.length; i++) {
22727                 if(day === this.disabledDays[i]){
22728                     this.markInvalid(this.disabledDaysText);
22729                     return false;
22730                 }
22731             }
22732         }
22733         var fvalue = this.formatDate(value);
22734         if(this.ddMatch && this.ddMatch.test(fvalue)){
22735             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22736             return false;
22737         }
22738         return true;
22739     },
22740
22741     // private
22742     // Provides logic to override the default TriggerField.validateBlur which just returns true
22743     validateBlur : function(){
22744         return !this.menu || !this.menu.isVisible();
22745     },
22746
22747     /**
22748      * Returns the current date value of the date field.
22749      * @return {Date} The date value
22750      */
22751     getValue : function(){
22752         
22753         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22754     },
22755
22756     /**
22757      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22758      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22759      * (the default format used is "m/d/y").
22760      * <br />Usage:
22761      * <pre><code>
22762 //All of these calls set the same date value (May 4, 2006)
22763
22764 //Pass a date object:
22765 var dt = new Date('5/4/06');
22766 dateField.setValue(dt);
22767
22768 //Pass a date string (default format):
22769 dateField.setValue('5/4/06');
22770
22771 //Pass a date string (custom format):
22772 dateField.format = 'Y-m-d';
22773 dateField.setValue('2006-5-4');
22774 </code></pre>
22775      * @param {String/Date} date The date or valid date string
22776      */
22777     setValue : function(date){
22778         if (this.hiddenField) {
22779             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22780         }
22781         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22782     },
22783
22784     // private
22785     parseDate : function(value){
22786         if(!value || value instanceof Date){
22787             return value;
22788         }
22789         var v = Date.parseDate(value, this.format);
22790         if(!v && this.altFormats){
22791             if(!this.altFormatsArray){
22792                 this.altFormatsArray = this.altFormats.split("|");
22793             }
22794             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22795                 v = Date.parseDate(value, this.altFormatsArray[i]);
22796             }
22797         }
22798         return v;
22799     },
22800
22801     // private
22802     formatDate : function(date, fmt){
22803         return (!date || !(date instanceof Date)) ?
22804                date : date.dateFormat(fmt || this.format);
22805     },
22806
22807     // private
22808     menuListeners : {
22809         select: function(m, d){
22810             this.setValue(d);
22811             this.fireEvent('select', this, d);
22812         },
22813         show : function(){ // retain focus styling
22814             this.onFocus();
22815         },
22816         hide : function(){
22817             this.focus.defer(10, this);
22818             var ml = this.menuListeners;
22819             this.menu.un("select", ml.select,  this);
22820             this.menu.un("show", ml.show,  this);
22821             this.menu.un("hide", ml.hide,  this);
22822         }
22823     },
22824
22825     // private
22826     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22827     onTriggerClick : function(){
22828         if(this.disabled){
22829             return;
22830         }
22831         if(this.menu == null){
22832             this.menu = new Roo.menu.DateMenu();
22833         }
22834         Roo.apply(this.menu.picker,  {
22835             showClear: this.allowBlank,
22836             minDate : this.minValue,
22837             maxDate : this.maxValue,
22838             disabledDatesRE : this.ddMatch,
22839             disabledDatesText : this.disabledDatesText,
22840             disabledDays : this.disabledDays,
22841             disabledDaysText : this.disabledDaysText,
22842             format : this.format,
22843             minText : String.format(this.minText, this.formatDate(this.minValue)),
22844             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22845         });
22846         this.menu.on(Roo.apply({}, this.menuListeners, {
22847             scope:this
22848         }));
22849         this.menu.picker.setValue(this.getValue() || new Date());
22850         this.menu.show(this.el, "tl-bl?");
22851     },
22852
22853     beforeBlur : function(){
22854         var v = this.parseDate(this.getRawValue());
22855         if(v){
22856             this.setValue(v);
22857         }
22858     }
22859
22860     /** @cfg {Boolean} grow @hide */
22861     /** @cfg {Number} growMin @hide */
22862     /** @cfg {Number} growMax @hide */
22863     /**
22864      * @hide
22865      * @method autoSize
22866      */
22867 });/*
22868  * Based on:
22869  * Ext JS Library 1.1.1
22870  * Copyright(c) 2006-2007, Ext JS, LLC.
22871  *
22872  * Originally Released Under LGPL - original licence link has changed is not relivant.
22873  *
22874  * Fork - LGPL
22875  * <script type="text/javascript">
22876  */
22877  
22878
22879 /**
22880  * @class Roo.form.ComboBox
22881  * @extends Roo.form.TriggerField
22882  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22883  * @constructor
22884  * Create a new ComboBox.
22885  * @param {Object} config Configuration options
22886  */
22887 Roo.form.ComboBox = function(config){
22888     Roo.form.ComboBox.superclass.constructor.call(this, config);
22889     this.addEvents({
22890         /**
22891          * @event expand
22892          * Fires when the dropdown list is expanded
22893              * @param {Roo.form.ComboBox} combo This combo box
22894              */
22895         'expand' : true,
22896         /**
22897          * @event collapse
22898          * Fires when the dropdown list is collapsed
22899              * @param {Roo.form.ComboBox} combo This combo box
22900              */
22901         'collapse' : true,
22902         /**
22903          * @event beforeselect
22904          * Fires before a list item is selected. Return false to cancel the selection.
22905              * @param {Roo.form.ComboBox} combo This combo box
22906              * @param {Roo.data.Record} record The data record returned from the underlying store
22907              * @param {Number} index The index of the selected item in the dropdown list
22908              */
22909         'beforeselect' : true,
22910         /**
22911          * @event select
22912          * Fires when a list item is selected
22913              * @param {Roo.form.ComboBox} combo This combo box
22914              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22915              * @param {Number} index The index of the selected item in the dropdown list
22916              */
22917         'select' : true,
22918         /**
22919          * @event beforequery
22920          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22921          * The event object passed has these properties:
22922              * @param {Roo.form.ComboBox} combo This combo box
22923              * @param {String} query The query
22924              * @param {Boolean} forceAll true to force "all" query
22925              * @param {Boolean} cancel true to cancel the query
22926              * @param {Object} e The query event object
22927              */
22928         'beforequery': true,
22929          /**
22930          * @event add
22931          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22932              * @param {Roo.form.ComboBox} combo This combo box
22933              */
22934         'add' : true,
22935         /**
22936          * @event edit
22937          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22938              * @param {Roo.form.ComboBox} combo This combo box
22939              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22940              */
22941         'edit' : true
22942         
22943         
22944     });
22945     if(this.transform){
22946         this.allowDomMove = false;
22947         var s = Roo.getDom(this.transform);
22948         if(!this.hiddenName){
22949             this.hiddenName = s.name;
22950         }
22951         if(!this.store){
22952             this.mode = 'local';
22953             var d = [], opts = s.options;
22954             for(var i = 0, len = opts.length;i < len; i++){
22955                 var o = opts[i];
22956                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22957                 if(o.selected) {
22958                     this.value = value;
22959                 }
22960                 d.push([value, o.text]);
22961             }
22962             this.store = new Roo.data.SimpleStore({
22963                 'id': 0,
22964                 fields: ['value', 'text'],
22965                 data : d
22966             });
22967             this.valueField = 'value';
22968             this.displayField = 'text';
22969         }
22970         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22971         if(!this.lazyRender){
22972             this.target = true;
22973             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22974             s.parentNode.removeChild(s); // remove it
22975             this.render(this.el.parentNode);
22976         }else{
22977             s.parentNode.removeChild(s); // remove it
22978         }
22979
22980     }
22981     if (this.store) {
22982         this.store = Roo.factory(this.store, Roo.data);
22983     }
22984     
22985     this.selectedIndex = -1;
22986     if(this.mode == 'local'){
22987         if(config.queryDelay === undefined){
22988             this.queryDelay = 10;
22989         }
22990         if(config.minChars === undefined){
22991             this.minChars = 0;
22992         }
22993     }
22994 };
22995
22996 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22997     /**
22998      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22999      */
23000     /**
23001      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23002      * rendering into an Roo.Editor, defaults to false)
23003      */
23004     /**
23005      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23006      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23007      */
23008     /**
23009      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23010      */
23011     /**
23012      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23013      * the dropdown list (defaults to undefined, with no header element)
23014      */
23015
23016      /**
23017      * @cfg {String/Roo.Template} tpl The template to use to render the output
23018      */
23019      
23020     // private
23021     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23022     /**
23023      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23024      */
23025     listWidth: undefined,
23026     /**
23027      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23028      * mode = 'remote' or 'text' if mode = 'local')
23029      */
23030     displayField: undefined,
23031     /**
23032      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23033      * mode = 'remote' or 'value' if mode = 'local'). 
23034      * Note: use of a valueField requires the user make a selection
23035      * in order for a value to be mapped.
23036      */
23037     valueField: undefined,
23038     
23039     
23040     /**
23041      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23042      * field's data value (defaults to the underlying DOM element's name)
23043      */
23044     hiddenName: undefined,
23045     /**
23046      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23047      */
23048     listClass: '',
23049     /**
23050      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23051      */
23052     selectedClass: 'x-combo-selected',
23053     /**
23054      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23055      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23056      * which displays a downward arrow icon).
23057      */
23058     triggerClass : 'x-form-arrow-trigger',
23059     /**
23060      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23061      */
23062     shadow:'sides',
23063     /**
23064      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23065      * anchor positions (defaults to 'tl-bl')
23066      */
23067     listAlign: 'tl-bl?',
23068     /**
23069      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23070      */
23071     maxHeight: 300,
23072     /**
23073      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23074      * query specified by the allQuery config option (defaults to 'query')
23075      */
23076     triggerAction: 'query',
23077     /**
23078      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23079      * (defaults to 4, does not apply if editable = false)
23080      */
23081     minChars : 4,
23082     /**
23083      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23084      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23085      */
23086     typeAhead: false,
23087     /**
23088      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23089      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23090      */
23091     queryDelay: 500,
23092     /**
23093      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23094      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23095      */
23096     pageSize: 0,
23097     /**
23098      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23099      * when editable = true (defaults to false)
23100      */
23101     selectOnFocus:false,
23102     /**
23103      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23104      */
23105     queryParam: 'query',
23106     /**
23107      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23108      * when mode = 'remote' (defaults to 'Loading...')
23109      */
23110     loadingText: 'Loading...',
23111     /**
23112      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23113      */
23114     resizable: false,
23115     /**
23116      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23117      */
23118     handleHeight : 8,
23119     /**
23120      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23121      * traditional select (defaults to true)
23122      */
23123     editable: true,
23124     /**
23125      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23126      */
23127     allQuery: '',
23128     /**
23129      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23130      */
23131     mode: 'remote',
23132     /**
23133      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23134      * listWidth has a higher value)
23135      */
23136     minListWidth : 70,
23137     /**
23138      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23139      * allow the user to set arbitrary text into the field (defaults to false)
23140      */
23141     forceSelection:false,
23142     /**
23143      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23144      * if typeAhead = true (defaults to 250)
23145      */
23146     typeAheadDelay : 250,
23147     /**
23148      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23149      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23150      */
23151     valueNotFoundText : undefined,
23152     /**
23153      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23154      */
23155     blockFocus : false,
23156     
23157     /**
23158      * @cfg {Boolean} disableClear Disable showing of clear button.
23159      */
23160     disableClear : false,
23161     /**
23162      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23163      */
23164     alwaysQuery : false,
23165     
23166     //private
23167     addicon : false,
23168     editicon: false,
23169     
23170     // element that contains real text value.. (when hidden is used..)
23171      
23172     // private
23173     onRender : function(ct, position){
23174         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23175         if(this.hiddenName){
23176             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23177                     'before', true);
23178             this.hiddenField.value =
23179                 this.hiddenValue !== undefined ? this.hiddenValue :
23180                 this.value !== undefined ? this.value : '';
23181
23182             // prevent input submission
23183             this.el.dom.removeAttribute('name');
23184              
23185              
23186         }
23187         if(Roo.isGecko){
23188             this.el.dom.setAttribute('autocomplete', 'off');
23189         }
23190
23191         var cls = 'x-combo-list';
23192
23193         this.list = new Roo.Layer({
23194             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23195         });
23196
23197         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23198         this.list.setWidth(lw);
23199         this.list.swallowEvent('mousewheel');
23200         this.assetHeight = 0;
23201
23202         if(this.title){
23203             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23204             this.assetHeight += this.header.getHeight();
23205         }
23206
23207         this.innerList = this.list.createChild({cls:cls+'-inner'});
23208         this.innerList.on('mouseover', this.onViewOver, this);
23209         this.innerList.on('mousemove', this.onViewMove, this);
23210         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23211         
23212         if(this.allowBlank && !this.pageSize && !this.disableClear){
23213             this.footer = this.list.createChild({cls:cls+'-ft'});
23214             this.pageTb = new Roo.Toolbar(this.footer);
23215            
23216         }
23217         if(this.pageSize){
23218             this.footer = this.list.createChild({cls:cls+'-ft'});
23219             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23220                     {pageSize: this.pageSize});
23221             
23222         }
23223         
23224         if (this.pageTb && this.allowBlank && !this.disableClear) {
23225             var _this = this;
23226             this.pageTb.add(new Roo.Toolbar.Fill(), {
23227                 cls: 'x-btn-icon x-btn-clear',
23228                 text: '&#160;',
23229                 handler: function()
23230                 {
23231                     _this.collapse();
23232                     _this.clearValue();
23233                     _this.onSelect(false, -1);
23234                 }
23235             });
23236         }
23237         if (this.footer) {
23238             this.assetHeight += this.footer.getHeight();
23239         }
23240         
23241
23242         if(!this.tpl){
23243             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23244         }
23245
23246         this.view = new Roo.View(this.innerList, this.tpl, {
23247             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23248         });
23249
23250         this.view.on('click', this.onViewClick, this);
23251
23252         this.store.on('beforeload', this.onBeforeLoad, this);
23253         this.store.on('load', this.onLoad, this);
23254         this.store.on('loadexception', this.onLoadException, this);
23255
23256         if(this.resizable){
23257             this.resizer = new Roo.Resizable(this.list,  {
23258                pinned:true, handles:'se'
23259             });
23260             this.resizer.on('resize', function(r, w, h){
23261                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23262                 this.listWidth = w;
23263                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23264                 this.restrictHeight();
23265             }, this);
23266             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23267         }
23268         if(!this.editable){
23269             this.editable = true;
23270             this.setEditable(false);
23271         }  
23272         
23273         
23274         if (typeof(this.events.add.listeners) != 'undefined') {
23275             
23276             this.addicon = this.wrap.createChild(
23277                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23278        
23279             this.addicon.on('click', function(e) {
23280                 this.fireEvent('add', this);
23281             }, this);
23282         }
23283         if (typeof(this.events.edit.listeners) != 'undefined') {
23284             
23285             this.editicon = this.wrap.createChild(
23286                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23287             if (this.addicon) {
23288                 this.editicon.setStyle('margin-left', '40px');
23289             }
23290             this.editicon.on('click', function(e) {
23291                 
23292                 // we fire even  if inothing is selected..
23293                 this.fireEvent('edit', this, this.lastData );
23294                 
23295             }, this);
23296         }
23297         
23298         
23299         
23300     },
23301
23302     // private
23303     initEvents : function(){
23304         Roo.form.ComboBox.superclass.initEvents.call(this);
23305
23306         this.keyNav = new Roo.KeyNav(this.el, {
23307             "up" : function(e){
23308                 this.inKeyMode = true;
23309                 this.selectPrev();
23310             },
23311
23312             "down" : function(e){
23313                 if(!this.isExpanded()){
23314                     this.onTriggerClick();
23315                 }else{
23316                     this.inKeyMode = true;
23317                     this.selectNext();
23318                 }
23319             },
23320
23321             "enter" : function(e){
23322                 this.onViewClick();
23323                 //return true;
23324             },
23325
23326             "esc" : function(e){
23327                 this.collapse();
23328             },
23329
23330             "tab" : function(e){
23331                 this.onViewClick(false);
23332                 this.fireEvent("specialkey", this, e);
23333                 return true;
23334             },
23335
23336             scope : this,
23337
23338             doRelay : function(foo, bar, hname){
23339                 if(hname == 'down' || this.scope.isExpanded()){
23340                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23341                 }
23342                 return true;
23343             },
23344
23345             forceKeyDown: true
23346         });
23347         this.queryDelay = Math.max(this.queryDelay || 10,
23348                 this.mode == 'local' ? 10 : 250);
23349         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23350         if(this.typeAhead){
23351             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23352         }
23353         if(this.editable !== false){
23354             this.el.on("keyup", this.onKeyUp, this);
23355         }
23356         if(this.forceSelection){
23357             this.on('blur', this.doForce, this);
23358         }
23359     },
23360
23361     onDestroy : function(){
23362         if(this.view){
23363             this.view.setStore(null);
23364             this.view.el.removeAllListeners();
23365             this.view.el.remove();
23366             this.view.purgeListeners();
23367         }
23368         if(this.list){
23369             this.list.destroy();
23370         }
23371         if(this.store){
23372             this.store.un('beforeload', this.onBeforeLoad, this);
23373             this.store.un('load', this.onLoad, this);
23374             this.store.un('loadexception', this.onLoadException, this);
23375         }
23376         Roo.form.ComboBox.superclass.onDestroy.call(this);
23377     },
23378
23379     // private
23380     fireKey : function(e){
23381         if(e.isNavKeyPress() && !this.list.isVisible()){
23382             this.fireEvent("specialkey", this, e);
23383         }
23384     },
23385
23386     // private
23387     onResize: function(w, h){
23388         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23389         
23390         if(typeof w != 'number'){
23391             // we do not handle it!?!?
23392             return;
23393         }
23394         var tw = this.trigger.getWidth();
23395         tw += this.addicon ? this.addicon.getWidth() : 0;
23396         tw += this.editicon ? this.editicon.getWidth() : 0;
23397         var x = w - tw;
23398         this.el.setWidth( this.adjustWidth('input', x));
23399             
23400         this.trigger.setStyle('left', x+'px');
23401         
23402         if(this.list && this.listWidth === undefined){
23403             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23404             this.list.setWidth(lw);
23405             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23406         }
23407         
23408     
23409         
23410     },
23411
23412     /**
23413      * Allow or prevent the user from directly editing the field text.  If false is passed,
23414      * the user will only be able to select from the items defined in the dropdown list.  This method
23415      * is the runtime equivalent of setting the 'editable' config option at config time.
23416      * @param {Boolean} value True to allow the user to directly edit the field text
23417      */
23418     setEditable : function(value){
23419         if(value == this.editable){
23420             return;
23421         }
23422         this.editable = value;
23423         if(!value){
23424             this.el.dom.setAttribute('readOnly', true);
23425             this.el.on('mousedown', this.onTriggerClick,  this);
23426             this.el.addClass('x-combo-noedit');
23427         }else{
23428             this.el.dom.setAttribute('readOnly', false);
23429             this.el.un('mousedown', this.onTriggerClick,  this);
23430             this.el.removeClass('x-combo-noedit');
23431         }
23432     },
23433
23434     // private
23435     onBeforeLoad : function(){
23436         if(!this.hasFocus){
23437             return;
23438         }
23439         this.innerList.update(this.loadingText ?
23440                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23441         this.restrictHeight();
23442         this.selectedIndex = -1;
23443     },
23444
23445     // private
23446     onLoad : function(){
23447         if(!this.hasFocus){
23448             return;
23449         }
23450         if(this.store.getCount() > 0){
23451             this.expand();
23452             this.restrictHeight();
23453             if(this.lastQuery == this.allQuery){
23454                 if(this.editable){
23455                     this.el.dom.select();
23456                 }
23457                 if(!this.selectByValue(this.value, true)){
23458                     this.select(0, true);
23459                 }
23460             }else{
23461                 this.selectNext();
23462                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23463                     this.taTask.delay(this.typeAheadDelay);
23464                 }
23465             }
23466         }else{
23467             this.onEmptyResults();
23468         }
23469         //this.el.focus();
23470     },
23471     // private
23472     onLoadException : function()
23473     {
23474         this.collapse();
23475         Roo.log(this.store.reader.jsonData);
23476         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23477             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23478         }
23479         
23480         
23481     },
23482     // private
23483     onTypeAhead : function(){
23484         if(this.store.getCount() > 0){
23485             var r = this.store.getAt(0);
23486             var newValue = r.data[this.displayField];
23487             var len = newValue.length;
23488             var selStart = this.getRawValue().length;
23489             if(selStart != len){
23490                 this.setRawValue(newValue);
23491                 this.selectText(selStart, newValue.length);
23492             }
23493         }
23494     },
23495
23496     // private
23497     onSelect : function(record, index){
23498         if(this.fireEvent('beforeselect', this, record, index) !== false){
23499             this.setFromData(index > -1 ? record.data : false);
23500             this.collapse();
23501             this.fireEvent('select', this, record, index);
23502         }
23503     },
23504
23505     /**
23506      * Returns the currently selected field value or empty string if no value is set.
23507      * @return {String} value The selected value
23508      */
23509     getValue : function(){
23510         if(this.valueField){
23511             return typeof this.value != 'undefined' ? this.value : '';
23512         }else{
23513             return Roo.form.ComboBox.superclass.getValue.call(this);
23514         }
23515     },
23516
23517     /**
23518      * Clears any text/value currently set in the field
23519      */
23520     clearValue : function(){
23521         if(this.hiddenField){
23522             this.hiddenField.value = '';
23523         }
23524         this.value = '';
23525         this.setRawValue('');
23526         this.lastSelectionText = '';
23527         this.applyEmptyText();
23528     },
23529
23530     /**
23531      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23532      * will be displayed in the field.  If the value does not match the data value of an existing item,
23533      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23534      * Otherwise the field will be blank (although the value will still be set).
23535      * @param {String} value The value to match
23536      */
23537     setValue : function(v){
23538         var text = v;
23539         if(this.valueField){
23540             var r = this.findRecord(this.valueField, v);
23541             if(r){
23542                 text = r.data[this.displayField];
23543             }else if(this.valueNotFoundText !== undefined){
23544                 text = this.valueNotFoundText;
23545             }
23546         }
23547         this.lastSelectionText = text;
23548         if(this.hiddenField){
23549             this.hiddenField.value = v;
23550         }
23551         Roo.form.ComboBox.superclass.setValue.call(this, text);
23552         this.value = v;
23553     },
23554     /**
23555      * @property {Object} the last set data for the element
23556      */
23557     
23558     lastData : false,
23559     /**
23560      * Sets the value of the field based on a object which is related to the record format for the store.
23561      * @param {Object} value the value to set as. or false on reset?
23562      */
23563     setFromData : function(o){
23564         var dv = ''; // display value
23565         var vv = ''; // value value..
23566         this.lastData = o;
23567         if (this.displayField) {
23568             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23569         } else {
23570             // this is an error condition!!!
23571             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23572         }
23573         
23574         if(this.valueField){
23575             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23576         }
23577         if(this.hiddenField){
23578             this.hiddenField.value = vv;
23579             
23580             this.lastSelectionText = dv;
23581             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23582             this.value = vv;
23583             return;
23584         }
23585         // no hidden field.. - we store the value in 'value', but still display
23586         // display field!!!!
23587         this.lastSelectionText = dv;
23588         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23589         this.value = vv;
23590         
23591         
23592     },
23593     // private
23594     reset : function(){
23595         // overridden so that last data is reset..
23596         this.setValue(this.originalValue);
23597         this.clearInvalid();
23598         this.lastData = false;
23599     },
23600     // private
23601     findRecord : function(prop, value){
23602         var record;
23603         if(this.store.getCount() > 0){
23604             this.store.each(function(r){
23605                 if(r.data[prop] == value){
23606                     record = r;
23607                     return false;
23608                 }
23609                 return true;
23610             });
23611         }
23612         return record;
23613     },
23614     
23615     getName: function()
23616     {
23617         // returns hidden if it's set..
23618         if (!this.rendered) {return ''};
23619         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23620         
23621     },
23622     // private
23623     onViewMove : function(e, t){
23624         this.inKeyMode = false;
23625     },
23626
23627     // private
23628     onViewOver : function(e, t){
23629         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23630             return;
23631         }
23632         var item = this.view.findItemFromChild(t);
23633         if(item){
23634             var index = this.view.indexOf(item);
23635             this.select(index, false);
23636         }
23637     },
23638
23639     // private
23640     onViewClick : function(doFocus)
23641     {
23642         var index = this.view.getSelectedIndexes()[0];
23643         var r = this.store.getAt(index);
23644         if(r){
23645             this.onSelect(r, index);
23646         }
23647         if(doFocus !== false && !this.blockFocus){
23648             this.el.focus();
23649         }
23650     },
23651
23652     // private
23653     restrictHeight : function(){
23654         this.innerList.dom.style.height = '';
23655         var inner = this.innerList.dom;
23656         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23657         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23658         this.list.beginUpdate();
23659         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23660         this.list.alignTo(this.el, this.listAlign);
23661         this.list.endUpdate();
23662     },
23663
23664     // private
23665     onEmptyResults : function(){
23666         this.collapse();
23667     },
23668
23669     /**
23670      * Returns true if the dropdown list is expanded, else false.
23671      */
23672     isExpanded : function(){
23673         return this.list.isVisible();
23674     },
23675
23676     /**
23677      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23678      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23679      * @param {String} value The data value of the item to select
23680      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23681      * selected item if it is not currently in view (defaults to true)
23682      * @return {Boolean} True if the value matched an item in the list, else false
23683      */
23684     selectByValue : function(v, scrollIntoView){
23685         if(v !== undefined && v !== null){
23686             var r = this.findRecord(this.valueField || this.displayField, v);
23687             if(r){
23688                 this.select(this.store.indexOf(r), scrollIntoView);
23689                 return true;
23690             }
23691         }
23692         return false;
23693     },
23694
23695     /**
23696      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23697      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23698      * @param {Number} index The zero-based index of the list item to select
23699      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23700      * selected item if it is not currently in view (defaults to true)
23701      */
23702     select : function(index, scrollIntoView){
23703         this.selectedIndex = index;
23704         this.view.select(index);
23705         if(scrollIntoView !== false){
23706             var el = this.view.getNode(index);
23707             if(el){
23708                 this.innerList.scrollChildIntoView(el, false);
23709             }
23710         }
23711     },
23712
23713     // private
23714     selectNext : function(){
23715         var ct = this.store.getCount();
23716         if(ct > 0){
23717             if(this.selectedIndex == -1){
23718                 this.select(0);
23719             }else if(this.selectedIndex < ct-1){
23720                 this.select(this.selectedIndex+1);
23721             }
23722         }
23723     },
23724
23725     // private
23726     selectPrev : function(){
23727         var ct = this.store.getCount();
23728         if(ct > 0){
23729             if(this.selectedIndex == -1){
23730                 this.select(0);
23731             }else if(this.selectedIndex != 0){
23732                 this.select(this.selectedIndex-1);
23733             }
23734         }
23735     },
23736
23737     // private
23738     onKeyUp : function(e){
23739         if(this.editable !== false && !e.isSpecialKey()){
23740             this.lastKey = e.getKey();
23741             this.dqTask.delay(this.queryDelay);
23742         }
23743     },
23744
23745     // private
23746     validateBlur : function(){
23747         return !this.list || !this.list.isVisible();   
23748     },
23749
23750     // private
23751     initQuery : function(){
23752         this.doQuery(this.getRawValue());
23753     },
23754
23755     // private
23756     doForce : function(){
23757         if(this.el.dom.value.length > 0){
23758             this.el.dom.value =
23759                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23760             this.applyEmptyText();
23761         }
23762     },
23763
23764     /**
23765      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23766      * query allowing the query action to be canceled if needed.
23767      * @param {String} query The SQL query to execute
23768      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23769      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23770      * saved in the current store (defaults to false)
23771      */
23772     doQuery : function(q, forceAll){
23773         if(q === undefined || q === null){
23774             q = '';
23775         }
23776         var qe = {
23777             query: q,
23778             forceAll: forceAll,
23779             combo: this,
23780             cancel:false
23781         };
23782         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23783             return false;
23784         }
23785         q = qe.query;
23786         forceAll = qe.forceAll;
23787         if(forceAll === true || (q.length >= this.minChars)){
23788             if(this.lastQuery != q || this.alwaysQuery){
23789                 this.lastQuery = q;
23790                 if(this.mode == 'local'){
23791                     this.selectedIndex = -1;
23792                     if(forceAll){
23793                         this.store.clearFilter();
23794                     }else{
23795                         this.store.filter(this.displayField, q);
23796                     }
23797                     this.onLoad();
23798                 }else{
23799                     this.store.baseParams[this.queryParam] = q;
23800                     this.store.load({
23801                         params: this.getParams(q)
23802                     });
23803                     this.expand();
23804                 }
23805             }else{
23806                 this.selectedIndex = -1;
23807                 this.onLoad();   
23808             }
23809         }
23810     },
23811
23812     // private
23813     getParams : function(q){
23814         var p = {};
23815         //p[this.queryParam] = q;
23816         if(this.pageSize){
23817             p.start = 0;
23818             p.limit = this.pageSize;
23819         }
23820         return p;
23821     },
23822
23823     /**
23824      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23825      */
23826     collapse : function(){
23827         if(!this.isExpanded()){
23828             return;
23829         }
23830         this.list.hide();
23831         Roo.get(document).un('mousedown', this.collapseIf, this);
23832         Roo.get(document).un('mousewheel', this.collapseIf, this);
23833         if (!this.editable) {
23834             Roo.get(document).un('keydown', this.listKeyPress, this);
23835         }
23836         this.fireEvent('collapse', this);
23837     },
23838
23839     // private
23840     collapseIf : function(e){
23841         if(!e.within(this.wrap) && !e.within(this.list)){
23842             this.collapse();
23843         }
23844     },
23845
23846     /**
23847      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23848      */
23849     expand : function(){
23850         if(this.isExpanded() || !this.hasFocus){
23851             return;
23852         }
23853         this.list.alignTo(this.el, this.listAlign);
23854         this.list.show();
23855         Roo.get(document).on('mousedown', this.collapseIf, this);
23856         Roo.get(document).on('mousewheel', this.collapseIf, this);
23857         if (!this.editable) {
23858             Roo.get(document).on('keydown', this.listKeyPress, this);
23859         }
23860         
23861         this.fireEvent('expand', this);
23862     },
23863
23864     // private
23865     // Implements the default empty TriggerField.onTriggerClick function
23866     onTriggerClick : function(){
23867         if(this.disabled){
23868             return;
23869         }
23870         if(this.isExpanded()){
23871             this.collapse();
23872             if (!this.blockFocus) {
23873                 this.el.focus();
23874             }
23875             
23876         }else {
23877             this.hasFocus = true;
23878             if(this.triggerAction == 'all') {
23879                 this.doQuery(this.allQuery, true);
23880             } else {
23881                 this.doQuery(this.getRawValue());
23882             }
23883             if (!this.blockFocus) {
23884                 this.el.focus();
23885             }
23886         }
23887     },
23888     listKeyPress : function(e)
23889     {
23890         //Roo.log('listkeypress');
23891         // scroll to first matching element based on key pres..
23892         if (e.isSpecialKey()) {
23893             return false;
23894         }
23895         var k = String.fromCharCode(e.getKey()).toUpperCase();
23896         //Roo.log(k);
23897         var match  = false;
23898         var csel = this.view.getSelectedNodes();
23899         var cselitem = false;
23900         if (csel.length) {
23901             var ix = this.view.indexOf(csel[0]);
23902             cselitem  = this.store.getAt(ix);
23903             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23904                 cselitem = false;
23905             }
23906             
23907         }
23908         
23909         this.store.each(function(v) { 
23910             if (cselitem) {
23911                 // start at existing selection.
23912                 if (cselitem.id == v.id) {
23913                     cselitem = false;
23914                 }
23915                 return;
23916             }
23917                 
23918             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23919                 match = this.store.indexOf(v);
23920                 return false;
23921             }
23922         }, this);
23923         
23924         if (match === false) {
23925             return true; // no more action?
23926         }
23927         // scroll to?
23928         this.view.select(match);
23929         var sn = Roo.get(this.view.getSelectedNodes()[0])
23930         sn.scrollIntoView(sn.dom.parentNode, false);
23931     }
23932
23933     /** 
23934     * @cfg {Boolean} grow 
23935     * @hide 
23936     */
23937     /** 
23938     * @cfg {Number} growMin 
23939     * @hide 
23940     */
23941     /** 
23942     * @cfg {Number} growMax 
23943     * @hide 
23944     */
23945     /**
23946      * @hide
23947      * @method autoSize
23948      */
23949 });/*
23950  * Copyright(c) 2010-2012, Roo J Solutions Limited
23951  *
23952  * Licence LGPL
23953  *
23954  */
23955
23956 /**
23957  * @class Roo.form.ComboBoxArray
23958  * @extends Roo.form.TextField
23959  * A facebook style adder... for lists of email / people / countries  etc...
23960  * pick multiple items from a combo box, and shows each one.
23961  *
23962  *  Fred [x]  Brian [x]  [Pick another |v]
23963  *
23964  *
23965  *  For this to work: it needs various extra information
23966  *    - normal combo problay has
23967  *      name, hiddenName
23968  *    + displayField, valueField
23969  *
23970  *    For our purpose...
23971  *
23972  *
23973  *   If we change from 'extends' to wrapping...
23974  *   
23975  *  
23976  *
23977  
23978  
23979  * @constructor
23980  * Create a new ComboBoxArray.
23981  * @param {Object} config Configuration options
23982  */
23983  
23984
23985 Roo.form.ComboBoxArray = function(config)
23986 {
23987     
23988     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23989     
23990     this.items = new Roo.util.MixedCollection(false);
23991     
23992     // construct the child combo...
23993     
23994     
23995     
23996     
23997    
23998     
23999 }
24000
24001  
24002 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24003
24004     /**
24005      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24006      */
24007     
24008     lastData : false,
24009     
24010     // behavies liek a hiddne field
24011     inputType:      'hidden',
24012     /**
24013      * @cfg {Number} width The width of the box that displays the selected element
24014      */ 
24015     width:          300,
24016
24017     
24018     
24019     /**
24020      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24021      */
24022     name : false,
24023     /**
24024      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24025      */
24026     hiddenName : false,
24027     
24028     
24029     // private the array of items that are displayed..
24030     items  : false,
24031     // private - the hidden field el.
24032     hiddenEl : false,
24033     // private - the filed el..
24034     el : false,
24035     
24036     //validateValue : function() { return true; }, // all values are ok!
24037     //onAddClick: function() { },
24038     
24039     onRender : function(ct, position) 
24040     {
24041         
24042         // create the standard hidden element
24043         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24044         
24045         
24046         // give fake names to child combo;
24047         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24048         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24049         
24050         this.combo = Roo.factory(this.combo, Roo.form);
24051         this.combo.onRender(ct, position);
24052         
24053         // assigned so form know we need to do this..
24054         this.store          = this.combo.store;
24055         this.valueField     = this.combo.valueField;
24056         this.displayField   = this.combo.displayField ;
24057         
24058         
24059         this.combo.wrap.addClass('x-cbarray-grp');
24060         
24061         var cbwrap = this.combo.wrap.createChild(
24062             {tag: 'div', cls: 'x-cbarray-cb'},
24063             this.combo.el.dom
24064         );
24065         
24066              
24067         this.hiddenEl = this.combo.wrap.createChild({
24068             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24069         });
24070         this.el = this.combo.wrap.createChild({
24071             tag: 'input',  type:'hidden' , name: this.name, value : ''
24072         });
24073          //   this.el.dom.removeAttribute("name");
24074         
24075         
24076         this.outerWrap = this.combo.wrap;
24077         this.wrap = cbwrap;
24078         
24079         this.outerWrap.setWidth(this.width);
24080         this.outerWrap.dom.removeChild(this.el.dom);
24081         
24082         this.wrap.dom.appendChild(this.el.dom);
24083         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24084         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24085         
24086         this.combo.trigger.setStyle('position','relative');
24087         this.combo.trigger.setStyle('left', '0px');
24088         this.combo.trigger.setStyle('top', '2px');
24089         
24090         this.combo.el.setStyle('vertical-align', 'text-bottom');
24091         
24092         //this.trigger.setStyle('vertical-align', 'top');
24093         
24094         // this should use the code from combo really... on('add' ....)
24095         if (this.adder) {
24096             
24097         
24098             this.adder = this.outerWrap.createChild(
24099                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24100             var _t = this;
24101             this.adder.on('click', function(e) {
24102                 _t.fireEvent('adderclick', this, e);
24103             }, _t);
24104         }
24105         //var _t = this;
24106         //this.adder.on('click', this.onAddClick, _t);
24107         
24108         
24109         this.combo.on('select', function(cb, rec, ix) {
24110             this.addItem(rec.data);
24111             
24112             cb.setValue('');
24113             cb.el.dom.value = '';
24114             //cb.lastData = rec.data;
24115             // add to list
24116             
24117         }, this);
24118         
24119         
24120     },
24121     
24122     
24123     getName: function()
24124     {
24125         // returns hidden if it's set..
24126         if (!this.rendered) {return ''};
24127         return  this.hiddenName ? this.hiddenName : this.name;
24128         
24129     },
24130     
24131     
24132     onResize: function(w, h){
24133         
24134         return;
24135         // not sure if this is needed..
24136         //this.combo.onResize(w,h);
24137         
24138         if(typeof w != 'number'){
24139             // we do not handle it!?!?
24140             return;
24141         }
24142         var tw = this.combo.trigger.getWidth();
24143         tw += this.addicon ? this.addicon.getWidth() : 0;
24144         tw += this.editicon ? this.editicon.getWidth() : 0;
24145         var x = w - tw;
24146         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24147             
24148         this.combo.trigger.setStyle('left', '0px');
24149         
24150         if(this.list && this.listWidth === undefined){
24151             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24152             this.list.setWidth(lw);
24153             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24154         }
24155         
24156     
24157         
24158     },
24159     
24160     addItem: function(rec)
24161     {
24162         var valueField = this.combo.valueField;
24163         var displayField = this.combo.displayField;
24164         if (this.items.indexOfKey(rec[valueField]) > -1) {
24165             //console.log("GOT " + rec.data.id);
24166             return;
24167         }
24168         
24169         var x = new Roo.form.ComboBoxArray.Item({
24170             //id : rec[this.idField],
24171             data : rec,
24172             displayField : displayField ,
24173             tipField : displayField ,
24174             cb : this
24175         });
24176         // use the 
24177         this.items.add(rec[valueField],x);
24178         // add it before the element..
24179         this.updateHiddenEl();
24180         x.render(this.outerWrap, this.wrap.dom);
24181         // add the image handler..
24182     },
24183     
24184     updateHiddenEl : function()
24185     {
24186         this.validate();
24187         if (!this.hiddenEl) {
24188             return;
24189         }
24190         var ar = [];
24191         var idField = this.combo.valueField;
24192         
24193         this.items.each(function(f) {
24194             ar.push(f.data[idField]);
24195            
24196         });
24197         this.hiddenEl.dom.value = ar.join(',');
24198         this.validate();
24199     },
24200     
24201     reset : function()
24202     {
24203         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24204         this.items.each(function(f) {
24205            f.remove(); 
24206         });
24207         this.el.dom.value = '';
24208         if (this.hiddenEl) {
24209             this.hiddenEl.dom.value = '';
24210         }
24211         
24212     },
24213     getValue: function()
24214     {
24215         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24216     },
24217     setValue: function(v) // not a valid action - must use addItems..
24218     {
24219          
24220         this.reset();
24221
24222         if (this.store.isLocal) {
24223             // then we can use the store to find the values..
24224             // comma seperated at present.. this needs to allow JSON based encoding..
24225             this.hiddenEl.value  = v;
24226             var v_ar = [];
24227             Roo.each(v.split(','), function(k) {
24228                 Roo.log("CHECK " + this.valueField + ',' + k);
24229                 var li = this.store.query(this.valueField, k);
24230                 if (!li.length) {
24231                     return;
24232                 }
24233                 add = {};
24234                 add[this.valueField] = k;
24235                 add[this.displayField] = li.item(0).data[this.displayField];
24236                 
24237                 this.addItem(add);
24238             }, this) 
24239             
24240                 
24241             
24242         }
24243         
24244         
24245     },
24246     setFromData: function(v)
24247     {
24248         // this recieves an object, if setValues is called.
24249         this.reset();
24250         this.el.dom.value = v[this.displayField];
24251         this.hiddenEl.dom.value = v[this.valueField];
24252         if (!v[this.valueField].length) {
24253             return;
24254         }
24255         var keys = v[this.valueField].split(',');
24256         var display = v[this.displayField].split(',');
24257         for (var i = 0 ; i < keys.length; i++) {
24258             
24259             add = {};
24260             add[this.valueField] = keys[i];
24261             add[this.displayField] = display[i];
24262             this.addItem(add);
24263         }
24264       
24265         
24266     },
24267     
24268     
24269     validateValue : function(value){
24270         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24271         
24272     }
24273     
24274 });
24275
24276
24277
24278 /**
24279  * @class Roo.form.ComboBoxArray.Item
24280  * @extends Roo.BoxComponent
24281  * A selected item in the list
24282  *  Fred [x]  Brian [x]  [Pick another |v]
24283  * 
24284  * @constructor
24285  * Create a new item.
24286  * @param {Object} config Configuration options
24287  */
24288  
24289 Roo.form.ComboBoxArray.Item = function(config) {
24290     config.id = Roo.id();
24291     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24292 }
24293
24294 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24295     data : {},
24296     cb: false,
24297     displayField : false,
24298     tipField : false,
24299     
24300     
24301     defaultAutoCreate : {
24302         tag: 'div',
24303         cls: 'x-cbarray-item',
24304         cn : [ 
24305             { tag: 'div' },
24306             {
24307                 tag: 'img',
24308                 width:16,
24309                 height : 16,
24310                 src : Roo.BLANK_IMAGE_URL ,
24311                 align: 'center'
24312             }
24313         ]
24314         
24315     },
24316     
24317  
24318     onRender : function(ct, position)
24319     {
24320         Roo.form.Field.superclass.onRender.call(this, ct, position);
24321         
24322         if(!this.el){
24323             var cfg = this.getAutoCreate();
24324             this.el = ct.createChild(cfg, position);
24325         }
24326         
24327         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24328         
24329         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24330             this.cb.renderer(this.data) :
24331             String.format('{0}',this.data[this.displayField]);
24332         
24333             
24334         this.el.child('div').dom.setAttribute('qtip',
24335                         String.format('{0}',this.data[this.tipField])
24336         );
24337         
24338         this.el.child('img').on('click', this.remove, this);
24339         
24340     },
24341    
24342     remove : function()
24343     {
24344         
24345         this.cb.items.remove(this);
24346         this.el.child('img').un('click', this.remove, this);
24347         this.el.remove();
24348         this.cb.updateHiddenEl();
24349     }
24350     
24351     
24352 });/*
24353  * Based on:
24354  * Ext JS Library 1.1.1
24355  * Copyright(c) 2006-2007, Ext JS, LLC.
24356  *
24357  * Originally Released Under LGPL - original licence link has changed is not relivant.
24358  *
24359  * Fork - LGPL
24360  * <script type="text/javascript">
24361  */
24362 /**
24363  * @class Roo.form.Checkbox
24364  * @extends Roo.form.Field
24365  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24366  * @constructor
24367  * Creates a new Checkbox
24368  * @param {Object} config Configuration options
24369  */
24370 Roo.form.Checkbox = function(config){
24371     Roo.form.Checkbox.superclass.constructor.call(this, config);
24372     this.addEvents({
24373         /**
24374          * @event check
24375          * Fires when the checkbox is checked or unchecked.
24376              * @param {Roo.form.Checkbox} this This checkbox
24377              * @param {Boolean} checked The new checked value
24378              */
24379         check : true
24380     });
24381 };
24382
24383 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24384     /**
24385      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24386      */
24387     focusClass : undefined,
24388     /**
24389      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24390      */
24391     fieldClass: "x-form-field",
24392     /**
24393      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24394      */
24395     checked: false,
24396     /**
24397      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24398      * {tag: "input", type: "checkbox", autocomplete: "off"})
24399      */
24400     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24401     /**
24402      * @cfg {String} boxLabel The text that appears beside the checkbox
24403      */
24404     boxLabel : "",
24405     /**
24406      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24407      */  
24408     inputValue : '1',
24409     /**
24410      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24411      */
24412      valueOff: '0', // value when not checked..
24413
24414     actionMode : 'viewEl', 
24415     //
24416     // private
24417     itemCls : 'x-menu-check-item x-form-item',
24418     groupClass : 'x-menu-group-item',
24419     inputType : 'hidden',
24420     
24421     
24422     inSetChecked: false, // check that we are not calling self...
24423     
24424     inputElement: false, // real input element?
24425     basedOn: false, // ????
24426     
24427     isFormField: true, // not sure where this is needed!!!!
24428
24429     onResize : function(){
24430         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24431         if(!this.boxLabel){
24432             this.el.alignTo(this.wrap, 'c-c');
24433         }
24434     },
24435
24436     initEvents : function(){
24437         Roo.form.Checkbox.superclass.initEvents.call(this);
24438         this.el.on("click", this.onClick,  this);
24439         this.el.on("change", this.onClick,  this);
24440     },
24441
24442
24443     getResizeEl : function(){
24444         return this.wrap;
24445     },
24446
24447     getPositionEl : function(){
24448         return this.wrap;
24449     },
24450
24451     // private
24452     onRender : function(ct, position){
24453         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24454         /*
24455         if(this.inputValue !== undefined){
24456             this.el.dom.value = this.inputValue;
24457         }
24458         */
24459         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24460         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24461         var viewEl = this.wrap.createChild({ 
24462             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24463         this.viewEl = viewEl;   
24464         this.wrap.on('click', this.onClick,  this); 
24465         
24466         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24467         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24468         
24469         
24470         
24471         if(this.boxLabel){
24472             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24473         //    viewEl.on('click', this.onClick,  this); 
24474         }
24475         //if(this.checked){
24476             this.setChecked(this.checked);
24477         //}else{
24478             //this.checked = this.el.dom;
24479         //}
24480
24481     },
24482
24483     // private
24484     initValue : Roo.emptyFn,
24485
24486     /**
24487      * Returns the checked state of the checkbox.
24488      * @return {Boolean} True if checked, else false
24489      */
24490     getValue : function(){
24491         if(this.el){
24492             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24493         }
24494         return this.valueOff;
24495         
24496     },
24497
24498         // private
24499     onClick : function(){ 
24500         this.setChecked(!this.checked);
24501
24502         //if(this.el.dom.checked != this.checked){
24503         //    this.setValue(this.el.dom.checked);
24504        // }
24505     },
24506
24507     /**
24508      * Sets the checked state of the checkbox.
24509      * On is always based on a string comparison between inputValue and the param.
24510      * @param {Boolean/String} value - the value to set 
24511      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24512      */
24513     setValue : function(v,suppressEvent){
24514         
24515         
24516         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24517         //if(this.el && this.el.dom){
24518         //    this.el.dom.checked = this.checked;
24519         //    this.el.dom.defaultChecked = this.checked;
24520         //}
24521         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24522         //this.fireEvent("check", this, this.checked);
24523     },
24524     // private..
24525     setChecked : function(state,suppressEvent)
24526     {
24527         if (this.inSetChecked) {
24528             this.checked = state;
24529             return;
24530         }
24531         
24532     
24533         if(this.wrap){
24534             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24535         }
24536         this.checked = state;
24537         if(suppressEvent !== true){
24538             this.fireEvent('check', this, state);
24539         }
24540         this.inSetChecked = true;
24541         this.el.dom.value = state ? this.inputValue : this.valueOff;
24542         this.inSetChecked = false;
24543         
24544     },
24545     // handle setting of hidden value by some other method!!?!?
24546     setFromHidden: function()
24547     {
24548         if(!this.el){
24549             return;
24550         }
24551         //console.log("SET FROM HIDDEN");
24552         //alert('setFrom hidden');
24553         this.setValue(this.el.dom.value);
24554     },
24555     
24556     onDestroy : function()
24557     {
24558         if(this.viewEl){
24559             Roo.get(this.viewEl).remove();
24560         }
24561          
24562         Roo.form.Checkbox.superclass.onDestroy.call(this);
24563     }
24564
24565 });/*
24566  * Based on:
24567  * Ext JS Library 1.1.1
24568  * Copyright(c) 2006-2007, Ext JS, LLC.
24569  *
24570  * Originally Released Under LGPL - original licence link has changed is not relivant.
24571  *
24572  * Fork - LGPL
24573  * <script type="text/javascript">
24574  */
24575  
24576 /**
24577  * @class Roo.form.Radio
24578  * @extends Roo.form.Checkbox
24579  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24580  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24581  * @constructor
24582  * Creates a new Radio
24583  * @param {Object} config Configuration options
24584  */
24585 Roo.form.Radio = function(){
24586     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24587 };
24588 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24589     inputType: 'radio',
24590
24591     /**
24592      * If this radio is part of a group, it will return the selected value
24593      * @return {String}
24594      */
24595     getGroupValue : function(){
24596         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24597     }
24598 });//<script type="text/javascript">
24599
24600 /*
24601  * Ext JS Library 1.1.1
24602  * Copyright(c) 2006-2007, Ext JS, LLC.
24603  * licensing@extjs.com
24604  * 
24605  * http://www.extjs.com/license
24606  */
24607  
24608  /*
24609   * 
24610   * Known bugs:
24611   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24612   * - IE ? - no idea how much works there.
24613   * 
24614   * 
24615   * 
24616   */
24617  
24618
24619 /**
24620  * @class Ext.form.HtmlEditor
24621  * @extends Ext.form.Field
24622  * Provides a lightweight HTML Editor component.
24623  *
24624  * This has been tested on Fireforx / Chrome.. IE may not be so great..
24625  * 
24626  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24627  * supported by this editor.</b><br/><br/>
24628  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24629  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24630  */
24631 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24632       /**
24633      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24634      */
24635     toolbars : false,
24636     /**
24637      * @cfg {String} createLinkText The default text for the create link prompt
24638      */
24639     createLinkText : 'Please enter the URL for the link:',
24640     /**
24641      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24642      */
24643     defaultLinkValue : 'http:/'+'/',
24644    
24645      /**
24646      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24647      *                        Roo.resizable.
24648      */
24649     resizable : false,
24650      /**
24651      * @cfg {Number} height (in pixels)
24652      */   
24653     height: 300,
24654    /**
24655      * @cfg {Number} width (in pixels)
24656      */   
24657     width: 500,
24658     
24659     /**
24660      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24661      * 
24662      */
24663     stylesheets: false,
24664     
24665     // id of frame..
24666     frameId: false,
24667     
24668     // private properties
24669     validationEvent : false,
24670     deferHeight: true,
24671     initialized : false,
24672     activated : false,
24673     sourceEditMode : false,
24674     onFocus : Roo.emptyFn,
24675     iframePad:3,
24676     hideMode:'offsets',
24677     
24678     defaultAutoCreate : { // modified by initCompnoent..
24679         tag: "textarea",
24680         style:"width:500px;height:300px;",
24681         autocomplete: "off"
24682     },
24683
24684     // private
24685     initComponent : function(){
24686         this.addEvents({
24687             /**
24688              * @event initialize
24689              * Fires when the editor is fully initialized (including the iframe)
24690              * @param {HtmlEditor} this
24691              */
24692             initialize: true,
24693             /**
24694              * @event activate
24695              * Fires when the editor is first receives the focus. Any insertion must wait
24696              * until after this event.
24697              * @param {HtmlEditor} this
24698              */
24699             activate: true,
24700              /**
24701              * @event beforesync
24702              * Fires before the textarea is updated with content from the editor iframe. Return false
24703              * to cancel the sync.
24704              * @param {HtmlEditor} this
24705              * @param {String} html
24706              */
24707             beforesync: true,
24708              /**
24709              * @event beforepush
24710              * Fires before the iframe editor is updated with content from the textarea. Return false
24711              * to cancel the push.
24712              * @param {HtmlEditor} this
24713              * @param {String} html
24714              */
24715             beforepush: true,
24716              /**
24717              * @event sync
24718              * Fires when the textarea is updated with content from the editor iframe.
24719              * @param {HtmlEditor} this
24720              * @param {String} html
24721              */
24722             sync: true,
24723              /**
24724              * @event push
24725              * Fires when the iframe editor is updated with content from the textarea.
24726              * @param {HtmlEditor} this
24727              * @param {String} html
24728              */
24729             push: true,
24730              /**
24731              * @event editmodechange
24732              * Fires when the editor switches edit modes
24733              * @param {HtmlEditor} this
24734              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24735              */
24736             editmodechange: true,
24737             /**
24738              * @event editorevent
24739              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24740              * @param {HtmlEditor} this
24741              */
24742             editorevent: true
24743         });
24744         this.defaultAutoCreate =  {
24745             tag: "textarea",
24746             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24747             autocomplete: "off"
24748         };
24749     },
24750
24751     /**
24752      * Protected method that will not generally be called directly. It
24753      * is called when the editor creates its toolbar. Override this method if you need to
24754      * add custom toolbar buttons.
24755      * @param {HtmlEditor} editor
24756      */
24757     createToolbar : function(editor){
24758         if (!editor.toolbars || !editor.toolbars.length) {
24759             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24760         }
24761         
24762         for (var i =0 ; i < editor.toolbars.length;i++) {
24763             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24764             editor.toolbars[i].init(editor);
24765         }
24766          
24767         
24768     },
24769
24770     /**
24771      * Protected method that will not generally be called directly. It
24772      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24773      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24774      */
24775     getDocMarkup : function(){
24776         // body styles..
24777         var st = '';
24778         if (this.stylesheets === false) {
24779             
24780             Roo.get(document.head).select('style').each(function(node) {
24781                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24782             });
24783             
24784             Roo.get(document.head).select('link').each(function(node) { 
24785                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24786             });
24787             
24788         } else if (!this.stylesheets.length) {
24789                 // simple..
24790                 st = '<style type="text/css">' +
24791                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24792                    '</style>';
24793         } else {
24794             Roo.each(this.stylesheets, function(s) {
24795                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24796             });
24797             
24798         }
24799         
24800         st +=  '<style type="text/css">' +
24801             'IMG { cursor: pointer } ' +
24802         '</style>';
24803
24804         
24805         return '<html><head>' + st  +
24806             //<style type="text/css">' +
24807             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24808             //'</style>' +
24809             ' </head><body class="roo-htmleditor-body"></body></html>';
24810     },
24811
24812     // private
24813     onRender : function(ct, position)
24814     {
24815         var _t = this;
24816         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24817         this.el.dom.style.border = '0 none';
24818         this.el.dom.setAttribute('tabIndex', -1);
24819         this.el.addClass('x-hidden');
24820         if(Roo.isIE){ // fix IE 1px bogus margin
24821             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24822         }
24823         this.wrap = this.el.wrap({
24824             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24825         });
24826         
24827         if (this.resizable) {
24828             this.resizeEl = new Roo.Resizable(this.wrap, {
24829                 pinned : true,
24830                 wrap: true,
24831                 dynamic : true,
24832                 minHeight : this.height,
24833                 height: this.height,
24834                 handles : this.resizable,
24835                 width: this.width,
24836                 listeners : {
24837                     resize : function(r, w, h) {
24838                         _t.onResize(w,h); // -something
24839                     }
24840                 }
24841             });
24842             
24843         }
24844
24845         this.frameId = Roo.id();
24846         
24847         this.createToolbar(this);
24848         
24849       
24850         
24851         var iframe = this.wrap.createChild({
24852             tag: 'iframe',
24853             id: this.frameId,
24854             name: this.frameId,
24855             frameBorder : 'no',
24856             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24857         }, this.el
24858         );
24859         
24860        // console.log(iframe);
24861         //this.wrap.dom.appendChild(iframe);
24862
24863         this.iframe = iframe.dom;
24864
24865          this.assignDocWin();
24866         
24867         this.doc.designMode = 'on';
24868        
24869         this.doc.open();
24870         this.doc.write(this.getDocMarkup());
24871         this.doc.close();
24872
24873         
24874         var task = { // must defer to wait for browser to be ready
24875             run : function(){
24876                 //console.log("run task?" + this.doc.readyState);
24877                 this.assignDocWin();
24878                 if(this.doc.body || this.doc.readyState == 'complete'){
24879                     try {
24880                         this.doc.designMode="on";
24881                     } catch (e) {
24882                         return;
24883                     }
24884                     Roo.TaskMgr.stop(task);
24885                     this.initEditor.defer(10, this);
24886                 }
24887             },
24888             interval : 10,
24889             duration:10000,
24890             scope: this
24891         };
24892         Roo.TaskMgr.start(task);
24893
24894         if(!this.width){
24895             this.setSize(this.wrap.getSize());
24896         }
24897         if (this.resizeEl) {
24898             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24899             // should trigger onReize..
24900         }
24901     },
24902
24903     // private
24904     onResize : function(w, h)
24905     {
24906         //Roo.log('resize: ' +w + ',' + h );
24907         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24908         if(this.el && this.iframe){
24909             if(typeof w == 'number'){
24910                 var aw = w - this.wrap.getFrameWidth('lr');
24911                 this.el.setWidth(this.adjustWidth('textarea', aw));
24912                 this.iframe.style.width = aw + 'px';
24913             }
24914             if(typeof h == 'number'){
24915                 var tbh = 0;
24916                 for (var i =0; i < this.toolbars.length;i++) {
24917                     // fixme - ask toolbars for heights?
24918                     tbh += this.toolbars[i].tb.el.getHeight();
24919                     if (this.toolbars[i].footer) {
24920                         tbh += this.toolbars[i].footer.el.getHeight();
24921                     }
24922                 }
24923                 
24924                 
24925                 
24926                 
24927                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24928                 ah -= 5; // knock a few pixes off for look..
24929                 this.el.setHeight(this.adjustWidth('textarea', ah));
24930                 this.iframe.style.height = ah + 'px';
24931                 if(this.doc){
24932                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24933                 }
24934             }
24935         }
24936     },
24937
24938     /**
24939      * Toggles the editor between standard and source edit mode.
24940      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24941      */
24942     toggleSourceEdit : function(sourceEditMode){
24943         
24944         this.sourceEditMode = sourceEditMode === true;
24945         
24946         if(this.sourceEditMode){
24947           
24948             this.syncValue();
24949             this.iframe.className = 'x-hidden';
24950             this.el.removeClass('x-hidden');
24951             this.el.dom.removeAttribute('tabIndex');
24952             this.el.focus();
24953         }else{
24954              
24955             this.pushValue();
24956             this.iframe.className = '';
24957             this.el.addClass('x-hidden');
24958             this.el.dom.setAttribute('tabIndex', -1);
24959             this.deferFocus();
24960         }
24961         this.setSize(this.wrap.getSize());
24962         this.fireEvent('editmodechange', this, this.sourceEditMode);
24963     },
24964
24965     // private used internally
24966     createLink : function(){
24967         var url = prompt(this.createLinkText, this.defaultLinkValue);
24968         if(url && url != 'http:/'+'/'){
24969             this.relayCmd('createlink', url);
24970         }
24971     },
24972
24973     // private (for BoxComponent)
24974     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24975
24976     // private (for BoxComponent)
24977     getResizeEl : function(){
24978         return this.wrap;
24979     },
24980
24981     // private (for BoxComponent)
24982     getPositionEl : function(){
24983         return this.wrap;
24984     },
24985
24986     // private
24987     initEvents : function(){
24988         this.originalValue = this.getValue();
24989     },
24990
24991     /**
24992      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24993      * @method
24994      */
24995     markInvalid : Roo.emptyFn,
24996     /**
24997      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24998      * @method
24999      */
25000     clearInvalid : Roo.emptyFn,
25001
25002     setValue : function(v){
25003         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25004         this.pushValue();
25005     },
25006
25007     /**
25008      * Protected method that will not generally be called directly. If you need/want
25009      * custom HTML cleanup, this is the method you should override.
25010      * @param {String} html The HTML to be cleaned
25011      * return {String} The cleaned HTML
25012      */
25013     cleanHtml : function(html){
25014         html = String(html);
25015         if(html.length > 5){
25016             if(Roo.isSafari){ // strip safari nonsense
25017                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25018             }
25019         }
25020         if(html == '&nbsp;'){
25021             html = '';
25022         }
25023         return html;
25024     },
25025
25026     /**
25027      * Protected method that will not generally be called directly. Syncs the contents
25028      * of the editor iframe with the textarea.
25029      */
25030     syncValue : function(){
25031         if(this.initialized){
25032             var bd = (this.doc.body || this.doc.documentElement);
25033             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25034             var html = bd.innerHTML;
25035             if(Roo.isSafari){
25036                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25037                 var m = bs.match(/text-align:(.*?);/i);
25038                 if(m && m[1]){
25039                     html = '<div style="'+m[0]+'">' + html + '</div>';
25040                 }
25041             }
25042             html = this.cleanHtml(html);
25043             // fix up the special chars..
25044             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25045                 return "&#"+b.charCodeAt()+";" 
25046             });
25047             if(this.fireEvent('beforesync', this, html) !== false){
25048                 this.el.dom.value = html;
25049                 this.fireEvent('sync', this, html);
25050             }
25051         }
25052     },
25053
25054     /**
25055      * Protected method that will not generally be called directly. Pushes the value of the textarea
25056      * into the iframe editor.
25057      */
25058     pushValue : function(){
25059         if(this.initialized){
25060             var v = this.el.dom.value;
25061             if(v.length < 1){
25062                 v = '&#160;';
25063             }
25064             
25065             if(this.fireEvent('beforepush', this, v) !== false){
25066                 var d = (this.doc.body || this.doc.documentElement);
25067                 d.innerHTML = v;
25068                 this.cleanUpPaste();
25069                 this.el.dom.value = d.innerHTML;
25070                 this.fireEvent('push', this, v);
25071             }
25072         }
25073     },
25074
25075     // private
25076     deferFocus : function(){
25077         this.focus.defer(10, this);
25078     },
25079
25080     // doc'ed in Field
25081     focus : function(){
25082         if(this.win && !this.sourceEditMode){
25083             this.win.focus();
25084         }else{
25085             this.el.focus();
25086         }
25087     },
25088     
25089     assignDocWin: function()
25090     {
25091         var iframe = this.iframe;
25092         
25093          if(Roo.isIE){
25094             this.doc = iframe.contentWindow.document;
25095             this.win = iframe.contentWindow;
25096         } else {
25097             if (!Roo.get(this.frameId)) {
25098                 return;
25099             }
25100             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25101             this.win = Roo.get(this.frameId).dom.contentWindow;
25102         }
25103     },
25104     
25105     // private
25106     initEditor : function(){
25107         //console.log("INIT EDITOR");
25108         this.assignDocWin();
25109         
25110         
25111         
25112         this.doc.designMode="on";
25113         this.doc.open();
25114         this.doc.write(this.getDocMarkup());
25115         this.doc.close();
25116         
25117         var dbody = (this.doc.body || this.doc.documentElement);
25118         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25119         // this copies styles from the containing element into thsi one..
25120         // not sure why we need all of this..
25121         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25122         ss['background-attachment'] = 'fixed'; // w3c
25123         dbody.bgProperties = 'fixed'; // ie
25124         Roo.DomHelper.applyStyles(dbody, ss);
25125         Roo.EventManager.on(this.doc, {
25126             //'mousedown': this.onEditorEvent,
25127             'mouseup': this.onEditorEvent,
25128             'dblclick': this.onEditorEvent,
25129             'click': this.onEditorEvent,
25130             'keyup': this.onEditorEvent,
25131             buffer:100,
25132             scope: this
25133         });
25134         if(Roo.isGecko){
25135             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25136         }
25137         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25138             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25139         }
25140         this.initialized = true;
25141
25142         this.fireEvent('initialize', this);
25143         this.pushValue();
25144     },
25145
25146     // private
25147     onDestroy : function(){
25148         
25149         
25150         
25151         if(this.rendered){
25152             
25153             for (var i =0; i < this.toolbars.length;i++) {
25154                 // fixme - ask toolbars for heights?
25155                 this.toolbars[i].onDestroy();
25156             }
25157             
25158             this.wrap.dom.innerHTML = '';
25159             this.wrap.remove();
25160         }
25161     },
25162
25163     // private
25164     onFirstFocus : function(){
25165         
25166         this.assignDocWin();
25167         
25168         
25169         this.activated = true;
25170         for (var i =0; i < this.toolbars.length;i++) {
25171             this.toolbars[i].onFirstFocus();
25172         }
25173        
25174         if(Roo.isGecko){ // prevent silly gecko errors
25175             this.win.focus();
25176             var s = this.win.getSelection();
25177             if(!s.focusNode || s.focusNode.nodeType != 3){
25178                 var r = s.getRangeAt(0);
25179                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25180                 r.collapse(true);
25181                 this.deferFocus();
25182             }
25183             try{
25184                 this.execCmd('useCSS', true);
25185                 this.execCmd('styleWithCSS', false);
25186             }catch(e){}
25187         }
25188         this.fireEvent('activate', this);
25189     },
25190
25191     // private
25192     adjustFont: function(btn){
25193         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25194         //if(Roo.isSafari){ // safari
25195         //    adjust *= 2;
25196        // }
25197         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25198         if(Roo.isSafari){ // safari
25199             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25200             v =  (v < 10) ? 10 : v;
25201             v =  (v > 48) ? 48 : v;
25202             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25203             
25204         }
25205         
25206         
25207         v = Math.max(1, v+adjust);
25208         
25209         this.execCmd('FontSize', v  );
25210     },
25211
25212     onEditorEvent : function(e){
25213         this.fireEvent('editorevent', this, e);
25214       //  this.updateToolbar();
25215         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25216     },
25217
25218     insertTag : function(tg)
25219     {
25220         // could be a bit smarter... -> wrap the current selected tRoo..
25221         
25222         this.execCmd("formatblock",   tg);
25223         
25224     },
25225     
25226     insertText : function(txt)
25227     {
25228         
25229         
25230         range = this.createRange();
25231         range.deleteContents();
25232                //alert(Sender.getAttribute('label'));
25233                
25234         range.insertNode(this.doc.createTextNode(txt));
25235     } ,
25236     
25237     // private
25238     relayBtnCmd : function(btn){
25239         this.relayCmd(btn.cmd);
25240     },
25241
25242     /**
25243      * Executes a Midas editor command on the editor document and performs necessary focus and
25244      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25245      * @param {String} cmd The Midas command
25246      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25247      */
25248     relayCmd : function(cmd, value){
25249         this.win.focus();
25250         this.execCmd(cmd, value);
25251         this.fireEvent('editorevent', this);
25252         //this.updateToolbar();
25253         this.deferFocus();
25254     },
25255
25256     /**
25257      * Executes a Midas editor command directly on the editor document.
25258      * For visual commands, you should use {@link #relayCmd} instead.
25259      * <b>This should only be called after the editor is initialized.</b>
25260      * @param {String} cmd The Midas command
25261      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25262      */
25263     execCmd : function(cmd, value){
25264         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25265         this.syncValue();
25266     },
25267  
25268  
25269    
25270     /**
25271      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25272      * to insert tRoo.
25273      * @param {String} text | dom node.. 
25274      */
25275     insertAtCursor : function(text)
25276     {
25277         
25278         
25279         
25280         if(!this.activated){
25281             return;
25282         }
25283         /*
25284         if(Roo.isIE){
25285             this.win.focus();
25286             var r = this.doc.selection.createRange();
25287             if(r){
25288                 r.collapse(true);
25289                 r.pasteHTML(text);
25290                 this.syncValue();
25291                 this.deferFocus();
25292             
25293             }
25294             return;
25295         }
25296         */
25297         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25298             this.win.focus();
25299             
25300             
25301             // from jquery ui (MIT licenced)
25302             var range, node;
25303             var win = this.win;
25304             
25305             if (win.getSelection && win.getSelection().getRangeAt) {
25306                 range = win.getSelection().getRangeAt(0);
25307                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25308                 range.insertNode(node);
25309             } else if (win.document.selection && win.document.selection.createRange) {
25310                 // no firefox support
25311                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25312                 win.document.selection.createRange().pasteHTML(txt);
25313             } else {
25314                 // no firefox support
25315                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25316                 this.execCmd('InsertHTML', txt);
25317             } 
25318             
25319             this.syncValue();
25320             
25321             this.deferFocus();
25322         }
25323     },
25324  // private
25325     mozKeyPress : function(e){
25326         if(e.ctrlKey){
25327             var c = e.getCharCode(), cmd;
25328           
25329             if(c > 0){
25330                 c = String.fromCharCode(c).toLowerCase();
25331                 switch(c){
25332                     case 'b':
25333                         cmd = 'bold';
25334                         break;
25335                     case 'i':
25336                         cmd = 'italic';
25337                         break;
25338                     
25339                     case 'u':
25340                         cmd = 'underline';
25341                         break;
25342                     
25343                     case 'v':
25344                         this.cleanUpPaste.defer(100, this);
25345                         return;
25346                         
25347                 }
25348                 if(cmd){
25349                     this.win.focus();
25350                     this.execCmd(cmd);
25351                     this.deferFocus();
25352                     e.preventDefault();
25353                 }
25354                 
25355             }
25356         }
25357     },
25358
25359     // private
25360     fixKeys : function(){ // load time branching for fastest keydown performance
25361         if(Roo.isIE){
25362             return function(e){
25363                 var k = e.getKey(), r;
25364                 if(k == e.TAB){
25365                     e.stopEvent();
25366                     r = this.doc.selection.createRange();
25367                     if(r){
25368                         r.collapse(true);
25369                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25370                         this.deferFocus();
25371                     }
25372                     return;
25373                 }
25374                 
25375                 if(k == e.ENTER){
25376                     r = this.doc.selection.createRange();
25377                     if(r){
25378                         var target = r.parentElement();
25379                         if(!target || target.tagName.toLowerCase() != 'li'){
25380                             e.stopEvent();
25381                             r.pasteHTML('<br />');
25382                             r.collapse(false);
25383                             r.select();
25384                         }
25385                     }
25386                 }
25387                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25388                     this.cleanUpPaste.defer(100, this);
25389                     return;
25390                 }
25391                 
25392                 
25393             };
25394         }else if(Roo.isOpera){
25395             return function(e){
25396                 var k = e.getKey();
25397                 if(k == e.TAB){
25398                     e.stopEvent();
25399                     this.win.focus();
25400                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25401                     this.deferFocus();
25402                 }
25403                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25404                     this.cleanUpPaste.defer(100, this);
25405                     return;
25406                 }
25407                 
25408             };
25409         }else if(Roo.isSafari){
25410             return function(e){
25411                 var k = e.getKey();
25412                 
25413                 if(k == e.TAB){
25414                     e.stopEvent();
25415                     this.execCmd('InsertText','\t');
25416                     this.deferFocus();
25417                     return;
25418                 }
25419                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25420                     this.cleanUpPaste.defer(100, this);
25421                     return;
25422                 }
25423                 
25424              };
25425         }
25426     }(),
25427     
25428     getAllAncestors: function()
25429     {
25430         var p = this.getSelectedNode();
25431         var a = [];
25432         if (!p) {
25433             a.push(p); // push blank onto stack..
25434             p = this.getParentElement();
25435         }
25436         
25437         
25438         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25439             a.push(p);
25440             p = p.parentNode;
25441         }
25442         a.push(this.doc.body);
25443         return a;
25444     },
25445     lastSel : false,
25446     lastSelNode : false,
25447     
25448     
25449     getSelection : function() 
25450     {
25451         this.assignDocWin();
25452         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25453     },
25454     
25455     getSelectedNode: function() 
25456     {
25457         // this may only work on Gecko!!!
25458         
25459         // should we cache this!!!!
25460         
25461         
25462         
25463          
25464         var range = this.createRange(this.getSelection()).cloneRange();
25465         
25466         if (Roo.isIE) {
25467             var parent = range.parentElement();
25468             while (true) {
25469                 var testRange = range.duplicate();
25470                 testRange.moveToElementText(parent);
25471                 if (testRange.inRange(range)) {
25472                     break;
25473                 }
25474                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25475                     break;
25476                 }
25477                 parent = parent.parentElement;
25478             }
25479             return parent;
25480         }
25481         
25482         // is ancestor a text element.
25483         var ac =  range.commonAncestorContainer;
25484         if (ac.nodeType == 3) {
25485             ac = ac.parentNode;
25486         }
25487         
25488         var ar = ac.childNodes;
25489          
25490         var nodes = [];
25491         var other_nodes = [];
25492         var has_other_nodes = false;
25493         for (var i=0;i<ar.length;i++) {
25494             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25495                 continue;
25496             }
25497             // fullly contained node.
25498             
25499             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25500                 nodes.push(ar[i]);
25501                 continue;
25502             }
25503             
25504             // probably selected..
25505             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25506                 other_nodes.push(ar[i]);
25507                 continue;
25508             }
25509             // outer..
25510             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25511                 continue;
25512             }
25513             
25514             
25515             has_other_nodes = true;
25516         }
25517         if (!nodes.length && other_nodes.length) {
25518             nodes= other_nodes;
25519         }
25520         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25521             return false;
25522         }
25523         
25524         return nodes[0];
25525     },
25526     createRange: function(sel)
25527     {
25528         // this has strange effects when using with 
25529         // top toolbar - not sure if it's a great idea.
25530         //this.editor.contentWindow.focus();
25531         if (typeof sel != "undefined") {
25532             try {
25533                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25534             } catch(e) {
25535                 return this.doc.createRange();
25536             }
25537         } else {
25538             return this.doc.createRange();
25539         }
25540     },
25541     getParentElement: function()
25542     {
25543         
25544         this.assignDocWin();
25545         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25546         
25547         var range = this.createRange(sel);
25548          
25549         try {
25550             var p = range.commonAncestorContainer;
25551             while (p.nodeType == 3) { // text node
25552                 p = p.parentNode;
25553             }
25554             return p;
25555         } catch (e) {
25556             return null;
25557         }
25558     
25559     },
25560     /***
25561      *
25562      * Range intersection.. the hard stuff...
25563      *  '-1' = before
25564      *  '0' = hits..
25565      *  '1' = after.
25566      *         [ -- selected range --- ]
25567      *   [fail]                        [fail]
25568      *
25569      *    basically..
25570      *      if end is before start or  hits it. fail.
25571      *      if start is after end or hits it fail.
25572      *
25573      *   if either hits (but other is outside. - then it's not 
25574      *   
25575      *    
25576      **/
25577     
25578     
25579     // @see http://www.thismuchiknow.co.uk/?p=64.
25580     rangeIntersectsNode : function(range, node)
25581     {
25582         var nodeRange = node.ownerDocument.createRange();
25583         try {
25584             nodeRange.selectNode(node);
25585         } catch (e) {
25586             nodeRange.selectNodeContents(node);
25587         }
25588     
25589         var rangeStartRange = range.cloneRange();
25590         rangeStartRange.collapse(true);
25591     
25592         var rangeEndRange = range.cloneRange();
25593         rangeEndRange.collapse(false);
25594     
25595         var nodeStartRange = nodeRange.cloneRange();
25596         nodeStartRange.collapse(true);
25597     
25598         var nodeEndRange = nodeRange.cloneRange();
25599         nodeEndRange.collapse(false);
25600     
25601         return rangeStartRange.compareBoundaryPoints(
25602                  Range.START_TO_START, nodeEndRange) == -1 &&
25603                rangeEndRange.compareBoundaryPoints(
25604                  Range.START_TO_START, nodeStartRange) == 1;
25605         
25606          
25607     },
25608     rangeCompareNode : function(range, node)
25609     {
25610         var nodeRange = node.ownerDocument.createRange();
25611         try {
25612             nodeRange.selectNode(node);
25613         } catch (e) {
25614             nodeRange.selectNodeContents(node);
25615         }
25616         
25617         
25618         range.collapse(true);
25619     
25620         nodeRange.collapse(true);
25621      
25622         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25623         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25624          
25625         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25626         
25627         var nodeIsBefore   =  ss == 1;
25628         var nodeIsAfter    = ee == -1;
25629         
25630         if (nodeIsBefore && nodeIsAfter)
25631             return 0; // outer
25632         if (!nodeIsBefore && nodeIsAfter)
25633             return 1; //right trailed.
25634         
25635         if (nodeIsBefore && !nodeIsAfter)
25636             return 2;  // left trailed.
25637         // fully contined.
25638         return 3;
25639     },
25640
25641     // private? - in a new class?
25642     cleanUpPaste :  function()
25643     {
25644         // cleans up the whole document..
25645          Roo.log('cleanuppaste');
25646         this.cleanUpChildren(this.doc.body);
25647         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25648         if (clean != this.doc.body.innerHTML) {
25649             this.doc.body.innerHTML = clean;
25650         }
25651         
25652     },
25653     
25654     cleanWordChars : function(input) {
25655         var he = Roo.form.HtmlEditor;
25656     
25657         var output = input;
25658         Roo.each(he.swapCodes, function(sw) { 
25659         
25660             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25661             output = output.replace(swapper, sw[1]);
25662         });
25663         return output;
25664     },
25665     
25666     
25667     cleanUpChildren : function (n)
25668     {
25669         if (!n.childNodes.length) {
25670             return;
25671         }
25672         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25673            this.cleanUpChild(n.childNodes[i]);
25674         }
25675     },
25676     
25677     
25678         
25679     
25680     cleanUpChild : function (node)
25681     {
25682         //console.log(node);
25683         if (node.nodeName == "#text") {
25684             // clean up silly Windows -- stuff?
25685             return; 
25686         }
25687         if (node.nodeName == "#comment") {
25688             node.parentNode.removeChild(node);
25689             // clean up silly Windows -- stuff?
25690             return; 
25691         }
25692         
25693         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25694             // remove node.
25695             node.parentNode.removeChild(node);
25696             return;
25697             
25698         }
25699         
25700         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25701         
25702         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25703         
25704         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25705             remove_keep_children = true;
25706         }
25707         
25708         if (remove_keep_children) {
25709             this.cleanUpChildren(node);
25710             // inserts everything just before this node...
25711             while (node.childNodes.length) {
25712                 var cn = node.childNodes[0];
25713                 node.removeChild(cn);
25714                 node.parentNode.insertBefore(cn, node);
25715             }
25716             node.parentNode.removeChild(node);
25717             return;
25718         }
25719         
25720         if (!node.attributes || !node.attributes.length) {
25721             this.cleanUpChildren(node);
25722             return;
25723         }
25724         
25725         function cleanAttr(n,v)
25726         {
25727             
25728             if (v.match(/^\./) || v.match(/^\//)) {
25729                 return;
25730             }
25731             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25732                 return;
25733             }
25734             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25735             node.removeAttribute(n);
25736             
25737         }
25738         
25739         function cleanStyle(n,v)
25740         {
25741             if (v.match(/expression/)) { //XSS?? should we even bother..
25742                 node.removeAttribute(n);
25743                 return;
25744             }
25745             
25746             
25747             var parts = v.split(/;/);
25748             Roo.each(parts, function(p) {
25749                 p = p.replace(/\s+/g,'');
25750                 if (!p.length) {
25751                     return true;
25752                 }
25753                 var l = p.split(':').shift().replace(/\s+/g,'');
25754                 
25755                 // only allow 'c whitelisted system attributes'
25756                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25757                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25758                     node.removeAttribute(n);
25759                     return false;
25760                 }
25761                 return true;
25762             });
25763             
25764             
25765         }
25766         
25767         
25768         for (var i = node.attributes.length-1; i > -1 ; i--) {
25769             var a = node.attributes[i];
25770             //console.log(a);
25771             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25772                 node.removeAttribute(a.name);
25773                 return;
25774             }
25775             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25776                 cleanAttr(a.name,a.value); // fixme..
25777                 return;
25778             }
25779             if (a.name == 'style') {
25780                 cleanStyle(a.name,a.value);
25781             }
25782             /// clean up MS crap..
25783             // tecnically this should be a list of valid class'es..
25784             
25785             
25786             if (a.name == 'class') {
25787                 if (a.value.match(/^Mso/)) {
25788                     node.className = '';
25789                 }
25790                 
25791                 if (a.value.match(/body/)) {
25792                     node.className = '';
25793                 }
25794             }
25795             
25796             // style cleanup!?
25797             // class cleanup?
25798             
25799         }
25800         
25801         
25802         this.cleanUpChildren(node);
25803         
25804         
25805     }
25806     
25807     
25808     // hide stuff that is not compatible
25809     /**
25810      * @event blur
25811      * @hide
25812      */
25813     /**
25814      * @event change
25815      * @hide
25816      */
25817     /**
25818      * @event focus
25819      * @hide
25820      */
25821     /**
25822      * @event specialkey
25823      * @hide
25824      */
25825     /**
25826      * @cfg {String} fieldClass @hide
25827      */
25828     /**
25829      * @cfg {String} focusClass @hide
25830      */
25831     /**
25832      * @cfg {String} autoCreate @hide
25833      */
25834     /**
25835      * @cfg {String} inputType @hide
25836      */
25837     /**
25838      * @cfg {String} invalidClass @hide
25839      */
25840     /**
25841      * @cfg {String} invalidText @hide
25842      */
25843     /**
25844      * @cfg {String} msgFx @hide
25845      */
25846     /**
25847      * @cfg {String} validateOnBlur @hide
25848      */
25849 });
25850
25851 Roo.form.HtmlEditor.white = [
25852         'area', 'br', 'img', 'input', 'hr', 'wbr',
25853         
25854        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25855        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25856        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25857        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25858        'table',   'ul',         'xmp', 
25859        
25860        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25861       'thead',   'tr', 
25862      
25863       'dir', 'menu', 'ol', 'ul', 'dl',
25864        
25865       'embed',  'object'
25866 ];
25867
25868
25869 Roo.form.HtmlEditor.black = [
25870     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25871         'applet', // 
25872         'base',   'basefont', 'bgsound', 'blink',  'body', 
25873         'frame',  'frameset', 'head',    'html',   'ilayer', 
25874         'iframe', 'layer',  'link',     'meta',    'object',   
25875         'script', 'style' ,'title',  'xml' // clean later..
25876 ];
25877 Roo.form.HtmlEditor.clean = [
25878     'script', 'style', 'title', 'xml'
25879 ];
25880 Roo.form.HtmlEditor.remove = [
25881     'font'
25882 ];
25883 // attributes..
25884
25885 Roo.form.HtmlEditor.ablack = [
25886     'on'
25887 ];
25888     
25889 Roo.form.HtmlEditor.aclean = [ 
25890     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25891 ];
25892
25893 // protocols..
25894 Roo.form.HtmlEditor.pwhite= [
25895         'http',  'https',  'mailto'
25896 ];
25897
25898 // white listed style attributes.
25899 Roo.form.HtmlEditor.cwhite= [
25900         'text-align',
25901         'font-size'
25902 ];
25903
25904
25905 Roo.form.HtmlEditor.swapCodes   =[ 
25906     [    8211, "--" ], 
25907     [    8212, "--" ], 
25908     [    8216,  "'" ],  
25909     [    8217, "'" ],  
25910     [    8220, '"' ],  
25911     [    8221, '"' ],  
25912     [    8226, "*" ],  
25913     [    8230, "..." ]
25914 ]; 
25915
25916     // <script type="text/javascript">
25917 /*
25918  * Based on
25919  * Ext JS Library 1.1.1
25920  * Copyright(c) 2006-2007, Ext JS, LLC.
25921  *  
25922  
25923  */
25924
25925 /**
25926  * @class Roo.form.HtmlEditorToolbar1
25927  * Basic Toolbar
25928  * 
25929  * Usage:
25930  *
25931  new Roo.form.HtmlEditor({
25932     ....
25933     toolbars : [
25934         new Roo.form.HtmlEditorToolbar1({
25935             disable : { fonts: 1 , format: 1, ..., ... , ...],
25936             btns : [ .... ]
25937         })
25938     }
25939      
25940  * 
25941  * @cfg {Object} disable List of elements to disable..
25942  * @cfg {Array} btns List of additional buttons.
25943  * 
25944  * 
25945  * NEEDS Extra CSS? 
25946  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25947  */
25948  
25949 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25950 {
25951     
25952     Roo.apply(this, config);
25953     
25954     // default disabled, based on 'good practice'..
25955     this.disable = this.disable || {};
25956     Roo.applyIf(this.disable, {
25957         fontSize : true,
25958         colors : true,
25959         specialElements : true
25960     });
25961     
25962     
25963     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25964     // dont call parent... till later.
25965 }
25966
25967 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25968     
25969     tb: false,
25970     
25971     rendered: false,
25972     
25973     editor : false,
25974     /**
25975      * @cfg {Object} disable  List of toolbar elements to disable
25976          
25977      */
25978     disable : false,
25979       /**
25980      * @cfg {Array} fontFamilies An array of available font families
25981      */
25982     fontFamilies : [
25983         'Arial',
25984         'Courier New',
25985         'Tahoma',
25986         'Times New Roman',
25987         'Verdana'
25988     ],
25989     
25990     specialChars : [
25991            "&#169;",
25992           "&#174;",     
25993           "&#8482;",    
25994           "&#163;" ,    
25995          // "&#8212;",    
25996           "&#8230;",    
25997           "&#247;" ,    
25998         //  "&#225;" ,     ?? a acute?
25999            "&#8364;"    , //Euro
26000        //   "&#8220;"    ,
26001         //  "&#8221;"    ,
26002         //  "&#8226;"    ,
26003           "&#176;"  //   , // degrees
26004
26005          // "&#233;"     , // e ecute
26006          // "&#250;"     , // u ecute?
26007     ],
26008     
26009     specialElements : [
26010         {
26011             text: "Insert Table",
26012             xtype: 'MenuItem',
26013             xns : Roo.Menu,
26014             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26015                 
26016         },
26017         {    
26018             text: "Insert Image",
26019             xtype: 'MenuItem',
26020             xns : Roo.Menu,
26021             ihtml : '<img src="about:blank"/>'
26022             
26023         }
26024         
26025          
26026     ],
26027     
26028     
26029     inputElements : [ 
26030             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26031             "input:submit", "input:button", "select", "textarea", "label" ],
26032     formats : [
26033         ["p"] ,  
26034         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26035         ["pre"],[ "code"], 
26036         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26037     ],
26038      /**
26039      * @cfg {String} defaultFont default font to use.
26040      */
26041     defaultFont: 'tahoma',
26042    
26043     fontSelect : false,
26044     
26045     
26046     formatCombo : false,
26047     
26048     init : function(editor)
26049     {
26050         this.editor = editor;
26051         
26052         
26053         var fid = editor.frameId;
26054         var etb = this;
26055         function btn(id, toggle, handler){
26056             var xid = fid + '-'+ id ;
26057             return {
26058                 id : xid,
26059                 cmd : id,
26060                 cls : 'x-btn-icon x-edit-'+id,
26061                 enableToggle:toggle !== false,
26062                 scope: editor, // was editor...
26063                 handler:handler||editor.relayBtnCmd,
26064                 clickEvent:'mousedown',
26065                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26066                 tabIndex:-1
26067             };
26068         }
26069         
26070         
26071         
26072         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26073         this.tb = tb;
26074          // stop form submits
26075         tb.el.on('click', function(e){
26076             e.preventDefault(); // what does this do?
26077         });
26078
26079         if(!this.disable.font && !Roo.isSafari){
26080             /* why no safari for fonts
26081             editor.fontSelect = tb.el.createChild({
26082                 tag:'select',
26083                 tabIndex: -1,
26084                 cls:'x-font-select',
26085                 html: editor.createFontOptions()
26086             });
26087             editor.fontSelect.on('change', function(){
26088                 var font = editor.fontSelect.dom.value;
26089                 editor.relayCmd('fontname', font);
26090                 editor.deferFocus();
26091             }, editor);
26092             tb.add(
26093                 editor.fontSelect.dom,
26094                 '-'
26095             );
26096             */
26097         };
26098         if(!this.disable.formats){
26099             this.formatCombo = new Roo.form.ComboBox({
26100                 store: new Roo.data.SimpleStore({
26101                     id : 'tag',
26102                     fields: ['tag'],
26103                     data : this.formats // from states.js
26104                 }),
26105                 blockFocus : true,
26106                 //autoCreate : {tag: "div",  size: "20"},
26107                 displayField:'tag',
26108                 typeAhead: false,
26109                 mode: 'local',
26110                 editable : false,
26111                 triggerAction: 'all',
26112                 emptyText:'Add tag',
26113                 selectOnFocus:true,
26114                 width:135,
26115                 listeners : {
26116                     'select': function(c, r, i) {
26117                         editor.insertTag(r.get('tag'));
26118                         editor.focus();
26119                     }
26120                 }
26121
26122             });
26123             tb.addField(this.formatCombo);
26124             
26125         }
26126         
26127         if(!this.disable.format){
26128             tb.add(
26129                 btn('bold'),
26130                 btn('italic'),
26131                 btn('underline')
26132             );
26133         };
26134         if(!this.disable.fontSize){
26135             tb.add(
26136                 '-',
26137                 
26138                 
26139                 btn('increasefontsize', false, editor.adjustFont),
26140                 btn('decreasefontsize', false, editor.adjustFont)
26141             );
26142         };
26143         
26144         
26145         if(!this.disable.colors){
26146             tb.add(
26147                 '-', {
26148                     id:editor.frameId +'-forecolor',
26149                     cls:'x-btn-icon x-edit-forecolor',
26150                     clickEvent:'mousedown',
26151                     tooltip: this.buttonTips['forecolor'] || undefined,
26152                     tabIndex:-1,
26153                     menu : new Roo.menu.ColorMenu({
26154                         allowReselect: true,
26155                         focus: Roo.emptyFn,
26156                         value:'000000',
26157                         plain:true,
26158                         selectHandler: function(cp, color){
26159                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26160                             editor.deferFocus();
26161                         },
26162                         scope: editor,
26163                         clickEvent:'mousedown'
26164                     })
26165                 }, {
26166                     id:editor.frameId +'backcolor',
26167                     cls:'x-btn-icon x-edit-backcolor',
26168                     clickEvent:'mousedown',
26169                     tooltip: this.buttonTips['backcolor'] || undefined,
26170                     tabIndex:-1,
26171                     menu : new Roo.menu.ColorMenu({
26172                         focus: Roo.emptyFn,
26173                         value:'FFFFFF',
26174                         plain:true,
26175                         allowReselect: true,
26176                         selectHandler: function(cp, color){
26177                             if(Roo.isGecko){
26178                                 editor.execCmd('useCSS', false);
26179                                 editor.execCmd('hilitecolor', color);
26180                                 editor.execCmd('useCSS', true);
26181                                 editor.deferFocus();
26182                             }else{
26183                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26184                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26185                                 editor.deferFocus();
26186                             }
26187                         },
26188                         scope:editor,
26189                         clickEvent:'mousedown'
26190                     })
26191                 }
26192             );
26193         };
26194         // now add all the items...
26195         
26196
26197         if(!this.disable.alignments){
26198             tb.add(
26199                 '-',
26200                 btn('justifyleft'),
26201                 btn('justifycenter'),
26202                 btn('justifyright')
26203             );
26204         };
26205
26206         //if(!Roo.isSafari){
26207             if(!this.disable.links){
26208                 tb.add(
26209                     '-',
26210                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26211                 );
26212             };
26213
26214             if(!this.disable.lists){
26215                 tb.add(
26216                     '-',
26217                     btn('insertorderedlist'),
26218                     btn('insertunorderedlist')
26219                 );
26220             }
26221             if(!this.disable.sourceEdit){
26222                 tb.add(
26223                     '-',
26224                     btn('sourceedit', true, function(btn){
26225                         this.toggleSourceEdit(btn.pressed);
26226                     })
26227                 );
26228             }
26229         //}
26230         
26231         var smenu = { };
26232         // special menu.. - needs to be tidied up..
26233         if (!this.disable.special) {
26234             smenu = {
26235                 text: "&#169;",
26236                 cls: 'x-edit-none',
26237                 
26238                 menu : {
26239                     items : []
26240                 }
26241             };
26242             for (var i =0; i < this.specialChars.length; i++) {
26243                 smenu.menu.items.push({
26244                     
26245                     html: this.specialChars[i],
26246                     handler: function(a,b) {
26247                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26248                         //editor.insertAtCursor(a.html);
26249                         
26250                     },
26251                     tabIndex:-1
26252                 });
26253             }
26254             
26255             
26256             tb.add(smenu);
26257             
26258             
26259         }
26260          
26261         if (!this.disable.specialElements) {
26262             var semenu = {
26263                 text: "Other;",
26264                 cls: 'x-edit-none',
26265                 menu : {
26266                     items : []
26267                 }
26268             };
26269             for (var i =0; i < this.specialElements.length; i++) {
26270                 semenu.menu.items.push(
26271                     Roo.apply({ 
26272                         handler: function(a,b) {
26273                             editor.insertAtCursor(this.ihtml);
26274                         }
26275                     }, this.specialElements[i])
26276                 );
26277                     
26278             }
26279             
26280             tb.add(semenu);
26281             
26282             
26283         }
26284          
26285         
26286         if (this.btns) {
26287             for(var i =0; i< this.btns.length;i++) {
26288                 var b = Roo.factory(this.btns[i],Roo.form);
26289                 b.cls =  'x-edit-none';
26290                 b.scope = editor;
26291                 tb.add(b);
26292             }
26293         
26294         }
26295         
26296         
26297         
26298         // disable everything...
26299         
26300         this.tb.items.each(function(item){
26301            if(item.id != editor.frameId+ '-sourceedit'){
26302                 item.disable();
26303             }
26304         });
26305         this.rendered = true;
26306         
26307         // the all the btns;
26308         editor.on('editorevent', this.updateToolbar, this);
26309         // other toolbars need to implement this..
26310         //editor.on('editmodechange', this.updateToolbar, this);
26311     },
26312     
26313     
26314     
26315     /**
26316      * Protected method that will not generally be called directly. It triggers
26317      * a toolbar update by reading the markup state of the current selection in the editor.
26318      */
26319     updateToolbar: function(){
26320
26321         if(!this.editor.activated){
26322             this.editor.onFirstFocus();
26323             return;
26324         }
26325
26326         var btns = this.tb.items.map, 
26327             doc = this.editor.doc,
26328             frameId = this.editor.frameId;
26329
26330         if(!this.disable.font && !Roo.isSafari){
26331             /*
26332             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26333             if(name != this.fontSelect.dom.value){
26334                 this.fontSelect.dom.value = name;
26335             }
26336             */
26337         }
26338         if(!this.disable.format){
26339             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26340             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26341             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26342         }
26343         if(!this.disable.alignments){
26344             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26345             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26346             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26347         }
26348         if(!Roo.isSafari && !this.disable.lists){
26349             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26350             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26351         }
26352         
26353         var ans = this.editor.getAllAncestors();
26354         if (this.formatCombo) {
26355             
26356             
26357             var store = this.formatCombo.store;
26358             this.formatCombo.setValue("");
26359             for (var i =0; i < ans.length;i++) {
26360                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26361                     // select it..
26362                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26363                     break;
26364                 }
26365             }
26366         }
26367         
26368         
26369         
26370         // hides menus... - so this cant be on a menu...
26371         Roo.menu.MenuMgr.hideAll();
26372
26373         //this.editorsyncValue();
26374     },
26375    
26376     
26377     createFontOptions : function(){
26378         var buf = [], fs = this.fontFamilies, ff, lc;
26379         for(var i = 0, len = fs.length; i< len; i++){
26380             ff = fs[i];
26381             lc = ff.toLowerCase();
26382             buf.push(
26383                 '<option value="',lc,'" style="font-family:',ff,';"',
26384                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26385                     ff,
26386                 '</option>'
26387             );
26388         }
26389         return buf.join('');
26390     },
26391     
26392     toggleSourceEdit : function(sourceEditMode){
26393         if(sourceEditMode === undefined){
26394             sourceEditMode = !this.sourceEditMode;
26395         }
26396         this.sourceEditMode = sourceEditMode === true;
26397         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26398         // just toggle the button?
26399         if(btn.pressed !== this.editor.sourceEditMode){
26400             btn.toggle(this.editor.sourceEditMode);
26401             return;
26402         }
26403         
26404         if(this.sourceEditMode){
26405             this.tb.items.each(function(item){
26406                 if(item.cmd != 'sourceedit'){
26407                     item.disable();
26408                 }
26409             });
26410           
26411         }else{
26412             if(this.initialized){
26413                 this.tb.items.each(function(item){
26414                     item.enable();
26415                 });
26416             }
26417             
26418         }
26419         // tell the editor that it's been pressed..
26420         this.editor.toggleSourceEdit(sourceEditMode);
26421        
26422     },
26423      /**
26424      * Object collection of toolbar tooltips for the buttons in the editor. The key
26425      * is the command id associated with that button and the value is a valid QuickTips object.
26426      * For example:
26427 <pre><code>
26428 {
26429     bold : {
26430         title: 'Bold (Ctrl+B)',
26431         text: 'Make the selected text bold.',
26432         cls: 'x-html-editor-tip'
26433     },
26434     italic : {
26435         title: 'Italic (Ctrl+I)',
26436         text: 'Make the selected text italic.',
26437         cls: 'x-html-editor-tip'
26438     },
26439     ...
26440 </code></pre>
26441     * @type Object
26442      */
26443     buttonTips : {
26444         bold : {
26445             title: 'Bold (Ctrl+B)',
26446             text: 'Make the selected text bold.',
26447             cls: 'x-html-editor-tip'
26448         },
26449         italic : {
26450             title: 'Italic (Ctrl+I)',
26451             text: 'Make the selected text italic.',
26452             cls: 'x-html-editor-tip'
26453         },
26454         underline : {
26455             title: 'Underline (Ctrl+U)',
26456             text: 'Underline the selected text.',
26457             cls: 'x-html-editor-tip'
26458         },
26459         increasefontsize : {
26460             title: 'Grow Text',
26461             text: 'Increase the font size.',
26462             cls: 'x-html-editor-tip'
26463         },
26464         decreasefontsize : {
26465             title: 'Shrink Text',
26466             text: 'Decrease the font size.',
26467             cls: 'x-html-editor-tip'
26468         },
26469         backcolor : {
26470             title: 'Text Highlight Color',
26471             text: 'Change the background color of the selected text.',
26472             cls: 'x-html-editor-tip'
26473         },
26474         forecolor : {
26475             title: 'Font Color',
26476             text: 'Change the color of the selected text.',
26477             cls: 'x-html-editor-tip'
26478         },
26479         justifyleft : {
26480             title: 'Align Text Left',
26481             text: 'Align text to the left.',
26482             cls: 'x-html-editor-tip'
26483         },
26484         justifycenter : {
26485             title: 'Center Text',
26486             text: 'Center text in the editor.',
26487             cls: 'x-html-editor-tip'
26488         },
26489         justifyright : {
26490             title: 'Align Text Right',
26491             text: 'Align text to the right.',
26492             cls: 'x-html-editor-tip'
26493         },
26494         insertunorderedlist : {
26495             title: 'Bullet List',
26496             text: 'Start a bulleted list.',
26497             cls: 'x-html-editor-tip'
26498         },
26499         insertorderedlist : {
26500             title: 'Numbered List',
26501             text: 'Start a numbered list.',
26502             cls: 'x-html-editor-tip'
26503         },
26504         createlink : {
26505             title: 'Hyperlink',
26506             text: 'Make the selected text a hyperlink.',
26507             cls: 'x-html-editor-tip'
26508         },
26509         sourceedit : {
26510             title: 'Source Edit',
26511             text: 'Switch to source editing mode.',
26512             cls: 'x-html-editor-tip'
26513         }
26514     },
26515     // private
26516     onDestroy : function(){
26517         if(this.rendered){
26518             
26519             this.tb.items.each(function(item){
26520                 if(item.menu){
26521                     item.menu.removeAll();
26522                     if(item.menu.el){
26523                         item.menu.el.destroy();
26524                     }
26525                 }
26526                 item.destroy();
26527             });
26528              
26529         }
26530     },
26531     onFirstFocus: function() {
26532         this.tb.items.each(function(item){
26533            item.enable();
26534         });
26535     }
26536 });
26537
26538
26539
26540
26541 // <script type="text/javascript">
26542 /*
26543  * Based on
26544  * Ext JS Library 1.1.1
26545  * Copyright(c) 2006-2007, Ext JS, LLC.
26546  *  
26547  
26548  */
26549
26550  
26551 /**
26552  * @class Roo.form.HtmlEditor.ToolbarContext
26553  * Context Toolbar
26554  * 
26555  * Usage:
26556  *
26557  new Roo.form.HtmlEditor({
26558     ....
26559     toolbars : [
26560         { xtype: 'ToolbarStandard', styles : {} }
26561         { xtype: 'ToolbarContext', disable : {} }
26562     ]
26563 })
26564
26565      
26566  * 
26567  * @config : {Object} disable List of elements to disable.. (not done yet.)
26568  * @config : {Object} styles  Map of styles available.
26569  * 
26570  */
26571
26572 Roo.form.HtmlEditor.ToolbarContext = function(config)
26573 {
26574     
26575     Roo.apply(this, config);
26576     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26577     // dont call parent... till later.
26578     this.styles = this.styles || {};
26579 }
26580 Roo.form.HtmlEditor.ToolbarContext.types = {
26581     'IMG' : {
26582         width : {
26583             title: "Width",
26584             width: 40
26585         },
26586         height:  {
26587             title: "Height",
26588             width: 40
26589         },
26590         align: {
26591             title: "Align",
26592             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26593             width : 80
26594             
26595         },
26596         border: {
26597             title: "Border",
26598             width: 40
26599         },
26600         alt: {
26601             title: "Alt",
26602             width: 120
26603         },
26604         src : {
26605             title: "Src",
26606             width: 220
26607         }
26608         
26609     },
26610     'A' : {
26611         name : {
26612             title: "Name",
26613             width: 50
26614         },
26615         href:  {
26616             title: "Href",
26617             width: 220
26618         } // border?
26619         
26620     },
26621     'TABLE' : {
26622         rows : {
26623             title: "Rows",
26624             width: 20
26625         },
26626         cols : {
26627             title: "Cols",
26628             width: 20
26629         },
26630         width : {
26631             title: "Width",
26632             width: 40
26633         },
26634         height : {
26635             title: "Height",
26636             width: 40
26637         },
26638         border : {
26639             title: "Border",
26640             width: 20
26641         }
26642     },
26643     'TD' : {
26644         width : {
26645             title: "Width",
26646             width: 40
26647         },
26648         height : {
26649             title: "Height",
26650             width: 40
26651         },   
26652         align: {
26653             title: "Align",
26654             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26655             width: 80
26656         },
26657         valign: {
26658             title: "Valign",
26659             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26660             width: 80
26661         },
26662         colspan: {
26663             title: "Colspan",
26664             width: 20
26665             
26666         }
26667     },
26668     'INPUT' : {
26669         name : {
26670             title: "name",
26671             width: 120
26672         },
26673         value : {
26674             title: "Value",
26675             width: 120
26676         },
26677         width : {
26678             title: "Width",
26679             width: 40
26680         }
26681     },
26682     'LABEL' : {
26683         'for' : {
26684             title: "For",
26685             width: 120
26686         }
26687     },
26688     'TEXTAREA' : {
26689           name : {
26690             title: "name",
26691             width: 120
26692         },
26693         rows : {
26694             title: "Rows",
26695             width: 20
26696         },
26697         cols : {
26698             title: "Cols",
26699             width: 20
26700         }
26701     },
26702     'SELECT' : {
26703         name : {
26704             title: "name",
26705             width: 120
26706         },
26707         selectoptions : {
26708             title: "Options",
26709             width: 200
26710         }
26711     },
26712     
26713     // should we really allow this??
26714     // should this just be 
26715     'BODY' : {
26716         title : {
26717             title: "title",
26718             width: 200,
26719             disabled : true
26720         }
26721     },
26722     '*' : {
26723         // empty..
26724     }
26725 };
26726
26727
26728
26729 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26730     
26731     tb: false,
26732     
26733     rendered: false,
26734     
26735     editor : false,
26736     /**
26737      * @cfg {Object} disable  List of toolbar elements to disable
26738          
26739      */
26740     disable : false,
26741     /**
26742      * @cfg {Object} styles List of styles 
26743      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26744      *
26745      * These must be defined in the page, so they get rendered correctly..
26746      * .headline { }
26747      * TD.underline { }
26748      * 
26749      */
26750     styles : false,
26751     
26752     
26753     
26754     toolbars : false,
26755     
26756     init : function(editor)
26757     {
26758         this.editor = editor;
26759         
26760         
26761         var fid = editor.frameId;
26762         var etb = this;
26763         function btn(id, toggle, handler){
26764             var xid = fid + '-'+ id ;
26765             return {
26766                 id : xid,
26767                 cmd : id,
26768                 cls : 'x-btn-icon x-edit-'+id,
26769                 enableToggle:toggle !== false,
26770                 scope: editor, // was editor...
26771                 handler:handler||editor.relayBtnCmd,
26772                 clickEvent:'mousedown',
26773                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26774                 tabIndex:-1
26775             };
26776         }
26777         // create a new element.
26778         var wdiv = editor.wrap.createChild({
26779                 tag: 'div'
26780             }, editor.wrap.dom.firstChild.nextSibling, true);
26781         
26782         // can we do this more than once??
26783         
26784          // stop form submits
26785       
26786  
26787         // disable everything...
26788         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26789         this.toolbars = {};
26790            
26791         for (var i in  ty) {
26792           
26793             this.toolbars[i] = this.buildToolbar(ty[i],i);
26794         }
26795         this.tb = this.toolbars.BODY;
26796         this.tb.el.show();
26797         this.buildFooter();
26798         this.footer.show();
26799         editor.on('hide', function( ) { this.footer.hide() }, this);
26800         editor.on('show', function( ) { this.footer.show() }, this);
26801         
26802          
26803         this.rendered = true;
26804         
26805         // the all the btns;
26806         editor.on('editorevent', this.updateToolbar, this);
26807         // other toolbars need to implement this..
26808         //editor.on('editmodechange', this.updateToolbar, this);
26809     },
26810     
26811     
26812     
26813     /**
26814      * Protected method that will not generally be called directly. It triggers
26815      * a toolbar update by reading the markup state of the current selection in the editor.
26816      */
26817     updateToolbar: function(editor,ev,sel){
26818
26819         //Roo.log(ev);
26820         // capture mouse up - this is handy for selecting images..
26821         // perhaps should go somewhere else...
26822         if(!this.editor.activated){
26823              this.editor.onFirstFocus();
26824             return;
26825         }
26826         
26827         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
26828         // selectNode - might want to handle IE?
26829         if (ev &&
26830             (ev.type == 'mouseup' || ev.type == 'click' ) &&
26831             ev.target && ev.target.tagName == 'IMG') {
26832             // they have click on an image...
26833             // let's see if we can change the selection...
26834             sel = ev.target;
26835          
26836               var nodeRange = sel.ownerDocument.createRange();
26837             try {
26838                 nodeRange.selectNode(sel);
26839             } catch (e) {
26840                 nodeRange.selectNodeContents(sel);
26841             }
26842             //nodeRange.collapse(true);
26843             var s = editor.win.getSelection();
26844             s.removeAllRanges();
26845             s.addRange(nodeRange);
26846         }  
26847         
26848       
26849         var updateFooter = sel ? false : true;
26850         
26851         
26852         var ans = this.editor.getAllAncestors();
26853         
26854         // pick
26855         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26856         
26857         if (!sel) { 
26858             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26859             sel = sel ? sel : this.editor.doc.body;
26860             sel = sel.tagName.length ? sel : this.editor.doc.body;
26861             
26862         }
26863         // pick a menu that exists..
26864         var tn = sel.tagName.toUpperCase();
26865         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26866         
26867         tn = sel.tagName.toUpperCase();
26868         
26869         var lastSel = this.tb.selectedNode
26870         
26871         this.tb.selectedNode = sel;
26872         
26873         // if current menu does not match..
26874         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26875                 
26876             this.tb.el.hide();
26877             ///console.log("show: " + tn);
26878             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26879             this.tb.el.show();
26880             // update name
26881             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26882             
26883             
26884             // update attributes
26885             if (this.tb.fields) {
26886                 this.tb.fields.each(function(e) {
26887                    e.setValue(sel.getAttribute(e.attrname));
26888                 });
26889             }
26890             
26891             var hasStyles = false;
26892             for(var i in this.styles) {
26893                 hasStyles = true;
26894                 break;
26895             }
26896             
26897             // update styles
26898             if (hasStyles) { 
26899                 var st = this.tb.fields.item(0);
26900                 
26901                 st.store.removeAll();
26902                
26903                 
26904                 var cn = sel.className.split(/\s+/);
26905                 
26906                 var avs = [];
26907                 if (this.styles['*']) {
26908                     
26909                     Roo.each(this.styles['*'], function(v) {
26910                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26911                     });
26912                 }
26913                 if (this.styles[tn]) { 
26914                     Roo.each(this.styles[tn], function(v) {
26915                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26916                     });
26917                 }
26918                 
26919                 st.store.loadData(avs);
26920                 st.collapse();
26921                 st.setValue(cn);
26922             }
26923             // flag our selected Node.
26924             this.tb.selectedNode = sel;
26925            
26926            
26927             Roo.menu.MenuMgr.hideAll();
26928
26929         }
26930         
26931         if (!updateFooter) {
26932             return;
26933         }
26934         // update the footer
26935         //
26936         var html = '';
26937         
26938         this.footerEls = ans.reverse();
26939         Roo.each(this.footerEls, function(a,i) {
26940             if (!a) { return; }
26941             html += html.length ? ' &gt; '  :  '';
26942             
26943             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26944             
26945         });
26946        
26947         // 
26948         var sz = this.footDisp.up('td').getSize();
26949         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26950         this.footDisp.dom.style.marginLeft = '5px';
26951         
26952         this.footDisp.dom.style.overflow = 'hidden';
26953         
26954         this.footDisp.dom.innerHTML = html;
26955             
26956         //this.editorsyncValue();
26957     },
26958    
26959        
26960     // private
26961     onDestroy : function(){
26962         if(this.rendered){
26963             
26964             this.tb.items.each(function(item){
26965                 if(item.menu){
26966                     item.menu.removeAll();
26967                     if(item.menu.el){
26968                         item.menu.el.destroy();
26969                     }
26970                 }
26971                 item.destroy();
26972             });
26973              
26974         }
26975     },
26976     onFirstFocus: function() {
26977         // need to do this for all the toolbars..
26978         this.tb.items.each(function(item){
26979            item.enable();
26980         });
26981     },
26982     buildToolbar: function(tlist, nm)
26983     {
26984         var editor = this.editor;
26985          // create a new element.
26986         var wdiv = editor.wrap.createChild({
26987                 tag: 'div'
26988             }, editor.wrap.dom.firstChild.nextSibling, true);
26989         
26990        
26991         var tb = new Roo.Toolbar(wdiv);
26992         // add the name..
26993         
26994         tb.add(nm+ ":&nbsp;");
26995         
26996         var styles = [];
26997         for(var i in this.styles) {
26998             styles.push(i);
26999         }
27000         
27001         // styles...
27002         if (styles && styles.length) {
27003             
27004             // this needs a multi-select checkbox...
27005             tb.addField( new Roo.form.ComboBox({
27006                 store: new Roo.data.SimpleStore({
27007                     id : 'val',
27008                     fields: ['val', 'selected'],
27009                     data : [] 
27010                 }),
27011                 name : '-roo-edit-className',
27012                 attrname : 'className',
27013                 displayField:'val',
27014                 typeAhead: false,
27015                 mode: 'local',
27016                 editable : false,
27017                 triggerAction: 'all',
27018                 emptyText:'Select Style',
27019                 selectOnFocus:true,
27020                 width: 130,
27021                 listeners : {
27022                     'select': function(c, r, i) {
27023                         // initial support only for on class per el..
27024                         tb.selectedNode.className =  r ? r.get('val') : '';
27025                         editor.syncValue();
27026                     }
27027                 }
27028     
27029             }));
27030         }
27031             
27032         
27033         
27034         for (var i in tlist) {
27035             
27036             var item = tlist[i];
27037             tb.add(item.title + ":&nbsp;");
27038             
27039             
27040             
27041             
27042             if (item.opts) {
27043                 // opts == pulldown..
27044                 tb.addField( new Roo.form.ComboBox({
27045                     store: new Roo.data.SimpleStore({
27046                         id : 'val',
27047                         fields: ['val'],
27048                         data : item.opts  
27049                     }),
27050                     name : '-roo-edit-' + i,
27051                     attrname : i,
27052                     displayField:'val',
27053                     typeAhead: false,
27054                     mode: 'local',
27055                     editable : false,
27056                     triggerAction: 'all',
27057                     emptyText:'Select',
27058                     selectOnFocus:true,
27059                     width: item.width ? item.width  : 130,
27060                     listeners : {
27061                         'select': function(c, r, i) {
27062                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27063                         }
27064                     }
27065
27066                 }));
27067                 continue;
27068                     
27069                  
27070                 
27071                 tb.addField( new Roo.form.TextField({
27072                     name: i,
27073                     width: 100,
27074                     //allowBlank:false,
27075                     value: ''
27076                 }));
27077                 continue;
27078             }
27079             tb.addField( new Roo.form.TextField({
27080                 name: '-roo-edit-' + i,
27081                 attrname : i,
27082                 
27083                 width: item.width,
27084                 //allowBlank:true,
27085                 value: '',
27086                 listeners: {
27087                     'change' : function(f, nv, ov) {
27088                         tb.selectedNode.setAttribute(f.attrname, nv);
27089                     }
27090                 }
27091             }));
27092              
27093         }
27094         tb.el.on('click', function(e){
27095             e.preventDefault(); // what does this do?
27096         });
27097         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27098         tb.el.hide();
27099         tb.name = nm;
27100         // dont need to disable them... as they will get hidden
27101         return tb;
27102          
27103         
27104     },
27105     buildFooter : function()
27106     {
27107         
27108         var fel = this.editor.wrap.createChild();
27109         this.footer = new Roo.Toolbar(fel);
27110         // toolbar has scrolly on left / right?
27111         var footDisp= new Roo.Toolbar.Fill();
27112         var _t = this;
27113         this.footer.add(
27114             {
27115                 text : '&lt;',
27116                 xtype: 'Button',
27117                 handler : function() {
27118                     _t.footDisp.scrollTo('left',0,true)
27119                 }
27120             }
27121         );
27122         this.footer.add( footDisp );
27123         this.footer.add( 
27124             {
27125                 text : '&gt;',
27126                 xtype: 'Button',
27127                 handler : function() {
27128                     // no animation..
27129                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27130                 }
27131             }
27132         );
27133         var fel = Roo.get(footDisp.el);
27134         fel.addClass('x-editor-context');
27135         this.footDispWrap = fel; 
27136         this.footDispWrap.overflow  = 'hidden';
27137         
27138         this.footDisp = fel.createChild();
27139         this.footDispWrap.on('click', this.onContextClick, this)
27140         
27141         
27142     },
27143     onContextClick : function (ev,dom)
27144     {
27145         ev.preventDefault();
27146         var  cn = dom.className;
27147         Roo.log(cn);
27148         if (!cn.match(/x-ed-loc-/)) {
27149             return;
27150         }
27151         var n = cn.split('-').pop();
27152         var ans = this.footerEls;
27153         var sel = ans[n];
27154         
27155          // pick
27156         var range = this.editor.createRange();
27157         
27158         range.selectNodeContents(sel);
27159         //range.selectNode(sel);
27160         
27161         
27162         var selection = this.editor.getSelection();
27163         selection.removeAllRanges();
27164         selection.addRange(range);
27165         
27166         
27167         
27168         this.updateToolbar(null, null, sel);
27169         
27170         
27171     }
27172     
27173     
27174     
27175     
27176     
27177 });
27178
27179
27180
27181
27182
27183 /*
27184  * Based on:
27185  * Ext JS Library 1.1.1
27186  * Copyright(c) 2006-2007, Ext JS, LLC.
27187  *
27188  * Originally Released Under LGPL - original licence link has changed is not relivant.
27189  *
27190  * Fork - LGPL
27191  * <script type="text/javascript">
27192  */
27193  
27194 /**
27195  * @class Roo.form.BasicForm
27196  * @extends Roo.util.Observable
27197  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27198  * @constructor
27199  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27200  * @param {Object} config Configuration options
27201  */
27202 Roo.form.BasicForm = function(el, config){
27203     this.allItems = [];
27204     this.childForms = [];
27205     Roo.apply(this, config);
27206     /*
27207      * The Roo.form.Field items in this form.
27208      * @type MixedCollection
27209      */
27210      
27211      
27212     this.items = new Roo.util.MixedCollection(false, function(o){
27213         return o.id || (o.id = Roo.id());
27214     });
27215     this.addEvents({
27216         /**
27217          * @event beforeaction
27218          * Fires before any action is performed. Return false to cancel the action.
27219          * @param {Form} this
27220          * @param {Action} action The action to be performed
27221          */
27222         beforeaction: true,
27223         /**
27224          * @event actionfailed
27225          * Fires when an action fails.
27226          * @param {Form} this
27227          * @param {Action} action The action that failed
27228          */
27229         actionfailed : true,
27230         /**
27231          * @event actioncomplete
27232          * Fires when an action is completed.
27233          * @param {Form} this
27234          * @param {Action} action The action that completed
27235          */
27236         actioncomplete : true
27237     });
27238     if(el){
27239         this.initEl(el);
27240     }
27241     Roo.form.BasicForm.superclass.constructor.call(this);
27242 };
27243
27244 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27245     /**
27246      * @cfg {String} method
27247      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27248      */
27249     /**
27250      * @cfg {DataReader} reader
27251      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27252      * This is optional as there is built-in support for processing JSON.
27253      */
27254     /**
27255      * @cfg {DataReader} errorReader
27256      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27257      * This is completely optional as there is built-in support for processing JSON.
27258      */
27259     /**
27260      * @cfg {String} url
27261      * The URL to use for form actions if one isn't supplied in the action options.
27262      */
27263     /**
27264      * @cfg {Boolean} fileUpload
27265      * Set to true if this form is a file upload.
27266      */
27267      
27268     /**
27269      * @cfg {Object} baseParams
27270      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27271      */
27272      /**
27273      
27274     /**
27275      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27276      */
27277     timeout: 30,
27278
27279     // private
27280     activeAction : null,
27281
27282     /**
27283      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27284      * or setValues() data instead of when the form was first created.
27285      */
27286     trackResetOnLoad : false,
27287     
27288     
27289     /**
27290      * childForms - used for multi-tab forms
27291      * @type {Array}
27292      */
27293     childForms : false,
27294     
27295     /**
27296      * allItems - full list of fields.
27297      * @type {Array}
27298      */
27299     allItems : false,
27300     
27301     /**
27302      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27303      * element by passing it or its id or mask the form itself by passing in true.
27304      * @type Mixed
27305      */
27306     waitMsgTarget : false,
27307
27308     // private
27309     initEl : function(el){
27310         this.el = Roo.get(el);
27311         this.id = this.el.id || Roo.id();
27312         this.el.on('submit', this.onSubmit, this);
27313         this.el.addClass('x-form');
27314     },
27315
27316     // private
27317     onSubmit : function(e){
27318         e.stopEvent();
27319     },
27320
27321     /**
27322      * Returns true if client-side validation on the form is successful.
27323      * @return Boolean
27324      */
27325     isValid : function(){
27326         var valid = true;
27327         this.items.each(function(f){
27328            if(!f.validate()){
27329                valid = false;
27330            }
27331         });
27332         return valid;
27333     },
27334
27335     /**
27336      * Returns true if any fields in this form have changed since their original load.
27337      * @return Boolean
27338      */
27339     isDirty : function(){
27340         var dirty = false;
27341         this.items.each(function(f){
27342            if(f.isDirty()){
27343                dirty = true;
27344                return false;
27345            }
27346         });
27347         return dirty;
27348     },
27349
27350     /**
27351      * Performs a predefined action (submit or load) or custom actions you define on this form.
27352      * @param {String} actionName The name of the action type
27353      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27354      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27355      * accept other config options):
27356      * <pre>
27357 Property          Type             Description
27358 ----------------  ---------------  ----------------------------------------------------------------------------------
27359 url               String           The url for the action (defaults to the form's url)
27360 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27361 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27362 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27363                                    validate the form on the client (defaults to false)
27364      * </pre>
27365      * @return {BasicForm} this
27366      */
27367     doAction : function(action, options){
27368         if(typeof action == 'string'){
27369             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27370         }
27371         if(this.fireEvent('beforeaction', this, action) !== false){
27372             this.beforeAction(action);
27373             action.run.defer(100, action);
27374         }
27375         return this;
27376     },
27377
27378     /**
27379      * Shortcut to do a submit action.
27380      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27381      * @return {BasicForm} this
27382      */
27383     submit : function(options){
27384         this.doAction('submit', options);
27385         return this;
27386     },
27387
27388     /**
27389      * Shortcut to do a load action.
27390      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27391      * @return {BasicForm} this
27392      */
27393     load : function(options){
27394         this.doAction('load', options);
27395         return this;
27396     },
27397
27398     /**
27399      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27400      * @param {Record} record The record to edit
27401      * @return {BasicForm} this
27402      */
27403     updateRecord : function(record){
27404         record.beginEdit();
27405         var fs = record.fields;
27406         fs.each(function(f){
27407             var field = this.findField(f.name);
27408             if(field){
27409                 record.set(f.name, field.getValue());
27410             }
27411         }, this);
27412         record.endEdit();
27413         return this;
27414     },
27415
27416     /**
27417      * Loads an Roo.data.Record into this form.
27418      * @param {Record} record The record to load
27419      * @return {BasicForm} this
27420      */
27421     loadRecord : function(record){
27422         this.setValues(record.data);
27423         return this;
27424     },
27425
27426     // private
27427     beforeAction : function(action){
27428         var o = action.options;
27429         
27430        
27431         if(this.waitMsgTarget === true){
27432             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27433         }else if(this.waitMsgTarget){
27434             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27435             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27436         }else {
27437             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27438         }
27439          
27440     },
27441
27442     // private
27443     afterAction : function(action, success){
27444         this.activeAction = null;
27445         var o = action.options;
27446         
27447         if(this.waitMsgTarget === true){
27448             this.el.unmask();
27449         }else if(this.waitMsgTarget){
27450             this.waitMsgTarget.unmask();
27451         }else{
27452             Roo.MessageBox.updateProgress(1);
27453             Roo.MessageBox.hide();
27454         }
27455          
27456         if(success){
27457             if(o.reset){
27458                 this.reset();
27459             }
27460             Roo.callback(o.success, o.scope, [this, action]);
27461             this.fireEvent('actioncomplete', this, action);
27462             
27463         }else{
27464             
27465             // failure condition..
27466             // we have a scenario where updates need confirming.
27467             // eg. if a locking scenario exists..
27468             // we look for { errors : { needs_confirm : true }} in the response.
27469             if (
27470                 (typeof(action.result) != 'undefined')  &&
27471                 (typeof(action.result.errors) != 'undefined')  &&
27472                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27473           ){
27474                 var _t = this;
27475                 Roo.MessageBox.confirm(
27476                     "Change requires confirmation",
27477                     action.result.errorMsg,
27478                     function(r) {
27479                         if (r != 'yes') {
27480                             return;
27481                         }
27482                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27483                     }
27484                     
27485                 );
27486                 
27487                 
27488                 
27489                 return;
27490             }
27491             
27492             Roo.callback(o.failure, o.scope, [this, action]);
27493             // show an error message if no failed handler is set..
27494             if (!this.hasListener('actionfailed')) {
27495                 Roo.MessageBox.alert("Error",
27496                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27497                         action.result.errorMsg :
27498                         "Saving Failed, please check your entries or try again"
27499                 );
27500             }
27501             
27502             this.fireEvent('actionfailed', this, action);
27503         }
27504         
27505     },
27506
27507     /**
27508      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27509      * @param {String} id The value to search for
27510      * @return Field
27511      */
27512     findField : function(id){
27513         var field = this.items.get(id);
27514         if(!field){
27515             this.items.each(function(f){
27516                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27517                     field = f;
27518                     return false;
27519                 }
27520             });
27521         }
27522         return field || null;
27523     },
27524
27525     /**
27526      * Add a secondary form to this one, 
27527      * Used to provide tabbed forms. One form is primary, with hidden values 
27528      * which mirror the elements from the other forms.
27529      * 
27530      * @param {Roo.form.Form} form to add.
27531      * 
27532      */
27533     addForm : function(form)
27534     {
27535        
27536         if (this.childForms.indexOf(form) > -1) {
27537             // already added..
27538             return;
27539         }
27540         this.childForms.push(form);
27541         var n = '';
27542         Roo.each(form.allItems, function (fe) {
27543             
27544             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27545             if (this.findField(n)) { // already added..
27546                 return;
27547             }
27548             var add = new Roo.form.Hidden({
27549                 name : n
27550             });
27551             add.render(this.el);
27552             
27553             this.add( add );
27554         }, this);
27555         
27556     },
27557     /**
27558      * Mark fields in this form invalid in bulk.
27559      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27560      * @return {BasicForm} this
27561      */
27562     markInvalid : function(errors){
27563         if(errors instanceof Array){
27564             for(var i = 0, len = errors.length; i < len; i++){
27565                 var fieldError = errors[i];
27566                 var f = this.findField(fieldError.id);
27567                 if(f){
27568                     f.markInvalid(fieldError.msg);
27569                 }
27570             }
27571         }else{
27572             var field, id;
27573             for(id in errors){
27574                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27575                     field.markInvalid(errors[id]);
27576                 }
27577             }
27578         }
27579         Roo.each(this.childForms || [], function (f) {
27580             f.markInvalid(errors);
27581         });
27582         
27583         return this;
27584     },
27585
27586     /**
27587      * Set values for fields in this form in bulk.
27588      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27589      * @return {BasicForm} this
27590      */
27591     setValues : function(values){
27592         if(values instanceof Array){ // array of objects
27593             for(var i = 0, len = values.length; i < len; i++){
27594                 var v = values[i];
27595                 var f = this.findField(v.id);
27596                 if(f){
27597                     f.setValue(v.value);
27598                     if(this.trackResetOnLoad){
27599                         f.originalValue = f.getValue();
27600                     }
27601                 }
27602             }
27603         }else{ // object hash
27604             var field, id;
27605             for(id in values){
27606                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27607                     
27608                     if (field.setFromData && 
27609                         field.valueField && 
27610                         field.displayField &&
27611                         // combos' with local stores can 
27612                         // be queried via setValue()
27613                         // to set their value..
27614                         (field.store && !field.store.isLocal)
27615                         ) {
27616                         // it's a combo
27617                         var sd = { };
27618                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27619                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27620                         field.setFromData(sd);
27621                         
27622                     } else {
27623                         field.setValue(values[id]);
27624                     }
27625                     
27626                     
27627                     if(this.trackResetOnLoad){
27628                         field.originalValue = field.getValue();
27629                     }
27630                 }
27631             }
27632         }
27633          
27634         Roo.each(this.childForms || [], function (f) {
27635             f.setValues(values);
27636         });
27637                 
27638         return this;
27639     },
27640
27641     /**
27642      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27643      * they are returned as an array.
27644      * @param {Boolean} asString
27645      * @return {Object}
27646      */
27647     getValues : function(asString){
27648         if (this.childForms) {
27649             // copy values from the child forms
27650             Roo.each(this.childForms, function (f) {
27651                 this.setValues(f.getValues());
27652             }, this);
27653         }
27654         
27655         
27656         
27657         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27658         if(asString === true){
27659             return fs;
27660         }
27661         return Roo.urlDecode(fs);
27662     },
27663     
27664     /**
27665      * Returns the fields in this form as an object with key/value pairs. 
27666      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27667      * @return {Object}
27668      */
27669     getFieldValues : function(with_hidden)
27670     {
27671         if (this.childForms) {
27672             // copy values from the child forms
27673             // should this call getFieldValues - probably not as we do not currently copy
27674             // hidden fields when we generate..
27675             Roo.each(this.childForms, function (f) {
27676                 this.setValues(f.getValues());
27677             }, this);
27678         }
27679         
27680         var ret = {};
27681         this.items.each(function(f){
27682             if (!f.getName()) {
27683                 return;
27684             }
27685             var v = f.getValue();
27686             // not sure if this supported any more..
27687             if ((typeof(v) == 'object') && f.getRawValue) {
27688                 v = f.getRawValue() ; // dates..
27689             }
27690             // combo boxes where name != hiddenName...
27691             if (f.name != f.getName()) {
27692                 ret[f.name] = f.getRawValue();
27693             }
27694             ret[f.getName()] = v;
27695         });
27696         
27697         return ret;
27698     },
27699
27700     /**
27701      * Clears all invalid messages in this form.
27702      * @return {BasicForm} this
27703      */
27704     clearInvalid : function(){
27705         this.items.each(function(f){
27706            f.clearInvalid();
27707         });
27708         
27709         Roo.each(this.childForms || [], function (f) {
27710             f.clearInvalid();
27711         });
27712         
27713         
27714         return this;
27715     },
27716
27717     /**
27718      * Resets this form.
27719      * @return {BasicForm} this
27720      */
27721     reset : function(){
27722         this.items.each(function(f){
27723             f.reset();
27724         });
27725         
27726         Roo.each(this.childForms || [], function (f) {
27727             f.reset();
27728         });
27729        
27730         
27731         return this;
27732     },
27733
27734     /**
27735      * Add Roo.form components to this form.
27736      * @param {Field} field1
27737      * @param {Field} field2 (optional)
27738      * @param {Field} etc (optional)
27739      * @return {BasicForm} this
27740      */
27741     add : function(){
27742         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27743         return this;
27744     },
27745
27746
27747     /**
27748      * Removes a field from the items collection (does NOT remove its markup).
27749      * @param {Field} field
27750      * @return {BasicForm} this
27751      */
27752     remove : function(field){
27753         this.items.remove(field);
27754         return this;
27755     },
27756
27757     /**
27758      * Looks at the fields in this form, checks them for an id attribute,
27759      * and calls applyTo on the existing dom element with that id.
27760      * @return {BasicForm} this
27761      */
27762     render : function(){
27763         this.items.each(function(f){
27764             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27765                 f.applyTo(f.id);
27766             }
27767         });
27768         return this;
27769     },
27770
27771     /**
27772      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27773      * @param {Object} values
27774      * @return {BasicForm} this
27775      */
27776     applyToFields : function(o){
27777         this.items.each(function(f){
27778            Roo.apply(f, o);
27779         });
27780         return this;
27781     },
27782
27783     /**
27784      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27785      * @param {Object} values
27786      * @return {BasicForm} this
27787      */
27788     applyIfToFields : function(o){
27789         this.items.each(function(f){
27790            Roo.applyIf(f, o);
27791         });
27792         return this;
27793     }
27794 });
27795
27796 // back compat
27797 Roo.BasicForm = Roo.form.BasicForm;/*
27798  * Based on:
27799  * Ext JS Library 1.1.1
27800  * Copyright(c) 2006-2007, Ext JS, LLC.
27801  *
27802  * Originally Released Under LGPL - original licence link has changed is not relivant.
27803  *
27804  * Fork - LGPL
27805  * <script type="text/javascript">
27806  */
27807
27808 /**
27809  * @class Roo.form.Form
27810  * @extends Roo.form.BasicForm
27811  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27812  * @constructor
27813  * @param {Object} config Configuration options
27814  */
27815 Roo.form.Form = function(config){
27816     var xitems =  [];
27817     if (config.items) {
27818         xitems = config.items;
27819         delete config.items;
27820     }
27821    
27822     
27823     Roo.form.Form.superclass.constructor.call(this, null, config);
27824     this.url = this.url || this.action;
27825     if(!this.root){
27826         this.root = new Roo.form.Layout(Roo.applyIf({
27827             id: Roo.id()
27828         }, config));
27829     }
27830     this.active = this.root;
27831     /**
27832      * Array of all the buttons that have been added to this form via {@link addButton}
27833      * @type Array
27834      */
27835     this.buttons = [];
27836     this.allItems = [];
27837     this.addEvents({
27838         /**
27839          * @event clientvalidation
27840          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27841          * @param {Form} this
27842          * @param {Boolean} valid true if the form has passed client-side validation
27843          */
27844         clientvalidation: true,
27845         /**
27846          * @event rendered
27847          * Fires when the form is rendered
27848          * @param {Roo.form.Form} form
27849          */
27850         rendered : true
27851     });
27852     
27853     if (this.progressUrl) {
27854             // push a hidden field onto the list of fields..
27855             this.addxtype( {
27856                     xns: Roo.form, 
27857                     xtype : 'Hidden', 
27858                     name : 'UPLOAD_IDENTIFIER' 
27859             });
27860         }
27861         
27862     
27863     Roo.each(xitems, this.addxtype, this);
27864     
27865     
27866     
27867 };
27868
27869 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27870     /**
27871      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27872      */
27873     /**
27874      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27875      */
27876     /**
27877      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27878      */
27879     buttonAlign:'center',
27880
27881     /**
27882      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27883      */
27884     minButtonWidth:75,
27885
27886     /**
27887      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27888      * This property cascades to child containers if not set.
27889      */
27890     labelAlign:'left',
27891
27892     /**
27893      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27894      * fires a looping event with that state. This is required to bind buttons to the valid
27895      * state using the config value formBind:true on the button.
27896      */
27897     monitorValid : false,
27898
27899     /**
27900      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27901      */
27902     monitorPoll : 200,
27903     
27904     /**
27905      * @cfg {String} progressUrl - Url to return progress data 
27906      */
27907     
27908     progressUrl : false,
27909   
27910     /**
27911      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27912      * fields are added and the column is closed. If no fields are passed the column remains open
27913      * until end() is called.
27914      * @param {Object} config The config to pass to the column
27915      * @param {Field} field1 (optional)
27916      * @param {Field} field2 (optional)
27917      * @param {Field} etc (optional)
27918      * @return Column The column container object
27919      */
27920     column : function(c){
27921         var col = new Roo.form.Column(c);
27922         this.start(col);
27923         if(arguments.length > 1){ // duplicate code required because of Opera
27924             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27925             this.end();
27926         }
27927         return col;
27928     },
27929
27930     /**
27931      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27932      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27933      * until end() is called.
27934      * @param {Object} config The config to pass to the fieldset
27935      * @param {Field} field1 (optional)
27936      * @param {Field} field2 (optional)
27937      * @param {Field} etc (optional)
27938      * @return FieldSet The fieldset container object
27939      */
27940     fieldset : function(c){
27941         var fs = new Roo.form.FieldSet(c);
27942         this.start(fs);
27943         if(arguments.length > 1){ // duplicate code required because of Opera
27944             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27945             this.end();
27946         }
27947         return fs;
27948     },
27949
27950     /**
27951      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27952      * fields are added and the container is closed. If no fields are passed the container remains open
27953      * until end() is called.
27954      * @param {Object} config The config to pass to the Layout
27955      * @param {Field} field1 (optional)
27956      * @param {Field} field2 (optional)
27957      * @param {Field} etc (optional)
27958      * @return Layout The container object
27959      */
27960     container : function(c){
27961         var l = new Roo.form.Layout(c);
27962         this.start(l);
27963         if(arguments.length > 1){ // duplicate code required because of Opera
27964             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27965             this.end();
27966         }
27967         return l;
27968     },
27969
27970     /**
27971      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27972      * @param {Object} container A Roo.form.Layout or subclass of Layout
27973      * @return {Form} this
27974      */
27975     start : function(c){
27976         // cascade label info
27977         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27978         this.active.stack.push(c);
27979         c.ownerCt = this.active;
27980         this.active = c;
27981         return this;
27982     },
27983
27984     /**
27985      * Closes the current open container
27986      * @return {Form} this
27987      */
27988     end : function(){
27989         if(this.active == this.root){
27990             return this;
27991         }
27992         this.active = this.active.ownerCt;
27993         return this;
27994     },
27995
27996     /**
27997      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27998      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27999      * as the label of the field.
28000      * @param {Field} field1
28001      * @param {Field} field2 (optional)
28002      * @param {Field} etc. (optional)
28003      * @return {Form} this
28004      */
28005     add : function(){
28006         this.active.stack.push.apply(this.active.stack, arguments);
28007         this.allItems.push.apply(this.allItems,arguments);
28008         var r = [];
28009         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28010             if(a[i].isFormField){
28011                 r.push(a[i]);
28012             }
28013         }
28014         if(r.length > 0){
28015             Roo.form.Form.superclass.add.apply(this, r);
28016         }
28017         return this;
28018     },
28019     
28020
28021     
28022     
28023     
28024      /**
28025      * Find any element that has been added to a form, using it's ID or name
28026      * This can include framesets, columns etc. along with regular fields..
28027      * @param {String} id - id or name to find.
28028      
28029      * @return {Element} e - or false if nothing found.
28030      */
28031     findbyId : function(id)
28032     {
28033         var ret = false;
28034         if (!id) {
28035             return ret;
28036         }
28037         Roo.each(this.allItems, function(f){
28038             if (f.id == id || f.name == id ){
28039                 ret = f;
28040                 return false;
28041             }
28042         });
28043         return ret;
28044     },
28045
28046     
28047     
28048     /**
28049      * Render this form into the passed container. This should only be called once!
28050      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28051      * @return {Form} this
28052      */
28053     render : function(ct)
28054     {
28055         
28056         
28057         
28058         ct = Roo.get(ct);
28059         var o = this.autoCreate || {
28060             tag: 'form',
28061             method : this.method || 'POST',
28062             id : this.id || Roo.id()
28063         };
28064         this.initEl(ct.createChild(o));
28065
28066         this.root.render(this.el);
28067         
28068        
28069              
28070         this.items.each(function(f){
28071             f.render('x-form-el-'+f.id);
28072         });
28073
28074         if(this.buttons.length > 0){
28075             // tables are required to maintain order and for correct IE layout
28076             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28077                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28078                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28079             }}, null, true);
28080             var tr = tb.getElementsByTagName('tr')[0];
28081             for(var i = 0, len = this.buttons.length; i < len; i++) {
28082                 var b = this.buttons[i];
28083                 var td = document.createElement('td');
28084                 td.className = 'x-form-btn-td';
28085                 b.render(tr.appendChild(td));
28086             }
28087         }
28088         if(this.monitorValid){ // initialize after render
28089             this.startMonitoring();
28090         }
28091         this.fireEvent('rendered', this);
28092         return this;
28093     },
28094
28095     /**
28096      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28097      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28098      * object or a valid Roo.DomHelper element config
28099      * @param {Function} handler The function called when the button is clicked
28100      * @param {Object} scope (optional) The scope of the handler function
28101      * @return {Roo.Button}
28102      */
28103     addButton : function(config, handler, scope){
28104         var bc = {
28105             handler: handler,
28106             scope: scope,
28107             minWidth: this.minButtonWidth,
28108             hideParent:true
28109         };
28110         if(typeof config == "string"){
28111             bc.text = config;
28112         }else{
28113             Roo.apply(bc, config);
28114         }
28115         var btn = new Roo.Button(null, bc);
28116         this.buttons.push(btn);
28117         return btn;
28118     },
28119
28120      /**
28121      * Adds a series of form elements (using the xtype property as the factory method.
28122      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28123      * @param {Object} config 
28124      */
28125     
28126     addxtype : function()
28127     {
28128         var ar = Array.prototype.slice.call(arguments, 0);
28129         var ret = false;
28130         for(var i = 0; i < ar.length; i++) {
28131             if (!ar[i]) {
28132                 continue; // skip -- if this happends something invalid got sent, we 
28133                 // should ignore it, as basically that interface element will not show up
28134                 // and that should be pretty obvious!!
28135             }
28136             
28137             if (Roo.form[ar[i].xtype]) {
28138                 ar[i].form = this;
28139                 var fe = Roo.factory(ar[i], Roo.form);
28140                 if (!ret) {
28141                     ret = fe;
28142                 }
28143                 fe.form = this;
28144                 if (fe.store) {
28145                     fe.store.form = this;
28146                 }
28147                 if (fe.isLayout) {  
28148                          
28149                     this.start(fe);
28150                     this.allItems.push(fe);
28151                     if (fe.items && fe.addxtype) {
28152                         fe.addxtype.apply(fe, fe.items);
28153                         delete fe.items;
28154                     }
28155                      this.end();
28156                     continue;
28157                 }
28158                 
28159                 
28160                  
28161                 this.add(fe);
28162               //  console.log('adding ' + ar[i].xtype);
28163             }
28164             if (ar[i].xtype == 'Button') {  
28165                 //console.log('adding button');
28166                 //console.log(ar[i]);
28167                 this.addButton(ar[i]);
28168                 this.allItems.push(fe);
28169                 continue;
28170             }
28171             
28172             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28173                 alert('end is not supported on xtype any more, use items');
28174             //    this.end();
28175             //    //console.log('adding end');
28176             }
28177             
28178         }
28179         return ret;
28180     },
28181     
28182     /**
28183      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28184      * option "monitorValid"
28185      */
28186     startMonitoring : function(){
28187         if(!this.bound){
28188             this.bound = true;
28189             Roo.TaskMgr.start({
28190                 run : this.bindHandler,
28191                 interval : this.monitorPoll || 200,
28192                 scope: this
28193             });
28194         }
28195     },
28196
28197     /**
28198      * Stops monitoring of the valid state of this form
28199      */
28200     stopMonitoring : function(){
28201         this.bound = false;
28202     },
28203
28204     // private
28205     bindHandler : function(){
28206         if(!this.bound){
28207             return false; // stops binding
28208         }
28209         var valid = true;
28210         this.items.each(function(f){
28211             if(!f.isValid(true)){
28212                 valid = false;
28213                 return false;
28214             }
28215         });
28216         for(var i = 0, len = this.buttons.length; i < len; i++){
28217             var btn = this.buttons[i];
28218             if(btn.formBind === true && btn.disabled === valid){
28219                 btn.setDisabled(!valid);
28220             }
28221         }
28222         this.fireEvent('clientvalidation', this, valid);
28223     }
28224     
28225     
28226     
28227     
28228     
28229     
28230     
28231     
28232 });
28233
28234
28235 // back compat
28236 Roo.Form = Roo.form.Form;
28237 /*
28238  * Based on:
28239  * Ext JS Library 1.1.1
28240  * Copyright(c) 2006-2007, Ext JS, LLC.
28241  *
28242  * Originally Released Under LGPL - original licence link has changed is not relivant.
28243  *
28244  * Fork - LGPL
28245  * <script type="text/javascript">
28246  */
28247  
28248  /**
28249  * @class Roo.form.Action
28250  * Internal Class used to handle form actions
28251  * @constructor
28252  * @param {Roo.form.BasicForm} el The form element or its id
28253  * @param {Object} config Configuration options
28254  */
28255  
28256  
28257 // define the action interface
28258 Roo.form.Action = function(form, options){
28259     this.form = form;
28260     this.options = options || {};
28261 };
28262 /**
28263  * Client Validation Failed
28264  * @const 
28265  */
28266 Roo.form.Action.CLIENT_INVALID = 'client';
28267 /**
28268  * Server Validation Failed
28269  * @const 
28270  */
28271  Roo.form.Action.SERVER_INVALID = 'server';
28272  /**
28273  * Connect to Server Failed
28274  * @const 
28275  */
28276 Roo.form.Action.CONNECT_FAILURE = 'connect';
28277 /**
28278  * Reading Data from Server Failed
28279  * @const 
28280  */
28281 Roo.form.Action.LOAD_FAILURE = 'load';
28282
28283 Roo.form.Action.prototype = {
28284     type : 'default',
28285     failureType : undefined,
28286     response : undefined,
28287     result : undefined,
28288
28289     // interface method
28290     run : function(options){
28291
28292     },
28293
28294     // interface method
28295     success : function(response){
28296
28297     },
28298
28299     // interface method
28300     handleResponse : function(response){
28301
28302     },
28303
28304     // default connection failure
28305     failure : function(response){
28306         
28307         this.response = response;
28308         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28309         this.form.afterAction(this, false);
28310     },
28311
28312     processResponse : function(response){
28313         this.response = response;
28314         if(!response.responseText){
28315             return true;
28316         }
28317         this.result = this.handleResponse(response);
28318         return this.result;
28319     },
28320
28321     // utility functions used internally
28322     getUrl : function(appendParams){
28323         var url = this.options.url || this.form.url || this.form.el.dom.action;
28324         if(appendParams){
28325             var p = this.getParams();
28326             if(p){
28327                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28328             }
28329         }
28330         return url;
28331     },
28332
28333     getMethod : function(){
28334         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28335     },
28336
28337     getParams : function(){
28338         var bp = this.form.baseParams;
28339         var p = this.options.params;
28340         if(p){
28341             if(typeof p == "object"){
28342                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28343             }else if(typeof p == 'string' && bp){
28344                 p += '&' + Roo.urlEncode(bp);
28345             }
28346         }else if(bp){
28347             p = Roo.urlEncode(bp);
28348         }
28349         return p;
28350     },
28351
28352     createCallback : function(){
28353         return {
28354             success: this.success,
28355             failure: this.failure,
28356             scope: this,
28357             timeout: (this.form.timeout*1000),
28358             upload: this.form.fileUpload ? this.success : undefined
28359         };
28360     }
28361 };
28362
28363 Roo.form.Action.Submit = function(form, options){
28364     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28365 };
28366
28367 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28368     type : 'submit',
28369
28370     haveProgress : false,
28371     uploadComplete : false,
28372     
28373     // uploadProgress indicator.
28374     uploadProgress : function()
28375     {
28376         if (!this.form.progressUrl) {
28377             return;
28378         }
28379         
28380         if (!this.haveProgress) {
28381             Roo.MessageBox.progress("Uploading", "Uploading");
28382         }
28383         if (this.uploadComplete) {
28384            Roo.MessageBox.hide();
28385            return;
28386         }
28387         
28388         this.haveProgress = true;
28389    
28390         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28391         
28392         var c = new Roo.data.Connection();
28393         c.request({
28394             url : this.form.progressUrl,
28395             params: {
28396                 id : uid
28397             },
28398             method: 'GET',
28399             success : function(req){
28400                //console.log(data);
28401                 var rdata = false;
28402                 var edata;
28403                 try  {
28404                    rdata = Roo.decode(req.responseText)
28405                 } catch (e) {
28406                     Roo.log("Invalid data from server..");
28407                     Roo.log(edata);
28408                     return;
28409                 }
28410                 if (!rdata || !rdata.success) {
28411                     Roo.log(rdata);
28412                     return;
28413                 }
28414                 var data = rdata.data;
28415                 
28416                 if (this.uploadComplete) {
28417                    Roo.MessageBox.hide();
28418                    return;
28419                 }
28420                    
28421                 if (data){
28422                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28423                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28424                     );
28425                 }
28426                 this.uploadProgress.defer(2000,this);
28427             },
28428        
28429             failure: function(data) {
28430                 Roo.log('progress url failed ');
28431                 Roo.log(data);
28432             },
28433             scope : this
28434         });
28435            
28436     },
28437     
28438     
28439     run : function()
28440     {
28441         // run get Values on the form, so it syncs any secondary forms.
28442         this.form.getValues();
28443         
28444         var o = this.options;
28445         var method = this.getMethod();
28446         var isPost = method == 'POST';
28447         if(o.clientValidation === false || this.form.isValid()){
28448             
28449             if (this.form.progressUrl) {
28450                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28451                     (new Date() * 1) + '' + Math.random());
28452                     
28453             } 
28454             
28455             
28456             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28457                 form:this.form.el.dom,
28458                 url:this.getUrl(!isPost),
28459                 method: method,
28460                 params:isPost ? this.getParams() : null,
28461                 isUpload: this.form.fileUpload
28462             }));
28463             
28464             this.uploadProgress();
28465
28466         }else if (o.clientValidation !== false){ // client validation failed
28467             this.failureType = Roo.form.Action.CLIENT_INVALID;
28468             this.form.afterAction(this, false);
28469         }
28470     },
28471
28472     success : function(response)
28473     {
28474         this.uploadComplete= true;
28475         if (this.haveProgress) {
28476             Roo.MessageBox.hide();
28477         }
28478         
28479         
28480         var result = this.processResponse(response);
28481         if(result === true || result.success){
28482             this.form.afterAction(this, true);
28483             return;
28484         }
28485         if(result.errors){
28486             this.form.markInvalid(result.errors);
28487             this.failureType = Roo.form.Action.SERVER_INVALID;
28488         }
28489         this.form.afterAction(this, false);
28490     },
28491     failure : function(response)
28492     {
28493         this.uploadComplete= true;
28494         if (this.haveProgress) {
28495             Roo.MessageBox.hide();
28496         }
28497         
28498         this.response = response;
28499         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28500         this.form.afterAction(this, false);
28501     },
28502     
28503     handleResponse : function(response){
28504         if(this.form.errorReader){
28505             var rs = this.form.errorReader.read(response);
28506             var errors = [];
28507             if(rs.records){
28508                 for(var i = 0, len = rs.records.length; i < len; i++) {
28509                     var r = rs.records[i];
28510                     errors[i] = r.data;
28511                 }
28512             }
28513             if(errors.length < 1){
28514                 errors = null;
28515             }
28516             return {
28517                 success : rs.success,
28518                 errors : errors
28519             };
28520         }
28521         var ret = false;
28522         try {
28523             ret = Roo.decode(response.responseText);
28524         } catch (e) {
28525             ret = {
28526                 success: false,
28527                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28528                 errors : []
28529             };
28530         }
28531         return ret;
28532         
28533     }
28534 });
28535
28536
28537 Roo.form.Action.Load = function(form, options){
28538     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28539     this.reader = this.form.reader;
28540 };
28541
28542 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28543     type : 'load',
28544
28545     run : function(){
28546         
28547         Roo.Ajax.request(Roo.apply(
28548                 this.createCallback(), {
28549                     method:this.getMethod(),
28550                     url:this.getUrl(false),
28551                     params:this.getParams()
28552         }));
28553     },
28554
28555     success : function(response){
28556         
28557         var result = this.processResponse(response);
28558         if(result === true || !result.success || !result.data){
28559             this.failureType = Roo.form.Action.LOAD_FAILURE;
28560             this.form.afterAction(this, false);
28561             return;
28562         }
28563         this.form.clearInvalid();
28564         this.form.setValues(result.data);
28565         this.form.afterAction(this, true);
28566     },
28567
28568     handleResponse : function(response){
28569         if(this.form.reader){
28570             var rs = this.form.reader.read(response);
28571             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28572             return {
28573                 success : rs.success,
28574                 data : data
28575             };
28576         }
28577         return Roo.decode(response.responseText);
28578     }
28579 });
28580
28581 Roo.form.Action.ACTION_TYPES = {
28582     'load' : Roo.form.Action.Load,
28583     'submit' : Roo.form.Action.Submit
28584 };/*
28585  * Based on:
28586  * Ext JS Library 1.1.1
28587  * Copyright(c) 2006-2007, Ext JS, LLC.
28588  *
28589  * Originally Released Under LGPL - original licence link has changed is not relivant.
28590  *
28591  * Fork - LGPL
28592  * <script type="text/javascript">
28593  */
28594  
28595 /**
28596  * @class Roo.form.Layout
28597  * @extends Roo.Component
28598  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28599  * @constructor
28600  * @param {Object} config Configuration options
28601  */
28602 Roo.form.Layout = function(config){
28603     var xitems = [];
28604     if (config.items) {
28605         xitems = config.items;
28606         delete config.items;
28607     }
28608     Roo.form.Layout.superclass.constructor.call(this, config);
28609     this.stack = [];
28610     Roo.each(xitems, this.addxtype, this);
28611      
28612 };
28613
28614 Roo.extend(Roo.form.Layout, Roo.Component, {
28615     /**
28616      * @cfg {String/Object} autoCreate
28617      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28618      */
28619     /**
28620      * @cfg {String/Object/Function} style
28621      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28622      * a function which returns such a specification.
28623      */
28624     /**
28625      * @cfg {String} labelAlign
28626      * Valid values are "left," "top" and "right" (defaults to "left")
28627      */
28628     /**
28629      * @cfg {Number} labelWidth
28630      * Fixed width in pixels of all field labels (defaults to undefined)
28631      */
28632     /**
28633      * @cfg {Boolean} clear
28634      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28635      */
28636     clear : true,
28637     /**
28638      * @cfg {String} labelSeparator
28639      * The separator to use after field labels (defaults to ':')
28640      */
28641     labelSeparator : ':',
28642     /**
28643      * @cfg {Boolean} hideLabels
28644      * True to suppress the display of field labels in this layout (defaults to false)
28645      */
28646     hideLabels : false,
28647
28648     // private
28649     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28650     
28651     isLayout : true,
28652     
28653     // private
28654     onRender : function(ct, position){
28655         if(this.el){ // from markup
28656             this.el = Roo.get(this.el);
28657         }else {  // generate
28658             var cfg = this.getAutoCreate();
28659             this.el = ct.createChild(cfg, position);
28660         }
28661         if(this.style){
28662             this.el.applyStyles(this.style);
28663         }
28664         if(this.labelAlign){
28665             this.el.addClass('x-form-label-'+this.labelAlign);
28666         }
28667         if(this.hideLabels){
28668             this.labelStyle = "display:none";
28669             this.elementStyle = "padding-left:0;";
28670         }else{
28671             if(typeof this.labelWidth == 'number'){
28672                 this.labelStyle = "width:"+this.labelWidth+"px;";
28673                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28674             }
28675             if(this.labelAlign == 'top'){
28676                 this.labelStyle = "width:auto;";
28677                 this.elementStyle = "padding-left:0;";
28678             }
28679         }
28680         var stack = this.stack;
28681         var slen = stack.length;
28682         if(slen > 0){
28683             if(!this.fieldTpl){
28684                 var t = new Roo.Template(
28685                     '<div class="x-form-item {5}">',
28686                         '<label for="{0}" style="{2}">{1}{4}</label>',
28687                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28688                         '</div>',
28689                     '</div><div class="x-form-clear-left"></div>'
28690                 );
28691                 t.disableFormats = true;
28692                 t.compile();
28693                 Roo.form.Layout.prototype.fieldTpl = t;
28694             }
28695             for(var i = 0; i < slen; i++) {
28696                 if(stack[i].isFormField){
28697                     this.renderField(stack[i]);
28698                 }else{
28699                     this.renderComponent(stack[i]);
28700                 }
28701             }
28702         }
28703         if(this.clear){
28704             this.el.createChild({cls:'x-form-clear'});
28705         }
28706     },
28707
28708     // private
28709     renderField : function(f){
28710         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28711                f.id, //0
28712                f.fieldLabel, //1
28713                f.labelStyle||this.labelStyle||'', //2
28714                this.elementStyle||'', //3
28715                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28716                f.itemCls||this.itemCls||''  //5
28717        ], true).getPrevSibling());
28718     },
28719
28720     // private
28721     renderComponent : function(c){
28722         c.render(c.isLayout ? this.el : this.el.createChild());    
28723     },
28724     /**
28725      * Adds a object form elements (using the xtype property as the factory method.)
28726      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28727      * @param {Object} config 
28728      */
28729     addxtype : function(o)
28730     {
28731         // create the lement.
28732         o.form = this.form;
28733         var fe = Roo.factory(o, Roo.form);
28734         this.form.allItems.push(fe);
28735         this.stack.push(fe);
28736         
28737         if (fe.isFormField) {
28738             this.form.items.add(fe);
28739         }
28740          
28741         return fe;
28742     }
28743 });
28744
28745 /**
28746  * @class Roo.form.Column
28747  * @extends Roo.form.Layout
28748  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28749  * @constructor
28750  * @param {Object} config Configuration options
28751  */
28752 Roo.form.Column = function(config){
28753     Roo.form.Column.superclass.constructor.call(this, config);
28754 };
28755
28756 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28757     /**
28758      * @cfg {Number/String} width
28759      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28760      */
28761     /**
28762      * @cfg {String/Object} autoCreate
28763      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28764      */
28765
28766     // private
28767     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28768
28769     // private
28770     onRender : function(ct, position){
28771         Roo.form.Column.superclass.onRender.call(this, ct, position);
28772         if(this.width){
28773             this.el.setWidth(this.width);
28774         }
28775     }
28776 });
28777
28778
28779 /**
28780  * @class Roo.form.Row
28781  * @extends Roo.form.Layout
28782  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28783  * @constructor
28784  * @param {Object} config Configuration options
28785  */
28786
28787  
28788 Roo.form.Row = function(config){
28789     Roo.form.Row.superclass.constructor.call(this, config);
28790 };
28791  
28792 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28793       /**
28794      * @cfg {Number/String} width
28795      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28796      */
28797     /**
28798      * @cfg {Number/String} height
28799      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28800      */
28801     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28802     
28803     padWidth : 20,
28804     // private
28805     onRender : function(ct, position){
28806         //console.log('row render');
28807         if(!this.rowTpl){
28808             var t = new Roo.Template(
28809                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28810                     '<label for="{0}" style="{2}">{1}{4}</label>',
28811                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28812                     '</div>',
28813                 '</div>'
28814             );
28815             t.disableFormats = true;
28816             t.compile();
28817             Roo.form.Layout.prototype.rowTpl = t;
28818         }
28819         this.fieldTpl = this.rowTpl;
28820         
28821         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28822         var labelWidth = 100;
28823         
28824         if ((this.labelAlign != 'top')) {
28825             if (typeof this.labelWidth == 'number') {
28826                 labelWidth = this.labelWidth
28827             }
28828             this.padWidth =  20 + labelWidth;
28829             
28830         }
28831         
28832         Roo.form.Column.superclass.onRender.call(this, ct, position);
28833         if(this.width){
28834             this.el.setWidth(this.width);
28835         }
28836         if(this.height){
28837             this.el.setHeight(this.height);
28838         }
28839     },
28840     
28841     // private
28842     renderField : function(f){
28843         f.fieldEl = this.fieldTpl.append(this.el, [
28844                f.id, f.fieldLabel,
28845                f.labelStyle||this.labelStyle||'',
28846                this.elementStyle||'',
28847                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28848                f.itemCls||this.itemCls||'',
28849                f.width ? f.width + this.padWidth : 160 + this.padWidth
28850        ],true);
28851     }
28852 });
28853  
28854
28855 /**
28856  * @class Roo.form.FieldSet
28857  * @extends Roo.form.Layout
28858  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28859  * @constructor
28860  * @param {Object} config Configuration options
28861  */
28862 Roo.form.FieldSet = function(config){
28863     Roo.form.FieldSet.superclass.constructor.call(this, config);
28864 };
28865
28866 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28867     /**
28868      * @cfg {String} legend
28869      * The text to display as the legend for the FieldSet (defaults to '')
28870      */
28871     /**
28872      * @cfg {String/Object} autoCreate
28873      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28874      */
28875
28876     // private
28877     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28878
28879     // private
28880     onRender : function(ct, position){
28881         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28882         if(this.legend){
28883             this.setLegend(this.legend);
28884         }
28885     },
28886
28887     // private
28888     setLegend : function(text){
28889         if(this.rendered){
28890             this.el.child('legend').update(text);
28891         }
28892     }
28893 });/*
28894  * Based on:
28895  * Ext JS Library 1.1.1
28896  * Copyright(c) 2006-2007, Ext JS, LLC.
28897  *
28898  * Originally Released Under LGPL - original licence link has changed is not relivant.
28899  *
28900  * Fork - LGPL
28901  * <script type="text/javascript">
28902  */
28903 /**
28904  * @class Roo.form.VTypes
28905  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28906  * @singleton
28907  */
28908 Roo.form.VTypes = function(){
28909     // closure these in so they are only created once.
28910     var alpha = /^[a-zA-Z_]+$/;
28911     var alphanum = /^[a-zA-Z0-9_]+$/;
28912     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28913     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28914
28915     // All these messages and functions are configurable
28916     return {
28917         /**
28918          * The function used to validate email addresses
28919          * @param {String} value The email address
28920          */
28921         'email' : function(v){
28922             return email.test(v);
28923         },
28924         /**
28925          * The error text to display when the email validation function returns false
28926          * @type String
28927          */
28928         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28929         /**
28930          * The keystroke filter mask to be applied on email input
28931          * @type RegExp
28932          */
28933         'emailMask' : /[a-z0-9_\.\-@]/i,
28934
28935         /**
28936          * The function used to validate URLs
28937          * @param {String} value The URL
28938          */
28939         'url' : function(v){
28940             return url.test(v);
28941         },
28942         /**
28943          * The error text to display when the url validation function returns false
28944          * @type String
28945          */
28946         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28947         
28948         /**
28949          * The function used to validate alpha values
28950          * @param {String} value The value
28951          */
28952         'alpha' : function(v){
28953             return alpha.test(v);
28954         },
28955         /**
28956          * The error text to display when the alpha validation function returns false
28957          * @type String
28958          */
28959         'alphaText' : 'This field should only contain letters and _',
28960         /**
28961          * The keystroke filter mask to be applied on alpha input
28962          * @type RegExp
28963          */
28964         'alphaMask' : /[a-z_]/i,
28965
28966         /**
28967          * The function used to validate alphanumeric values
28968          * @param {String} value The value
28969          */
28970         'alphanum' : function(v){
28971             return alphanum.test(v);
28972         },
28973         /**
28974          * The error text to display when the alphanumeric validation function returns false
28975          * @type String
28976          */
28977         'alphanumText' : 'This field should only contain letters, numbers and _',
28978         /**
28979          * The keystroke filter mask to be applied on alphanumeric input
28980          * @type RegExp
28981          */
28982         'alphanumMask' : /[a-z0-9_]/i
28983     };
28984 }();//<script type="text/javascript">
28985
28986 /**
28987  * @class Roo.form.FCKeditor
28988  * @extends Roo.form.TextArea
28989  * Wrapper around the FCKEditor http://www.fckeditor.net
28990  * @constructor
28991  * Creates a new FCKeditor
28992  * @param {Object} config Configuration options
28993  */
28994 Roo.form.FCKeditor = function(config){
28995     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28996     this.addEvents({
28997          /**
28998          * @event editorinit
28999          * Fired when the editor is initialized - you can add extra handlers here..
29000          * @param {FCKeditor} this
29001          * @param {Object} the FCK object.
29002          */
29003         editorinit : true
29004     });
29005     
29006     
29007 };
29008 Roo.form.FCKeditor.editors = { };
29009 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29010 {
29011     //defaultAutoCreate : {
29012     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29013     //},
29014     // private
29015     /**
29016      * @cfg {Object} fck options - see fck manual for details.
29017      */
29018     fckconfig : false,
29019     
29020     /**
29021      * @cfg {Object} fck toolbar set (Basic or Default)
29022      */
29023     toolbarSet : 'Basic',
29024     /**
29025      * @cfg {Object} fck BasePath
29026      */ 
29027     basePath : '/fckeditor/',
29028     
29029     
29030     frame : false,
29031     
29032     value : '',
29033     
29034    
29035     onRender : function(ct, position)
29036     {
29037         if(!this.el){
29038             this.defaultAutoCreate = {
29039                 tag: "textarea",
29040                 style:"width:300px;height:60px;",
29041                 autocomplete: "off"
29042             };
29043         }
29044         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29045         /*
29046         if(this.grow){
29047             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29048             if(this.preventScrollbars){
29049                 this.el.setStyle("overflow", "hidden");
29050             }
29051             this.el.setHeight(this.growMin);
29052         }
29053         */
29054         //console.log('onrender' + this.getId() );
29055         Roo.form.FCKeditor.editors[this.getId()] = this;
29056          
29057
29058         this.replaceTextarea() ;
29059         
29060     },
29061     
29062     getEditor : function() {
29063         return this.fckEditor;
29064     },
29065     /**
29066      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29067      * @param {Mixed} value The value to set
29068      */
29069     
29070     
29071     setValue : function(value)
29072     {
29073         //console.log('setValue: ' + value);
29074         
29075         if(typeof(value) == 'undefined') { // not sure why this is happending...
29076             return;
29077         }
29078         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29079         
29080         //if(!this.el || !this.getEditor()) {
29081         //    this.value = value;
29082             //this.setValue.defer(100,this,[value]);    
29083         //    return;
29084         //} 
29085         
29086         if(!this.getEditor()) {
29087             return;
29088         }
29089         
29090         this.getEditor().SetData(value);
29091         
29092         //
29093
29094     },
29095
29096     /**
29097      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29098      * @return {Mixed} value The field value
29099      */
29100     getValue : function()
29101     {
29102         
29103         if (this.frame && this.frame.dom.style.display == 'none') {
29104             return Roo.form.FCKeditor.superclass.getValue.call(this);
29105         }
29106         
29107         if(!this.el || !this.getEditor()) {
29108            
29109            // this.getValue.defer(100,this); 
29110             return this.value;
29111         }
29112        
29113         
29114         var value=this.getEditor().GetData();
29115         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29116         return Roo.form.FCKeditor.superclass.getValue.call(this);
29117         
29118
29119     },
29120
29121     /**
29122      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29123      * @return {Mixed} value The field value
29124      */
29125     getRawValue : function()
29126     {
29127         if (this.frame && this.frame.dom.style.display == 'none') {
29128             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29129         }
29130         
29131         if(!this.el || !this.getEditor()) {
29132             //this.getRawValue.defer(100,this); 
29133             return this.value;
29134             return;
29135         }
29136         
29137         
29138         
29139         var value=this.getEditor().GetData();
29140         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29141         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29142          
29143     },
29144     
29145     setSize : function(w,h) {
29146         
29147         
29148         
29149         //if (this.frame && this.frame.dom.style.display == 'none') {
29150         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29151         //    return;
29152         //}
29153         //if(!this.el || !this.getEditor()) {
29154         //    this.setSize.defer(100,this, [w,h]); 
29155         //    return;
29156         //}
29157         
29158         
29159         
29160         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29161         
29162         this.frame.dom.setAttribute('width', w);
29163         this.frame.dom.setAttribute('height', h);
29164         this.frame.setSize(w,h);
29165         
29166     },
29167     
29168     toggleSourceEdit : function(value) {
29169         
29170       
29171          
29172         this.el.dom.style.display = value ? '' : 'none';
29173         this.frame.dom.style.display = value ?  'none' : '';
29174         
29175     },
29176     
29177     
29178     focus: function(tag)
29179     {
29180         if (this.frame.dom.style.display == 'none') {
29181             return Roo.form.FCKeditor.superclass.focus.call(this);
29182         }
29183         if(!this.el || !this.getEditor()) {
29184             this.focus.defer(100,this, [tag]); 
29185             return;
29186         }
29187         
29188         
29189         
29190         
29191         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29192         this.getEditor().Focus();
29193         if (tgs.length) {
29194             if (!this.getEditor().Selection.GetSelection()) {
29195                 this.focus.defer(100,this, [tag]); 
29196                 return;
29197             }
29198             
29199             
29200             var r = this.getEditor().EditorDocument.createRange();
29201             r.setStart(tgs[0],0);
29202             r.setEnd(tgs[0],0);
29203             this.getEditor().Selection.GetSelection().removeAllRanges();
29204             this.getEditor().Selection.GetSelection().addRange(r);
29205             this.getEditor().Focus();
29206         }
29207         
29208     },
29209     
29210     
29211     
29212     replaceTextarea : function()
29213     {
29214         if ( document.getElementById( this.getId() + '___Frame' ) )
29215             return ;
29216         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29217         //{
29218             // We must check the elements firstly using the Id and then the name.
29219         var oTextarea = document.getElementById( this.getId() );
29220         
29221         var colElementsByName = document.getElementsByName( this.getId() ) ;
29222          
29223         oTextarea.style.display = 'none' ;
29224
29225         if ( oTextarea.tabIndex ) {            
29226             this.TabIndex = oTextarea.tabIndex ;
29227         }
29228         
29229         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29230         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29231         this.frame = Roo.get(this.getId() + '___Frame')
29232     },
29233     
29234     _getConfigHtml : function()
29235     {
29236         var sConfig = '' ;
29237
29238         for ( var o in this.fckconfig ) {
29239             sConfig += sConfig.length > 0  ? '&amp;' : '';
29240             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29241         }
29242
29243         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29244     },
29245     
29246     
29247     _getIFrameHtml : function()
29248     {
29249         var sFile = 'fckeditor.html' ;
29250         /* no idea what this is about..
29251         try
29252         {
29253             if ( (/fcksource=true/i).test( window.top.location.search ) )
29254                 sFile = 'fckeditor.original.html' ;
29255         }
29256         catch (e) { 
29257         */
29258
29259         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29260         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29261         
29262         
29263         var html = '<iframe id="' + this.getId() +
29264             '___Frame" src="' + sLink +
29265             '" width="' + this.width +
29266             '" height="' + this.height + '"' +
29267             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29268             ' frameborder="0" scrolling="no"></iframe>' ;
29269
29270         return html ;
29271     },
29272     
29273     _insertHtmlBefore : function( html, element )
29274     {
29275         if ( element.insertAdjacentHTML )       {
29276             // IE
29277             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29278         } else { // Gecko
29279             var oRange = document.createRange() ;
29280             oRange.setStartBefore( element ) ;
29281             var oFragment = oRange.createContextualFragment( html );
29282             element.parentNode.insertBefore( oFragment, element ) ;
29283         }
29284     }
29285     
29286     
29287   
29288     
29289     
29290     
29291     
29292
29293 });
29294
29295 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29296
29297 function FCKeditor_OnComplete(editorInstance){
29298     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29299     f.fckEditor = editorInstance;
29300     //console.log("loaded");
29301     f.fireEvent('editorinit', f, editorInstance);
29302
29303   
29304
29305  
29306
29307
29308
29309
29310
29311
29312
29313
29314
29315
29316
29317
29318
29319
29320
29321 //<script type="text/javascript">
29322 /**
29323  * @class Roo.form.GridField
29324  * @extends Roo.form.Field
29325  * Embed a grid (or editable grid into a form)
29326  * STATUS ALPHA
29327  * 
29328  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29329  * it needs 
29330  * xgrid.store = Roo.data.Store
29331  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29332  * xgrid.store.reader = Roo.data.JsonReader 
29333  * 
29334  * 
29335  * @constructor
29336  * Creates a new GridField
29337  * @param {Object} config Configuration options
29338  */
29339 Roo.form.GridField = function(config){
29340     Roo.form.GridField.superclass.constructor.call(this, config);
29341      
29342 };
29343
29344 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29345     /**
29346      * @cfg {Number} width  - used to restrict width of grid..
29347      */
29348     width : 100,
29349     /**
29350      * @cfg {Number} height - used to restrict height of grid..
29351      */
29352     height : 50,
29353      /**
29354      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29355          * 
29356          *}
29357      */
29358     xgrid : false, 
29359     /**
29360      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29361      * {tag: "input", type: "checkbox", autocomplete: "off"})
29362      */
29363    // defaultAutoCreate : { tag: 'div' },
29364     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29365     /**
29366      * @cfg {String} addTitle Text to include for adding a title.
29367      */
29368     addTitle : false,
29369     //
29370     onResize : function(){
29371         Roo.form.Field.superclass.onResize.apply(this, arguments);
29372     },
29373
29374     initEvents : function(){
29375         // Roo.form.Checkbox.superclass.initEvents.call(this);
29376         // has no events...
29377        
29378     },
29379
29380
29381     getResizeEl : function(){
29382         return this.wrap;
29383     },
29384
29385     getPositionEl : function(){
29386         return this.wrap;
29387     },
29388
29389     // private
29390     onRender : function(ct, position){
29391         
29392         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29393         var style = this.style;
29394         delete this.style;
29395         
29396         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29397         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29398         this.viewEl = this.wrap.createChild({ tag: 'div' });
29399         if (style) {
29400             this.viewEl.applyStyles(style);
29401         }
29402         if (this.width) {
29403             this.viewEl.setWidth(this.width);
29404         }
29405         if (this.height) {
29406             this.viewEl.setHeight(this.height);
29407         }
29408         //if(this.inputValue !== undefined){
29409         //this.setValue(this.value);
29410         
29411         
29412         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29413         
29414         
29415         this.grid.render();
29416         this.grid.getDataSource().on('remove', this.refreshValue, this);
29417         this.grid.getDataSource().on('update', this.refreshValue, this);
29418         this.grid.on('afteredit', this.refreshValue, this);
29419  
29420     },
29421      
29422     
29423     /**
29424      * Sets the value of the item. 
29425      * @param {String} either an object  or a string..
29426      */
29427     setValue : function(v){
29428         //this.value = v;
29429         v = v || []; // empty set..
29430         // this does not seem smart - it really only affects memoryproxy grids..
29431         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29432             var ds = this.grid.getDataSource();
29433             // assumes a json reader..
29434             var data = {}
29435             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29436             ds.loadData( data);
29437         }
29438         // clear selection so it does not get stale.
29439         if (this.grid.sm) { 
29440             this.grid.sm.clearSelections();
29441         }
29442         
29443         Roo.form.GridField.superclass.setValue.call(this, v);
29444         this.refreshValue();
29445         // should load data in the grid really....
29446     },
29447     
29448     // private
29449     refreshValue: function() {
29450          var val = [];
29451         this.grid.getDataSource().each(function(r) {
29452             val.push(r.data);
29453         });
29454         this.el.dom.value = Roo.encode(val);
29455     }
29456     
29457      
29458     
29459     
29460 });/*
29461  * Based on:
29462  * Ext JS Library 1.1.1
29463  * Copyright(c) 2006-2007, Ext JS, LLC.
29464  *
29465  * Originally Released Under LGPL - original licence link has changed is not relivant.
29466  *
29467  * Fork - LGPL
29468  * <script type="text/javascript">
29469  */
29470 /**
29471  * @class Roo.form.DisplayField
29472  * @extends Roo.form.Field
29473  * A generic Field to display non-editable data.
29474  * @constructor
29475  * Creates a new Display Field item.
29476  * @param {Object} config Configuration options
29477  */
29478 Roo.form.DisplayField = function(config){
29479     Roo.form.DisplayField.superclass.constructor.call(this, config);
29480     
29481 };
29482
29483 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29484     inputType:      'hidden',
29485     allowBlank:     true,
29486     readOnly:         true,
29487     
29488  
29489     /**
29490      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29491      */
29492     focusClass : undefined,
29493     /**
29494      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29495      */
29496     fieldClass: 'x-form-field',
29497     
29498      /**
29499      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29500      */
29501     valueRenderer: undefined,
29502     
29503     width: 100,
29504     /**
29505      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29506      * {tag: "input", type: "checkbox", autocomplete: "off"})
29507      */
29508      
29509  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29510
29511     onResize : function(){
29512         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29513         
29514     },
29515
29516     initEvents : function(){
29517         // Roo.form.Checkbox.superclass.initEvents.call(this);
29518         // has no events...
29519        
29520     },
29521
29522
29523     getResizeEl : function(){
29524         return this.wrap;
29525     },
29526
29527     getPositionEl : function(){
29528         return this.wrap;
29529     },
29530
29531     // private
29532     onRender : function(ct, position){
29533         
29534         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29535         //if(this.inputValue !== undefined){
29536         this.wrap = this.el.wrap();
29537         
29538         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29539         
29540         if (this.bodyStyle) {
29541             this.viewEl.applyStyles(this.bodyStyle);
29542         }
29543         //this.viewEl.setStyle('padding', '2px');
29544         
29545         this.setValue(this.value);
29546         
29547     },
29548 /*
29549     // private
29550     initValue : Roo.emptyFn,
29551
29552   */
29553
29554         // private
29555     onClick : function(){
29556         
29557     },
29558
29559     /**
29560      * Sets the checked state of the checkbox.
29561      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29562      */
29563     setValue : function(v){
29564         this.value = v;
29565         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29566         // this might be called before we have a dom element..
29567         if (!this.viewEl) {
29568             return;
29569         }
29570         this.viewEl.dom.innerHTML = html;
29571         Roo.form.DisplayField.superclass.setValue.call(this, v);
29572
29573     }
29574 });/*
29575  * 
29576  * Licence- LGPL
29577  * 
29578  */
29579
29580 /**
29581  * @class Roo.form.DayPicker
29582  * @extends Roo.form.Field
29583  * A Day picker show [M] [T] [W] ....
29584  * @constructor
29585  * Creates a new Day Picker
29586  * @param {Object} config Configuration options
29587  */
29588 Roo.form.DayPicker= function(config){
29589     Roo.form.DayPicker.superclass.constructor.call(this, config);
29590      
29591 };
29592
29593 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29594     /**
29595      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29596      */
29597     focusClass : undefined,
29598     /**
29599      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29600      */
29601     fieldClass: "x-form-field",
29602    
29603     /**
29604      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29605      * {tag: "input", type: "checkbox", autocomplete: "off"})
29606      */
29607     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29608     
29609    
29610     actionMode : 'viewEl', 
29611     //
29612     // private
29613  
29614     inputType : 'hidden',
29615     
29616      
29617     inputElement: false, // real input element?
29618     basedOn: false, // ????
29619     
29620     isFormField: true, // not sure where this is needed!!!!
29621
29622     onResize : function(){
29623         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29624         if(!this.boxLabel){
29625             this.el.alignTo(this.wrap, 'c-c');
29626         }
29627     },
29628
29629     initEvents : function(){
29630         Roo.form.Checkbox.superclass.initEvents.call(this);
29631         this.el.on("click", this.onClick,  this);
29632         this.el.on("change", this.onClick,  this);
29633     },
29634
29635
29636     getResizeEl : function(){
29637         return this.wrap;
29638     },
29639
29640     getPositionEl : function(){
29641         return this.wrap;
29642     },
29643
29644     
29645     // private
29646     onRender : function(ct, position){
29647         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29648        
29649         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29650         
29651         var r1 = '<table><tr>';
29652         var r2 = '<tr class="x-form-daypick-icons">';
29653         for (var i=0; i < 7; i++) {
29654             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29655             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29656         }
29657         
29658         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29659         viewEl.select('img').on('click', this.onClick, this);
29660         this.viewEl = viewEl;   
29661         
29662         
29663         // this will not work on Chrome!!!
29664         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29665         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29666         
29667         
29668           
29669
29670     },
29671
29672     // private
29673     initValue : Roo.emptyFn,
29674
29675     /**
29676      * Returns the checked state of the checkbox.
29677      * @return {Boolean} True if checked, else false
29678      */
29679     getValue : function(){
29680         return this.el.dom.value;
29681         
29682     },
29683
29684         // private
29685     onClick : function(e){ 
29686         //this.setChecked(!this.checked);
29687         Roo.get(e.target).toggleClass('x-menu-item-checked');
29688         this.refreshValue();
29689         //if(this.el.dom.checked != this.checked){
29690         //    this.setValue(this.el.dom.checked);
29691        // }
29692     },
29693     
29694     // private
29695     refreshValue : function()
29696     {
29697         var val = '';
29698         this.viewEl.select('img',true).each(function(e,i,n)  {
29699             val += e.is(".x-menu-item-checked") ? String(n) : '';
29700         });
29701         this.setValue(val, true);
29702     },
29703
29704     /**
29705      * Sets the checked state of the checkbox.
29706      * On is always based on a string comparison between inputValue and the param.
29707      * @param {Boolean/String} value - the value to set 
29708      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29709      */
29710     setValue : function(v,suppressEvent){
29711         if (!this.el.dom) {
29712             return;
29713         }
29714         var old = this.el.dom.value ;
29715         this.el.dom.value = v;
29716         if (suppressEvent) {
29717             return ;
29718         }
29719          
29720         // update display..
29721         this.viewEl.select('img',true).each(function(e,i,n)  {
29722             
29723             var on = e.is(".x-menu-item-checked");
29724             var newv = v.indexOf(String(n)) > -1;
29725             if (on != newv) {
29726                 e.toggleClass('x-menu-item-checked');
29727             }
29728             
29729         });
29730         
29731         
29732         this.fireEvent('change', this, v, old);
29733         
29734         
29735     },
29736    
29737     // handle setting of hidden value by some other method!!?!?
29738     setFromHidden: function()
29739     {
29740         if(!this.el){
29741             return;
29742         }
29743         //console.log("SET FROM HIDDEN");
29744         //alert('setFrom hidden');
29745         this.setValue(this.el.dom.value);
29746     },
29747     
29748     onDestroy : function()
29749     {
29750         if(this.viewEl){
29751             Roo.get(this.viewEl).remove();
29752         }
29753          
29754         Roo.form.DayPicker.superclass.onDestroy.call(this);
29755     }
29756
29757 });/*
29758  * RooJS Library 1.1.1
29759  * Copyright(c) 2008-2011  Alan Knowles
29760  *
29761  * License - LGPL
29762  */
29763  
29764
29765 /**
29766  * @class Roo.form.ComboCheck
29767  * @extends Roo.form.ComboBox
29768  * A combobox for multiple select items.
29769  *
29770  * FIXME - could do with a reset button..
29771  * 
29772  * @constructor
29773  * Create a new ComboCheck
29774  * @param {Object} config Configuration options
29775  */
29776 Roo.form.ComboCheck = function(config){
29777     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29778     // should verify some data...
29779     // like
29780     // hiddenName = required..
29781     // displayField = required
29782     // valudField == required
29783     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29784     var _t = this;
29785     Roo.each(req, function(e) {
29786         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29787             throw "Roo.form.ComboCheck : missing value for: " + e;
29788         }
29789     });
29790     
29791     
29792 };
29793
29794 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29795      
29796      
29797     editable : false,
29798      
29799     selectedClass: 'x-menu-item-checked', 
29800     
29801     // private
29802     onRender : function(ct, position){
29803         var _t = this;
29804         
29805         
29806         
29807         if(!this.tpl){
29808             var cls = 'x-combo-list';
29809
29810             
29811             this.tpl =  new Roo.Template({
29812                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29813                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29814                    '<span>{' + this.displayField + '}</span>' +
29815                     '</div>' 
29816                 
29817             });
29818         }
29819  
29820         
29821         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29822         this.view.singleSelect = false;
29823         this.view.multiSelect = true;
29824         this.view.toggleSelect = true;
29825         this.pageTb.add(new Roo.Toolbar.Fill(), {
29826             
29827             text: 'Done',
29828             handler: function()
29829             {
29830                 _t.collapse();
29831             }
29832         });
29833     },
29834     
29835     onViewOver : function(e, t){
29836         // do nothing...
29837         return;
29838         
29839     },
29840     
29841     onViewClick : function(doFocus,index){
29842         return;
29843         
29844     },
29845     select: function () {
29846         //Roo.log("SELECT CALLED");
29847     },
29848      
29849     selectByValue : function(xv, scrollIntoView){
29850         var ar = this.getValueArray();
29851         var sels = [];
29852         
29853         Roo.each(ar, function(v) {
29854             if(v === undefined || v === null){
29855                 return;
29856             }
29857             var r = this.findRecord(this.valueField, v);
29858             if(r){
29859                 sels.push(this.store.indexOf(r))
29860                 
29861             }
29862         },this);
29863         this.view.select(sels);
29864         return false;
29865     },
29866     
29867     
29868     
29869     onSelect : function(record, index){
29870        // Roo.log("onselect Called");
29871        // this is only called by the clear button now..
29872         this.view.clearSelections();
29873         this.setValue('[]');
29874         if (this.value != this.valueBefore) {
29875             this.fireEvent('change', this, this.value, this.valueBefore);
29876         }
29877     },
29878     getValueArray : function()
29879     {
29880         var ar = [] ;
29881         
29882         try {
29883             //Roo.log(this.value);
29884             if (typeof(this.value) == 'undefined') {
29885                 return [];
29886             }
29887             var ar = Roo.decode(this.value);
29888             return  ar instanceof Array ? ar : []; //?? valid?
29889             
29890         } catch(e) {
29891             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29892             return [];
29893         }
29894          
29895     },
29896     expand : function ()
29897     {
29898         Roo.form.ComboCheck.superclass.expand.call(this);
29899         this.valueBefore = this.value;
29900         
29901
29902     },
29903     
29904     collapse : function(){
29905         Roo.form.ComboCheck.superclass.collapse.call(this);
29906         var sl = this.view.getSelectedIndexes();
29907         var st = this.store;
29908         var nv = [];
29909         var tv = [];
29910         var r;
29911         Roo.each(sl, function(i) {
29912             r = st.getAt(i);
29913             nv.push(r.get(this.valueField));
29914         },this);
29915         this.setValue(Roo.encode(nv));
29916         if (this.value != this.valueBefore) {
29917
29918             this.fireEvent('change', this, this.value, this.valueBefore);
29919         }
29920         
29921     },
29922     
29923     setValue : function(v){
29924         // Roo.log(v);
29925         this.value = v;
29926         
29927         var vals = this.getValueArray();
29928         var tv = [];
29929         Roo.each(vals, function(k) {
29930             var r = this.findRecord(this.valueField, k);
29931             if(r){
29932                 tv.push(r.data[this.displayField]);
29933             }else if(this.valueNotFoundText !== undefined){
29934                 tv.push( this.valueNotFoundText );
29935             }
29936         },this);
29937        // Roo.log(tv);
29938         
29939         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29940         this.hiddenField.value = v;
29941         this.value = v;
29942     }
29943     
29944 });//<script type="text/javasscript">
29945  
29946
29947 /**
29948  * @class Roo.DDView
29949  * A DnD enabled version of Roo.View.
29950  * @param {Element/String} container The Element in which to create the View.
29951  * @param {String} tpl The template string used to create the markup for each element of the View
29952  * @param {Object} config The configuration properties. These include all the config options of
29953  * {@link Roo.View} plus some specific to this class.<br>
29954  * <p>
29955  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29956  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29957  * <p>
29958  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29959 .x-view-drag-insert-above {
29960         border-top:1px dotted #3366cc;
29961 }
29962 .x-view-drag-insert-below {
29963         border-bottom:1px dotted #3366cc;
29964 }
29965 </code></pre>
29966  * 
29967  */
29968  
29969 Roo.DDView = function(container, tpl, config) {
29970     Roo.DDView.superclass.constructor.apply(this, arguments);
29971     this.getEl().setStyle("outline", "0px none");
29972     this.getEl().unselectable();
29973     if (this.dragGroup) {
29974                 this.setDraggable(this.dragGroup.split(","));
29975     }
29976     if (this.dropGroup) {
29977                 this.setDroppable(this.dropGroup.split(","));
29978     }
29979     if (this.deletable) {
29980         this.setDeletable();
29981     }
29982     this.isDirtyFlag = false;
29983         this.addEvents({
29984                 "drop" : true
29985         });
29986 };
29987
29988 Roo.extend(Roo.DDView, Roo.View, {
29989 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29990 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
29991 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
29992 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
29993
29994         isFormField: true,
29995
29996         reset: Roo.emptyFn,
29997         
29998         clearInvalid: Roo.form.Field.prototype.clearInvalid,
29999
30000         validate: function() {
30001                 return true;
30002         },
30003         
30004         destroy: function() {
30005                 this.purgeListeners();
30006                 this.getEl.removeAllListeners();
30007                 this.getEl().remove();
30008                 if (this.dragZone) {
30009                         if (this.dragZone.destroy) {
30010                                 this.dragZone.destroy();
30011                         }
30012                 }
30013                 if (this.dropZone) {
30014                         if (this.dropZone.destroy) {
30015                                 this.dropZone.destroy();
30016                         }
30017                 }
30018         },
30019
30020 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30021         getName: function() {
30022                 return this.name;
30023         },
30024
30025 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30026         setValue: function(v) {
30027                 if (!this.store) {
30028                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30029                 }
30030                 var data = {};
30031                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30032                 this.store.proxy = new Roo.data.MemoryProxy(data);
30033                 this.store.load();
30034         },
30035
30036 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30037         getValue: function() {
30038                 var result = '(';
30039                 this.store.each(function(rec) {
30040                         result += rec.id + ',';
30041                 });
30042                 return result.substr(0, result.length - 1) + ')';
30043         },
30044         
30045         getIds: function() {
30046                 var i = 0, result = new Array(this.store.getCount());
30047                 this.store.each(function(rec) {
30048                         result[i++] = rec.id;
30049                 });
30050                 return result;
30051         },
30052         
30053         isDirty: function() {
30054                 return this.isDirtyFlag;
30055         },
30056
30057 /**
30058  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30059  *      whole Element becomes the target, and this causes the drop gesture to append.
30060  */
30061     getTargetFromEvent : function(e) {
30062                 var target = e.getTarget();
30063                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30064                 target = target.parentNode;
30065                 }
30066                 if (!target) {
30067                         target = this.el.dom.lastChild || this.el.dom;
30068                 }
30069                 return target;
30070     },
30071
30072 /**
30073  *      Create the drag data which consists of an object which has the property "ddel" as
30074  *      the drag proxy element. 
30075  */
30076     getDragData : function(e) {
30077         var target = this.findItemFromChild(e.getTarget());
30078                 if(target) {
30079                         this.handleSelection(e);
30080                         var selNodes = this.getSelectedNodes();
30081             var dragData = {
30082                 source: this,
30083                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30084                 nodes: selNodes,
30085                 records: []
30086                         };
30087                         var selectedIndices = this.getSelectedIndexes();
30088                         for (var i = 0; i < selectedIndices.length; i++) {
30089                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30090                         }
30091                         if (selNodes.length == 1) {
30092                                 dragData.ddel = target.cloneNode(true); // the div element
30093                         } else {
30094                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30095                                 div.className = 'multi-proxy';
30096                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30097                                         div.appendChild(selNodes[i].cloneNode(true));
30098                                 }
30099                                 dragData.ddel = div;
30100                         }
30101             //console.log(dragData)
30102             //console.log(dragData.ddel.innerHTML)
30103                         return dragData;
30104                 }
30105         //console.log('nodragData')
30106                 return false;
30107     },
30108     
30109 /**     Specify to which ddGroup items in this DDView may be dragged. */
30110     setDraggable: function(ddGroup) {
30111         if (ddGroup instanceof Array) {
30112                 Roo.each(ddGroup, this.setDraggable, this);
30113                 return;
30114         }
30115         if (this.dragZone) {
30116                 this.dragZone.addToGroup(ddGroup);
30117         } else {
30118                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30119                                 containerScroll: true,
30120                                 ddGroup: ddGroup 
30121
30122                         });
30123 //                      Draggability implies selection. DragZone's mousedown selects the element.
30124                         if (!this.multiSelect) { this.singleSelect = true; }
30125
30126 //                      Wire the DragZone's handlers up to methods in *this*
30127                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30128                 }
30129     },
30130
30131 /**     Specify from which ddGroup this DDView accepts drops. */
30132     setDroppable: function(ddGroup) {
30133         if (ddGroup instanceof Array) {
30134                 Roo.each(ddGroup, this.setDroppable, this);
30135                 return;
30136         }
30137         if (this.dropZone) {
30138                 this.dropZone.addToGroup(ddGroup);
30139         } else {
30140                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30141                                 containerScroll: true,
30142                                 ddGroup: ddGroup
30143                         });
30144
30145 //                      Wire the DropZone's handlers up to methods in *this*
30146                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30147                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30148                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30149                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30150                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30151                 }
30152     },
30153
30154 /**     Decide whether to drop above or below a View node. */
30155     getDropPoint : function(e, n, dd){
30156         if (n == this.el.dom) { return "above"; }
30157                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30158                 var c = t + (b - t) / 2;
30159                 var y = Roo.lib.Event.getPageY(e);
30160                 if(y <= c) {
30161                         return "above";
30162                 }else{
30163                         return "below";
30164                 }
30165     },
30166
30167     onNodeEnter : function(n, dd, e, data){
30168                 return false;
30169     },
30170     
30171     onNodeOver : function(n, dd, e, data){
30172                 var pt = this.getDropPoint(e, n, dd);
30173                 // set the insert point style on the target node
30174                 var dragElClass = this.dropNotAllowed;
30175                 if (pt) {
30176                         var targetElClass;
30177                         if (pt == "above"){
30178                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30179                                 targetElClass = "x-view-drag-insert-above";
30180                         } else {
30181                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30182                                 targetElClass = "x-view-drag-insert-below";
30183                         }
30184                         if (this.lastInsertClass != targetElClass){
30185                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30186                                 this.lastInsertClass = targetElClass;
30187                         }
30188                 }
30189                 return dragElClass;
30190         },
30191
30192     onNodeOut : function(n, dd, e, data){
30193                 this.removeDropIndicators(n);
30194     },
30195
30196     onNodeDrop : function(n, dd, e, data){
30197         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30198                 return false;
30199         }
30200         var pt = this.getDropPoint(e, n, dd);
30201                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30202                 if (pt == "below") { insertAt++; }
30203                 for (var i = 0; i < data.records.length; i++) {
30204                         var r = data.records[i];
30205                         var dup = this.store.getById(r.id);
30206                         if (dup && (dd != this.dragZone)) {
30207                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30208                         } else {
30209                                 if (data.copy) {
30210                                         this.store.insert(insertAt++, r.copy());
30211                                 } else {
30212                                         data.source.isDirtyFlag = true;
30213                                         r.store.remove(r);
30214                                         this.store.insert(insertAt++, r);
30215                                 }
30216                                 this.isDirtyFlag = true;
30217                         }
30218                 }
30219                 this.dragZone.cachedTarget = null;
30220                 return true;
30221     },
30222
30223     removeDropIndicators : function(n){
30224                 if(n){
30225                         Roo.fly(n).removeClass([
30226                                 "x-view-drag-insert-above",
30227                                 "x-view-drag-insert-below"]);
30228                         this.lastInsertClass = "_noclass";
30229                 }
30230     },
30231
30232 /**
30233  *      Utility method. Add a delete option to the DDView's context menu.
30234  *      @param {String} imageUrl The URL of the "delete" icon image.
30235  */
30236         setDeletable: function(imageUrl) {
30237                 if (!this.singleSelect && !this.multiSelect) {
30238                         this.singleSelect = true;
30239                 }
30240                 var c = this.getContextMenu();
30241                 this.contextMenu.on("itemclick", function(item) {
30242                         switch (item.id) {
30243                                 case "delete":
30244                                         this.remove(this.getSelectedIndexes());
30245                                         break;
30246                         }
30247                 }, this);
30248                 this.contextMenu.add({
30249                         icon: imageUrl,
30250                         id: "delete",
30251                         text: 'Delete'
30252                 });
30253         },
30254         
30255 /**     Return the context menu for this DDView. */
30256         getContextMenu: function() {
30257                 if (!this.contextMenu) {
30258 //                      Create the View's context menu
30259                         this.contextMenu = new Roo.menu.Menu({
30260                                 id: this.id + "-contextmenu"
30261                         });
30262                         this.el.on("contextmenu", this.showContextMenu, this);
30263                 }
30264                 return this.contextMenu;
30265         },
30266         
30267         disableContextMenu: function() {
30268                 if (this.contextMenu) {
30269                         this.el.un("contextmenu", this.showContextMenu, this);
30270                 }
30271         },
30272
30273         showContextMenu: function(e, item) {
30274         item = this.findItemFromChild(e.getTarget());
30275                 if (item) {
30276                         e.stopEvent();
30277                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30278                         this.contextMenu.showAt(e.getXY());
30279             }
30280     },
30281
30282 /**
30283  *      Remove {@link Roo.data.Record}s at the specified indices.
30284  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30285  */
30286     remove: function(selectedIndices) {
30287                 selectedIndices = [].concat(selectedIndices);
30288                 for (var i = 0; i < selectedIndices.length; i++) {
30289                         var rec = this.store.getAt(selectedIndices[i]);
30290                         this.store.remove(rec);
30291                 }
30292     },
30293
30294 /**
30295  *      Double click fires the event, but also, if this is draggable, and there is only one other
30296  *      related DropZone, it transfers the selected node.
30297  */
30298     onDblClick : function(e){
30299         var item = this.findItemFromChild(e.getTarget());
30300         if(item){
30301             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30302                 return false;
30303             }
30304             if (this.dragGroup) {
30305                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30306                     while (targets.indexOf(this.dropZone) > -1) {
30307                             targets.remove(this.dropZone);
30308                                 }
30309                     if (targets.length == 1) {
30310                                         this.dragZone.cachedTarget = null;
30311                         var el = Roo.get(targets[0].getEl());
30312                         var box = el.getBox(true);
30313                         targets[0].onNodeDrop(el.dom, {
30314                                 target: el.dom,
30315                                 xy: [box.x, box.y + box.height - 1]
30316                         }, null, this.getDragData(e));
30317                     }
30318                 }
30319         }
30320     },
30321     
30322     handleSelection: function(e) {
30323                 this.dragZone.cachedTarget = null;
30324         var item = this.findItemFromChild(e.getTarget());
30325         if (!item) {
30326                 this.clearSelections(true);
30327                 return;
30328         }
30329                 if (item && (this.multiSelect || this.singleSelect)){
30330                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30331                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30332                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30333                                 this.unselect(item);
30334                         } else {
30335                                 this.select(item, this.multiSelect && e.ctrlKey);
30336                                 this.lastSelection = item;
30337                         }
30338                 }
30339     },
30340
30341     onItemClick : function(item, index, e){
30342                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30343                         return false;
30344                 }
30345                 return true;
30346     },
30347
30348     unselect : function(nodeInfo, suppressEvent){
30349                 var node = this.getNode(nodeInfo);
30350                 if(node && this.isSelected(node)){
30351                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30352                                 Roo.fly(node).removeClass(this.selectedClass);
30353                                 this.selections.remove(node);
30354                                 if(!suppressEvent){
30355                                         this.fireEvent("selectionchange", this, this.selections);
30356                                 }
30357                         }
30358                 }
30359     }
30360 });
30361 /*
30362  * Based on:
30363  * Ext JS Library 1.1.1
30364  * Copyright(c) 2006-2007, Ext JS, LLC.
30365  *
30366  * Originally Released Under LGPL - original licence link has changed is not relivant.
30367  *
30368  * Fork - LGPL
30369  * <script type="text/javascript">
30370  */
30371  
30372 /**
30373  * @class Roo.LayoutManager
30374  * @extends Roo.util.Observable
30375  * Base class for layout managers.
30376  */
30377 Roo.LayoutManager = function(container, config){
30378     Roo.LayoutManager.superclass.constructor.call(this);
30379     this.el = Roo.get(container);
30380     // ie scrollbar fix
30381     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30382         document.body.scroll = "no";
30383     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30384         this.el.position('relative');
30385     }
30386     this.id = this.el.id;
30387     this.el.addClass("x-layout-container");
30388     /** false to disable window resize monitoring @type Boolean */
30389     this.monitorWindowResize = true;
30390     this.regions = {};
30391     this.addEvents({
30392         /**
30393          * @event layout
30394          * Fires when a layout is performed. 
30395          * @param {Roo.LayoutManager} this
30396          */
30397         "layout" : true,
30398         /**
30399          * @event regionresized
30400          * Fires when the user resizes a region. 
30401          * @param {Roo.LayoutRegion} region The resized region
30402          * @param {Number} newSize The new size (width for east/west, height for north/south)
30403          */
30404         "regionresized" : true,
30405         /**
30406          * @event regioncollapsed
30407          * Fires when a region is collapsed. 
30408          * @param {Roo.LayoutRegion} region The collapsed region
30409          */
30410         "regioncollapsed" : true,
30411         /**
30412          * @event regionexpanded
30413          * Fires when a region is expanded.  
30414          * @param {Roo.LayoutRegion} region The expanded region
30415          */
30416         "regionexpanded" : true
30417     });
30418     this.updating = false;
30419     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30420 };
30421
30422 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30423     /**
30424      * Returns true if this layout is currently being updated
30425      * @return {Boolean}
30426      */
30427     isUpdating : function(){
30428         return this.updating; 
30429     },
30430     
30431     /**
30432      * Suspend the LayoutManager from doing auto-layouts while
30433      * making multiple add or remove calls
30434      */
30435     beginUpdate : function(){
30436         this.updating = true;    
30437     },
30438     
30439     /**
30440      * Restore auto-layouts and optionally disable the manager from performing a layout
30441      * @param {Boolean} noLayout true to disable a layout update 
30442      */
30443     endUpdate : function(noLayout){
30444         this.updating = false;
30445         if(!noLayout){
30446             this.layout();
30447         }    
30448     },
30449     
30450     layout: function(){
30451         
30452     },
30453     
30454     onRegionResized : function(region, newSize){
30455         this.fireEvent("regionresized", region, newSize);
30456         this.layout();
30457     },
30458     
30459     onRegionCollapsed : function(region){
30460         this.fireEvent("regioncollapsed", region);
30461     },
30462     
30463     onRegionExpanded : function(region){
30464         this.fireEvent("regionexpanded", region);
30465     },
30466         
30467     /**
30468      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30469      * performs box-model adjustments.
30470      * @return {Object} The size as an object {width: (the width), height: (the height)}
30471      */
30472     getViewSize : function(){
30473         var size;
30474         if(this.el.dom != document.body){
30475             size = this.el.getSize();
30476         }else{
30477             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30478         }
30479         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30480         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30481         return size;
30482     },
30483     
30484     /**
30485      * Returns the Element this layout is bound to.
30486      * @return {Roo.Element}
30487      */
30488     getEl : function(){
30489         return this.el;
30490     },
30491     
30492     /**
30493      * Returns the specified region.
30494      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30495      * @return {Roo.LayoutRegion}
30496      */
30497     getRegion : function(target){
30498         return this.regions[target.toLowerCase()];
30499     },
30500     
30501     onWindowResize : function(){
30502         if(this.monitorWindowResize){
30503             this.layout();
30504         }
30505     }
30506 });/*
30507  * Based on:
30508  * Ext JS Library 1.1.1
30509  * Copyright(c) 2006-2007, Ext JS, LLC.
30510  *
30511  * Originally Released Under LGPL - original licence link has changed is not relivant.
30512  *
30513  * Fork - LGPL
30514  * <script type="text/javascript">
30515  */
30516 /**
30517  * @class Roo.BorderLayout
30518  * @extends Roo.LayoutManager
30519  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30520  * please see: <br><br>
30521  * <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>
30522  * <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>
30523  * Example:
30524  <pre><code>
30525  var layout = new Roo.BorderLayout(document.body, {
30526     north: {
30527         initialSize: 25,
30528         titlebar: false
30529     },
30530     west: {
30531         split:true,
30532         initialSize: 200,
30533         minSize: 175,
30534         maxSize: 400,
30535         titlebar: true,
30536         collapsible: true
30537     },
30538     east: {
30539         split:true,
30540         initialSize: 202,
30541         minSize: 175,
30542         maxSize: 400,
30543         titlebar: true,
30544         collapsible: true
30545     },
30546     south: {
30547         split:true,
30548         initialSize: 100,
30549         minSize: 100,
30550         maxSize: 200,
30551         titlebar: true,
30552         collapsible: true
30553     },
30554     center: {
30555         titlebar: true,
30556         autoScroll:true,
30557         resizeTabs: true,
30558         minTabWidth: 50,
30559         preferredTabWidth: 150
30560     }
30561 });
30562
30563 // shorthand
30564 var CP = Roo.ContentPanel;
30565
30566 layout.beginUpdate();
30567 layout.add("north", new CP("north", "North"));
30568 layout.add("south", new CP("south", {title: "South", closable: true}));
30569 layout.add("west", new CP("west", {title: "West"}));
30570 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30571 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30572 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30573 layout.getRegion("center").showPanel("center1");
30574 layout.endUpdate();
30575 </code></pre>
30576
30577 <b>The container the layout is rendered into can be either the body element or any other element.
30578 If it is not the body element, the container needs to either be an absolute positioned element,
30579 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30580 the container size if it is not the body element.</b>
30581
30582 * @constructor
30583 * Create a new BorderLayout
30584 * @param {String/HTMLElement/Element} container The container this layout is bound to
30585 * @param {Object} config Configuration options
30586  */
30587 Roo.BorderLayout = function(container, config){
30588     config = config || {};
30589     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30590     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30591     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30592         var target = this.factory.validRegions[i];
30593         if(config[target]){
30594             this.addRegion(target, config[target]);
30595         }
30596     }
30597 };
30598
30599 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30600     /**
30601      * Creates and adds a new region if it doesn't already exist.
30602      * @param {String} target The target region key (north, south, east, west or center).
30603      * @param {Object} config The regions config object
30604      * @return {BorderLayoutRegion} The new region
30605      */
30606     addRegion : function(target, config){
30607         if(!this.regions[target]){
30608             var r = this.factory.create(target, this, config);
30609             this.bindRegion(target, r);
30610         }
30611         return this.regions[target];
30612     },
30613
30614     // private (kinda)
30615     bindRegion : function(name, r){
30616         this.regions[name] = r;
30617         r.on("visibilitychange", this.layout, this);
30618         r.on("paneladded", this.layout, this);
30619         r.on("panelremoved", this.layout, this);
30620         r.on("invalidated", this.layout, this);
30621         r.on("resized", this.onRegionResized, this);
30622         r.on("collapsed", this.onRegionCollapsed, this);
30623         r.on("expanded", this.onRegionExpanded, this);
30624     },
30625
30626     /**
30627      * Performs a layout update.
30628      */
30629     layout : function(){
30630         if(this.updating) return;
30631         var size = this.getViewSize();
30632         var w = size.width;
30633         var h = size.height;
30634         var centerW = w;
30635         var centerH = h;
30636         var centerY = 0;
30637         var centerX = 0;
30638         //var x = 0, y = 0;
30639
30640         var rs = this.regions;
30641         var north = rs["north"];
30642         var south = rs["south"]; 
30643         var west = rs["west"];
30644         var east = rs["east"];
30645         var center = rs["center"];
30646         //if(this.hideOnLayout){ // not supported anymore
30647             //c.el.setStyle("display", "none");
30648         //}
30649         if(north && north.isVisible()){
30650             var b = north.getBox();
30651             var m = north.getMargins();
30652             b.width = w - (m.left+m.right);
30653             b.x = m.left;
30654             b.y = m.top;
30655             centerY = b.height + b.y + m.bottom;
30656             centerH -= centerY;
30657             north.updateBox(this.safeBox(b));
30658         }
30659         if(south && south.isVisible()){
30660             var b = south.getBox();
30661             var m = south.getMargins();
30662             b.width = w - (m.left+m.right);
30663             b.x = m.left;
30664             var totalHeight = (b.height + m.top + m.bottom);
30665             b.y = h - totalHeight + m.top;
30666             centerH -= totalHeight;
30667             south.updateBox(this.safeBox(b));
30668         }
30669         if(west && west.isVisible()){
30670             var b = west.getBox();
30671             var m = west.getMargins();
30672             b.height = centerH - (m.top+m.bottom);
30673             b.x = m.left;
30674             b.y = centerY + m.top;
30675             var totalWidth = (b.width + m.left + m.right);
30676             centerX += totalWidth;
30677             centerW -= totalWidth;
30678             west.updateBox(this.safeBox(b));
30679         }
30680         if(east && east.isVisible()){
30681             var b = east.getBox();
30682             var m = east.getMargins();
30683             b.height = centerH - (m.top+m.bottom);
30684             var totalWidth = (b.width + m.left + m.right);
30685             b.x = w - totalWidth + m.left;
30686             b.y = centerY + m.top;
30687             centerW -= totalWidth;
30688             east.updateBox(this.safeBox(b));
30689         }
30690         if(center){
30691             var m = center.getMargins();
30692             var centerBox = {
30693                 x: centerX + m.left,
30694                 y: centerY + m.top,
30695                 width: centerW - (m.left+m.right),
30696                 height: centerH - (m.top+m.bottom)
30697             };
30698             //if(this.hideOnLayout){
30699                 //center.el.setStyle("display", "block");
30700             //}
30701             center.updateBox(this.safeBox(centerBox));
30702         }
30703         this.el.repaint();
30704         this.fireEvent("layout", this);
30705     },
30706
30707     // private
30708     safeBox : function(box){
30709         box.width = Math.max(0, box.width);
30710         box.height = Math.max(0, box.height);
30711         return box;
30712     },
30713
30714     /**
30715      * Adds a ContentPanel (or subclass) to this layout.
30716      * @param {String} target The target region key (north, south, east, west or center).
30717      * @param {Roo.ContentPanel} panel The panel to add
30718      * @return {Roo.ContentPanel} The added panel
30719      */
30720     add : function(target, panel){
30721          
30722         target = target.toLowerCase();
30723         return this.regions[target].add(panel);
30724     },
30725
30726     /**
30727      * Remove a ContentPanel (or subclass) to this layout.
30728      * @param {String} target The target region key (north, south, east, west or center).
30729      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30730      * @return {Roo.ContentPanel} The removed panel
30731      */
30732     remove : function(target, panel){
30733         target = target.toLowerCase();
30734         return this.regions[target].remove(panel);
30735     },
30736
30737     /**
30738      * Searches all regions for a panel with the specified id
30739      * @param {String} panelId
30740      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30741      */
30742     findPanel : function(panelId){
30743         var rs = this.regions;
30744         for(var target in rs){
30745             if(typeof rs[target] != "function"){
30746                 var p = rs[target].getPanel(panelId);
30747                 if(p){
30748                     return p;
30749                 }
30750             }
30751         }
30752         return null;
30753     },
30754
30755     /**
30756      * Searches all regions for a panel with the specified id and activates (shows) it.
30757      * @param {String/ContentPanel} panelId The panels id or the panel itself
30758      * @return {Roo.ContentPanel} The shown panel or null
30759      */
30760     showPanel : function(panelId) {
30761       var rs = this.regions;
30762       for(var target in rs){
30763          var r = rs[target];
30764          if(typeof r != "function"){
30765             if(r.hasPanel(panelId)){
30766                return r.showPanel(panelId);
30767             }
30768          }
30769       }
30770       return null;
30771    },
30772
30773    /**
30774      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30775      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30776      */
30777     restoreState : function(provider){
30778         if(!provider){
30779             provider = Roo.state.Manager;
30780         }
30781         var sm = new Roo.LayoutStateManager();
30782         sm.init(this, provider);
30783     },
30784
30785     /**
30786      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30787      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30788      * a valid ContentPanel config object.  Example:
30789      * <pre><code>
30790 // Create the main layout
30791 var layout = new Roo.BorderLayout('main-ct', {
30792     west: {
30793         split:true,
30794         minSize: 175,
30795         titlebar: true
30796     },
30797     center: {
30798         title:'Components'
30799     }
30800 }, 'main-ct');
30801
30802 // Create and add multiple ContentPanels at once via configs
30803 layout.batchAdd({
30804    west: {
30805        id: 'source-files',
30806        autoCreate:true,
30807        title:'Ext Source Files',
30808        autoScroll:true,
30809        fitToFrame:true
30810    },
30811    center : {
30812        el: cview,
30813        autoScroll:true,
30814        fitToFrame:true,
30815        toolbar: tb,
30816        resizeEl:'cbody'
30817    }
30818 });
30819 </code></pre>
30820      * @param {Object} regions An object containing ContentPanel configs by region name
30821      */
30822     batchAdd : function(regions){
30823         this.beginUpdate();
30824         for(var rname in regions){
30825             var lr = this.regions[rname];
30826             if(lr){
30827                 this.addTypedPanels(lr, regions[rname]);
30828             }
30829         }
30830         this.endUpdate();
30831     },
30832
30833     // private
30834     addTypedPanels : function(lr, ps){
30835         if(typeof ps == 'string'){
30836             lr.add(new Roo.ContentPanel(ps));
30837         }
30838         else if(ps instanceof Array){
30839             for(var i =0, len = ps.length; i < len; i++){
30840                 this.addTypedPanels(lr, ps[i]);
30841             }
30842         }
30843         else if(!ps.events){ // raw config?
30844             var el = ps.el;
30845             delete ps.el; // prevent conflict
30846             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30847         }
30848         else {  // panel object assumed!
30849             lr.add(ps);
30850         }
30851     },
30852     /**
30853      * Adds a xtype elements to the layout.
30854      * <pre><code>
30855
30856 layout.addxtype({
30857        xtype : 'ContentPanel',
30858        region: 'west',
30859        items: [ .... ]
30860    }
30861 );
30862
30863 layout.addxtype({
30864         xtype : 'NestedLayoutPanel',
30865         region: 'west',
30866         layout: {
30867            center: { },
30868            west: { }   
30869         },
30870         items : [ ... list of content panels or nested layout panels.. ]
30871    }
30872 );
30873 </code></pre>
30874      * @param {Object} cfg Xtype definition of item to add.
30875      */
30876     addxtype : function(cfg)
30877     {
30878         // basically accepts a pannel...
30879         // can accept a layout region..!?!?
30880         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30881         
30882         if (!cfg.xtype.match(/Panel$/)) {
30883             return false;
30884         }
30885         var ret = false;
30886         
30887         if (typeof(cfg.region) == 'undefined') {
30888             Roo.log("Failed to add Panel, region was not set");
30889             Roo.log(cfg);
30890             return false;
30891         }
30892         var region = cfg.region;
30893         delete cfg.region;
30894         
30895           
30896         var xitems = [];
30897         if (cfg.items) {
30898             xitems = cfg.items;
30899             delete cfg.items;
30900         }
30901         var nb = false;
30902         
30903         switch(cfg.xtype) 
30904         {
30905             case 'ContentPanel':  // ContentPanel (el, cfg)
30906             case 'ScrollPanel':  // ContentPanel (el, cfg)
30907                 if(cfg.autoCreate) {
30908                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30909                 } else {
30910                     var el = this.el.createChild();
30911                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30912                 }
30913                 
30914                 this.add(region, ret);
30915                 break;
30916             
30917             
30918             case 'TreePanel': // our new panel!
30919                 cfg.el = this.el.createChild();
30920                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30921                 this.add(region, ret);
30922                 break;
30923             
30924             case 'NestedLayoutPanel': 
30925                 // create a new Layout (which is  a Border Layout...
30926                 var el = this.el.createChild();
30927                 var clayout = cfg.layout;
30928                 delete cfg.layout;
30929                 clayout.items   = clayout.items  || [];
30930                 // replace this exitems with the clayout ones..
30931                 xitems = clayout.items;
30932                  
30933                 
30934                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30935                     cfg.background = false;
30936                 }
30937                 var layout = new Roo.BorderLayout(el, clayout);
30938                 
30939                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30940                 //console.log('adding nested layout panel '  + cfg.toSource());
30941                 this.add(region, ret);
30942                 nb = {}; /// find first...
30943                 break;
30944                 
30945             case 'GridPanel': 
30946             
30947                 // needs grid and region
30948                 
30949                 //var el = this.getRegion(region).el.createChild();
30950                 var el = this.el.createChild();
30951                 // create the grid first...
30952                 
30953                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30954                 delete cfg.grid;
30955                 if (region == 'center' && this.active ) {
30956                     cfg.background = false;
30957                 }
30958                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30959                 
30960                 this.add(region, ret);
30961                 if (cfg.background) {
30962                     ret.on('activate', function(gp) {
30963                         if (!gp.grid.rendered) {
30964                             gp.grid.render();
30965                         }
30966                     });
30967                 } else {
30968                     grid.render();
30969                 }
30970                 break;
30971            
30972                
30973                 
30974                 
30975             default: 
30976                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30977                 return null;
30978              // GridPanel (grid, cfg)
30979             
30980         }
30981         this.beginUpdate();
30982         // add children..
30983         var region = '';
30984         var abn = {};
30985         Roo.each(xitems, function(i)  {
30986             region = nb && i.region ? i.region : false;
30987             
30988             var add = ret.addxtype(i);
30989            
30990             if (region) {
30991                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
30992                 if (!i.background) {
30993                     abn[region] = nb[region] ;
30994                 }
30995             }
30996             
30997         });
30998         this.endUpdate();
30999
31000         // make the last non-background panel active..
31001         //if (nb) { Roo.log(abn); }
31002         if (nb) {
31003             
31004             for(var r in abn) {
31005                 region = this.getRegion(r);
31006                 if (region) {
31007                     // tried using nb[r], but it does not work..
31008                      
31009                     region.showPanel(abn[r]);
31010                    
31011                 }
31012             }
31013         }
31014         return ret;
31015         
31016     }
31017 });
31018
31019 /**
31020  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31021  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31022  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31023  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31024  * <pre><code>
31025 // shorthand
31026 var CP = Roo.ContentPanel;
31027
31028 var layout = Roo.BorderLayout.create({
31029     north: {
31030         initialSize: 25,
31031         titlebar: false,
31032         panels: [new CP("north", "North")]
31033     },
31034     west: {
31035         split:true,
31036         initialSize: 200,
31037         minSize: 175,
31038         maxSize: 400,
31039         titlebar: true,
31040         collapsible: true,
31041         panels: [new CP("west", {title: "West"})]
31042     },
31043     east: {
31044         split:true,
31045         initialSize: 202,
31046         minSize: 175,
31047         maxSize: 400,
31048         titlebar: true,
31049         collapsible: true,
31050         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31051     },
31052     south: {
31053         split:true,
31054         initialSize: 100,
31055         minSize: 100,
31056         maxSize: 200,
31057         titlebar: true,
31058         collapsible: true,
31059         panels: [new CP("south", {title: "South", closable: true})]
31060     },
31061     center: {
31062         titlebar: true,
31063         autoScroll:true,
31064         resizeTabs: true,
31065         minTabWidth: 50,
31066         preferredTabWidth: 150,
31067         panels: [
31068             new CP("center1", {title: "Close Me", closable: true}),
31069             new CP("center2", {title: "Center Panel", closable: false})
31070         ]
31071     }
31072 }, document.body);
31073
31074 layout.getRegion("center").showPanel("center1");
31075 </code></pre>
31076  * @param config
31077  * @param targetEl
31078  */
31079 Roo.BorderLayout.create = function(config, targetEl){
31080     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31081     layout.beginUpdate();
31082     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31083     for(var j = 0, jlen = regions.length; j < jlen; j++){
31084         var lr = regions[j];
31085         if(layout.regions[lr] && config[lr].panels){
31086             var r = layout.regions[lr];
31087             var ps = config[lr].panels;
31088             layout.addTypedPanels(r, ps);
31089         }
31090     }
31091     layout.endUpdate();
31092     return layout;
31093 };
31094
31095 // private
31096 Roo.BorderLayout.RegionFactory = {
31097     // private
31098     validRegions : ["north","south","east","west","center"],
31099
31100     // private
31101     create : function(target, mgr, config){
31102         target = target.toLowerCase();
31103         if(config.lightweight || config.basic){
31104             return new Roo.BasicLayoutRegion(mgr, config, target);
31105         }
31106         switch(target){
31107             case "north":
31108                 return new Roo.NorthLayoutRegion(mgr, config);
31109             case "south":
31110                 return new Roo.SouthLayoutRegion(mgr, config);
31111             case "east":
31112                 return new Roo.EastLayoutRegion(mgr, config);
31113             case "west":
31114                 return new Roo.WestLayoutRegion(mgr, config);
31115             case "center":
31116                 return new Roo.CenterLayoutRegion(mgr, config);
31117         }
31118         throw 'Layout region "'+target+'" not supported.';
31119     }
31120 };/*
31121  * Based on:
31122  * Ext JS Library 1.1.1
31123  * Copyright(c) 2006-2007, Ext JS, LLC.
31124  *
31125  * Originally Released Under LGPL - original licence link has changed is not relivant.
31126  *
31127  * Fork - LGPL
31128  * <script type="text/javascript">
31129  */
31130  
31131 /**
31132  * @class Roo.BasicLayoutRegion
31133  * @extends Roo.util.Observable
31134  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31135  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31136  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31137  */
31138 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31139     this.mgr = mgr;
31140     this.position  = pos;
31141     this.events = {
31142         /**
31143          * @scope Roo.BasicLayoutRegion
31144          */
31145         
31146         /**
31147          * @event beforeremove
31148          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31149          * @param {Roo.LayoutRegion} this
31150          * @param {Roo.ContentPanel} panel The panel
31151          * @param {Object} e The cancel event object
31152          */
31153         "beforeremove" : true,
31154         /**
31155          * @event invalidated
31156          * Fires when the layout for this region is changed.
31157          * @param {Roo.LayoutRegion} this
31158          */
31159         "invalidated" : true,
31160         /**
31161          * @event visibilitychange
31162          * Fires when this region is shown or hidden 
31163          * @param {Roo.LayoutRegion} this
31164          * @param {Boolean} visibility true or false
31165          */
31166         "visibilitychange" : true,
31167         /**
31168          * @event paneladded
31169          * Fires when a panel is added. 
31170          * @param {Roo.LayoutRegion} this
31171          * @param {Roo.ContentPanel} panel The panel
31172          */
31173         "paneladded" : true,
31174         /**
31175          * @event panelremoved
31176          * Fires when a panel is removed. 
31177          * @param {Roo.LayoutRegion} this
31178          * @param {Roo.ContentPanel} panel The panel
31179          */
31180         "panelremoved" : true,
31181         /**
31182          * @event collapsed
31183          * Fires when this region is collapsed.
31184          * @param {Roo.LayoutRegion} this
31185          */
31186         "collapsed" : true,
31187         /**
31188          * @event expanded
31189          * Fires when this region is expanded.
31190          * @param {Roo.LayoutRegion} this
31191          */
31192         "expanded" : true,
31193         /**
31194          * @event slideshow
31195          * Fires when this region is slid into view.
31196          * @param {Roo.LayoutRegion} this
31197          */
31198         "slideshow" : true,
31199         /**
31200          * @event slidehide
31201          * Fires when this region slides out of view. 
31202          * @param {Roo.LayoutRegion} this
31203          */
31204         "slidehide" : true,
31205         /**
31206          * @event panelactivated
31207          * Fires when a panel is activated. 
31208          * @param {Roo.LayoutRegion} this
31209          * @param {Roo.ContentPanel} panel The activated panel
31210          */
31211         "panelactivated" : true,
31212         /**
31213          * @event resized
31214          * Fires when the user resizes this region. 
31215          * @param {Roo.LayoutRegion} this
31216          * @param {Number} newSize The new size (width for east/west, height for north/south)
31217          */
31218         "resized" : true
31219     };
31220     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31221     this.panels = new Roo.util.MixedCollection();
31222     this.panels.getKey = this.getPanelId.createDelegate(this);
31223     this.box = null;
31224     this.activePanel = null;
31225     // ensure listeners are added...
31226     
31227     if (config.listeners || config.events) {
31228         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31229             listeners : config.listeners || {},
31230             events : config.events || {}
31231         });
31232     }
31233     
31234     if(skipConfig !== true){
31235         this.applyConfig(config);
31236     }
31237 };
31238
31239 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31240     getPanelId : function(p){
31241         return p.getId();
31242     },
31243     
31244     applyConfig : function(config){
31245         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31246         this.config = config;
31247         
31248     },
31249     
31250     /**
31251      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31252      * the width, for horizontal (north, south) the height.
31253      * @param {Number} newSize The new width or height
31254      */
31255     resizeTo : function(newSize){
31256         var el = this.el ? this.el :
31257                  (this.activePanel ? this.activePanel.getEl() : null);
31258         if(el){
31259             switch(this.position){
31260                 case "east":
31261                 case "west":
31262                     el.setWidth(newSize);
31263                     this.fireEvent("resized", this, newSize);
31264                 break;
31265                 case "north":
31266                 case "south":
31267                     el.setHeight(newSize);
31268                     this.fireEvent("resized", this, newSize);
31269                 break;                
31270             }
31271         }
31272     },
31273     
31274     getBox : function(){
31275         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31276     },
31277     
31278     getMargins : function(){
31279         return this.margins;
31280     },
31281     
31282     updateBox : function(box){
31283         this.box = box;
31284         var el = this.activePanel.getEl();
31285         el.dom.style.left = box.x + "px";
31286         el.dom.style.top = box.y + "px";
31287         this.activePanel.setSize(box.width, box.height);
31288     },
31289     
31290     /**
31291      * Returns the container element for this region.
31292      * @return {Roo.Element}
31293      */
31294     getEl : function(){
31295         return this.activePanel;
31296     },
31297     
31298     /**
31299      * Returns true if this region is currently visible.
31300      * @return {Boolean}
31301      */
31302     isVisible : function(){
31303         return this.activePanel ? true : false;
31304     },
31305     
31306     setActivePanel : function(panel){
31307         panel = this.getPanel(panel);
31308         if(this.activePanel && this.activePanel != panel){
31309             this.activePanel.setActiveState(false);
31310             this.activePanel.getEl().setLeftTop(-10000,-10000);
31311         }
31312         this.activePanel = panel;
31313         panel.setActiveState(true);
31314         if(this.box){
31315             panel.setSize(this.box.width, this.box.height);
31316         }
31317         this.fireEvent("panelactivated", this, panel);
31318         this.fireEvent("invalidated");
31319     },
31320     
31321     /**
31322      * Show the specified panel.
31323      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31324      * @return {Roo.ContentPanel} The shown panel or null
31325      */
31326     showPanel : function(panel){
31327         if(panel = this.getPanel(panel)){
31328             this.setActivePanel(panel);
31329         }
31330         return panel;
31331     },
31332     
31333     /**
31334      * Get the active panel for this region.
31335      * @return {Roo.ContentPanel} The active panel or null
31336      */
31337     getActivePanel : function(){
31338         return this.activePanel;
31339     },
31340     
31341     /**
31342      * Add the passed ContentPanel(s)
31343      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31344      * @return {Roo.ContentPanel} The panel added (if only one was added)
31345      */
31346     add : function(panel){
31347         if(arguments.length > 1){
31348             for(var i = 0, len = arguments.length; i < len; i++) {
31349                 this.add(arguments[i]);
31350             }
31351             return null;
31352         }
31353         if(this.hasPanel(panel)){
31354             this.showPanel(panel);
31355             return panel;
31356         }
31357         var el = panel.getEl();
31358         if(el.dom.parentNode != this.mgr.el.dom){
31359             this.mgr.el.dom.appendChild(el.dom);
31360         }
31361         if(panel.setRegion){
31362             panel.setRegion(this);
31363         }
31364         this.panels.add(panel);
31365         el.setStyle("position", "absolute");
31366         if(!panel.background){
31367             this.setActivePanel(panel);
31368             if(this.config.initialSize && this.panels.getCount()==1){
31369                 this.resizeTo(this.config.initialSize);
31370             }
31371         }
31372         this.fireEvent("paneladded", this, panel);
31373         return panel;
31374     },
31375     
31376     /**
31377      * Returns true if the panel is in this region.
31378      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31379      * @return {Boolean}
31380      */
31381     hasPanel : function(panel){
31382         if(typeof panel == "object"){ // must be panel obj
31383             panel = panel.getId();
31384         }
31385         return this.getPanel(panel) ? true : false;
31386     },
31387     
31388     /**
31389      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31390      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31391      * @param {Boolean} preservePanel Overrides the config preservePanel option
31392      * @return {Roo.ContentPanel} The panel that was removed
31393      */
31394     remove : function(panel, preservePanel){
31395         panel = this.getPanel(panel);
31396         if(!panel){
31397             return null;
31398         }
31399         var e = {};
31400         this.fireEvent("beforeremove", this, panel, e);
31401         if(e.cancel === true){
31402             return null;
31403         }
31404         var panelId = panel.getId();
31405         this.panels.removeKey(panelId);
31406         return panel;
31407     },
31408     
31409     /**
31410      * Returns the panel specified or null if it's not in this region.
31411      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31412      * @return {Roo.ContentPanel}
31413      */
31414     getPanel : function(id){
31415         if(typeof id == "object"){ // must be panel obj
31416             return id;
31417         }
31418         return this.panels.get(id);
31419     },
31420     
31421     /**
31422      * Returns this regions position (north/south/east/west/center).
31423      * @return {String} 
31424      */
31425     getPosition: function(){
31426         return this.position;    
31427     }
31428 });/*
31429  * Based on:
31430  * Ext JS Library 1.1.1
31431  * Copyright(c) 2006-2007, Ext JS, LLC.
31432  *
31433  * Originally Released Under LGPL - original licence link has changed is not relivant.
31434  *
31435  * Fork - LGPL
31436  * <script type="text/javascript">
31437  */
31438  
31439 /**
31440  * @class Roo.LayoutRegion
31441  * @extends Roo.BasicLayoutRegion
31442  * This class represents a region in a layout manager.
31443  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31444  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31445  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31446  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31447  * @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})
31448  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31449  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31450  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31451  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31452  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31453  * @cfg {String}    title           The title for the region (overrides panel titles)
31454  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31455  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31456  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31457  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31458  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31459  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31460  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31461  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31462  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31463  * @cfg {Boolean}   showPin         True to show a pin button
31464  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31465  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31466  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31467  * @cfg {Number}    width           For East/West panels
31468  * @cfg {Number}    height          For North/South panels
31469  * @cfg {Boolean}   split           To show the splitter
31470  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31471  */
31472 Roo.LayoutRegion = function(mgr, config, pos){
31473     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31474     var dh = Roo.DomHelper;
31475     /** This region's container element 
31476     * @type Roo.Element */
31477     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31478     /** This region's title element 
31479     * @type Roo.Element */
31480
31481     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31482         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31483         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31484     ]}, true);
31485     this.titleEl.enableDisplayMode();
31486     /** This region's title text element 
31487     * @type HTMLElement */
31488     this.titleTextEl = this.titleEl.dom.firstChild;
31489     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31490     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31491     this.closeBtn.enableDisplayMode();
31492     this.closeBtn.on("click", this.closeClicked, this);
31493     this.closeBtn.hide();
31494
31495     this.createBody(config);
31496     this.visible = true;
31497     this.collapsed = false;
31498
31499     if(config.hideWhenEmpty){
31500         this.hide();
31501         this.on("paneladded", this.validateVisibility, this);
31502         this.on("panelremoved", this.validateVisibility, this);
31503     }
31504     this.applyConfig(config);
31505 };
31506
31507 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31508
31509     createBody : function(){
31510         /** This region's body element 
31511         * @type Roo.Element */
31512         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31513     },
31514
31515     applyConfig : function(c){
31516         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31517             var dh = Roo.DomHelper;
31518             if(c.titlebar !== false){
31519                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31520                 this.collapseBtn.on("click", this.collapse, this);
31521                 this.collapseBtn.enableDisplayMode();
31522
31523                 if(c.showPin === true || this.showPin){
31524                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31525                     this.stickBtn.enableDisplayMode();
31526                     this.stickBtn.on("click", this.expand, this);
31527                     this.stickBtn.hide();
31528                 }
31529             }
31530             /** This region's collapsed element
31531             * @type Roo.Element */
31532             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31533                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31534             ]}, true);
31535             if(c.floatable !== false){
31536                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31537                this.collapsedEl.on("click", this.collapseClick, this);
31538             }
31539
31540             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31541                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31542                    id: "message", unselectable: "on", style:{"float":"left"}});
31543                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31544              }
31545             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31546             this.expandBtn.on("click", this.expand, this);
31547         }
31548         if(this.collapseBtn){
31549             this.collapseBtn.setVisible(c.collapsible == true);
31550         }
31551         this.cmargins = c.cmargins || this.cmargins ||
31552                          (this.position == "west" || this.position == "east" ?
31553                              {top: 0, left: 2, right:2, bottom: 0} :
31554                              {top: 2, left: 0, right:0, bottom: 2});
31555         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31556         this.bottomTabs = c.tabPosition != "top";
31557         this.autoScroll = c.autoScroll || false;
31558         if(this.autoScroll){
31559             this.bodyEl.setStyle("overflow", "auto");
31560         }else{
31561             this.bodyEl.setStyle("overflow", "hidden");
31562         }
31563         //if(c.titlebar !== false){
31564             if((!c.titlebar && !c.title) || c.titlebar === false){
31565                 this.titleEl.hide();
31566             }else{
31567                 this.titleEl.show();
31568                 if(c.title){
31569                     this.titleTextEl.innerHTML = c.title;
31570                 }
31571             }
31572         //}
31573         this.duration = c.duration || .30;
31574         this.slideDuration = c.slideDuration || .45;
31575         this.config = c;
31576         if(c.collapsed){
31577             this.collapse(true);
31578         }
31579         if(c.hidden){
31580             this.hide();
31581         }
31582     },
31583     /**
31584      * Returns true if this region is currently visible.
31585      * @return {Boolean}
31586      */
31587     isVisible : function(){
31588         return this.visible;
31589     },
31590
31591     /**
31592      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31593      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31594      */
31595     setCollapsedTitle : function(title){
31596         title = title || "&#160;";
31597         if(this.collapsedTitleTextEl){
31598             this.collapsedTitleTextEl.innerHTML = title;
31599         }
31600     },
31601
31602     getBox : function(){
31603         var b;
31604         if(!this.collapsed){
31605             b = this.el.getBox(false, true);
31606         }else{
31607             b = this.collapsedEl.getBox(false, true);
31608         }
31609         return b;
31610     },
31611
31612     getMargins : function(){
31613         return this.collapsed ? this.cmargins : this.margins;
31614     },
31615
31616     highlight : function(){
31617         this.el.addClass("x-layout-panel-dragover");
31618     },
31619
31620     unhighlight : function(){
31621         this.el.removeClass("x-layout-panel-dragover");
31622     },
31623
31624     updateBox : function(box){
31625         this.box = box;
31626         if(!this.collapsed){
31627             this.el.dom.style.left = box.x + "px";
31628             this.el.dom.style.top = box.y + "px";
31629             this.updateBody(box.width, box.height);
31630         }else{
31631             this.collapsedEl.dom.style.left = box.x + "px";
31632             this.collapsedEl.dom.style.top = box.y + "px";
31633             this.collapsedEl.setSize(box.width, box.height);
31634         }
31635         if(this.tabs){
31636             this.tabs.autoSizeTabs();
31637         }
31638     },
31639
31640     updateBody : function(w, h){
31641         if(w !== null){
31642             this.el.setWidth(w);
31643             w -= this.el.getBorderWidth("rl");
31644             if(this.config.adjustments){
31645                 w += this.config.adjustments[0];
31646             }
31647         }
31648         if(h !== null){
31649             this.el.setHeight(h);
31650             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31651             h -= this.el.getBorderWidth("tb");
31652             if(this.config.adjustments){
31653                 h += this.config.adjustments[1];
31654             }
31655             this.bodyEl.setHeight(h);
31656             if(this.tabs){
31657                 h = this.tabs.syncHeight(h);
31658             }
31659         }
31660         if(this.panelSize){
31661             w = w !== null ? w : this.panelSize.width;
31662             h = h !== null ? h : this.panelSize.height;
31663         }
31664         if(this.activePanel){
31665             var el = this.activePanel.getEl();
31666             w = w !== null ? w : el.getWidth();
31667             h = h !== null ? h : el.getHeight();
31668             this.panelSize = {width: w, height: h};
31669             this.activePanel.setSize(w, h);
31670         }
31671         if(Roo.isIE && this.tabs){
31672             this.tabs.el.repaint();
31673         }
31674     },
31675
31676     /**
31677      * Returns the container element for this region.
31678      * @return {Roo.Element}
31679      */
31680     getEl : function(){
31681         return this.el;
31682     },
31683
31684     /**
31685      * Hides this region.
31686      */
31687     hide : function(){
31688         if(!this.collapsed){
31689             this.el.dom.style.left = "-2000px";
31690             this.el.hide();
31691         }else{
31692             this.collapsedEl.dom.style.left = "-2000px";
31693             this.collapsedEl.hide();
31694         }
31695         this.visible = false;
31696         this.fireEvent("visibilitychange", this, false);
31697     },
31698
31699     /**
31700      * Shows this region if it was previously hidden.
31701      */
31702     show : function(){
31703         if(!this.collapsed){
31704             this.el.show();
31705         }else{
31706             this.collapsedEl.show();
31707         }
31708         this.visible = true;
31709         this.fireEvent("visibilitychange", this, true);
31710     },
31711
31712     closeClicked : function(){
31713         if(this.activePanel){
31714             this.remove(this.activePanel);
31715         }
31716     },
31717
31718     collapseClick : function(e){
31719         if(this.isSlid){
31720            e.stopPropagation();
31721            this.slideIn();
31722         }else{
31723            e.stopPropagation();
31724            this.slideOut();
31725         }
31726     },
31727
31728     /**
31729      * Collapses this region.
31730      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31731      */
31732     collapse : function(skipAnim){
31733         if(this.collapsed) return;
31734         this.collapsed = true;
31735         if(this.split){
31736             this.split.el.hide();
31737         }
31738         if(this.config.animate && skipAnim !== true){
31739             this.fireEvent("invalidated", this);
31740             this.animateCollapse();
31741         }else{
31742             this.el.setLocation(-20000,-20000);
31743             this.el.hide();
31744             this.collapsedEl.show();
31745             this.fireEvent("collapsed", this);
31746             this.fireEvent("invalidated", this);
31747         }
31748     },
31749
31750     animateCollapse : function(){
31751         // overridden
31752     },
31753
31754     /**
31755      * Expands this region if it was previously collapsed.
31756      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31757      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31758      */
31759     expand : function(e, skipAnim){
31760         if(e) e.stopPropagation();
31761         if(!this.collapsed || this.el.hasActiveFx()) return;
31762         if(this.isSlid){
31763             this.afterSlideIn();
31764             skipAnim = true;
31765         }
31766         this.collapsed = false;
31767         if(this.config.animate && skipAnim !== true){
31768             this.animateExpand();
31769         }else{
31770             this.el.show();
31771             if(this.split){
31772                 this.split.el.show();
31773             }
31774             this.collapsedEl.setLocation(-2000,-2000);
31775             this.collapsedEl.hide();
31776             this.fireEvent("invalidated", this);
31777             this.fireEvent("expanded", this);
31778         }
31779     },
31780
31781     animateExpand : function(){
31782         // overridden
31783     },
31784
31785     initTabs : function()
31786     {
31787         this.bodyEl.setStyle("overflow", "hidden");
31788         var ts = new Roo.TabPanel(
31789                 this.bodyEl.dom,
31790                 {
31791                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31792                     disableTooltips: this.config.disableTabTips,
31793                     toolbar : this.config.toolbar
31794                 }
31795         );
31796         if(this.config.hideTabs){
31797             ts.stripWrap.setDisplayed(false);
31798         }
31799         this.tabs = ts;
31800         ts.resizeTabs = this.config.resizeTabs === true;
31801         ts.minTabWidth = this.config.minTabWidth || 40;
31802         ts.maxTabWidth = this.config.maxTabWidth || 250;
31803         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31804         ts.monitorResize = false;
31805         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31806         ts.bodyEl.addClass('x-layout-tabs-body');
31807         this.panels.each(this.initPanelAsTab, this);
31808     },
31809
31810     initPanelAsTab : function(panel){
31811         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31812                     this.config.closeOnTab && panel.isClosable());
31813         if(panel.tabTip !== undefined){
31814             ti.setTooltip(panel.tabTip);
31815         }
31816         ti.on("activate", function(){
31817               this.setActivePanel(panel);
31818         }, this);
31819         if(this.config.closeOnTab){
31820             ti.on("beforeclose", function(t, e){
31821                 e.cancel = true;
31822                 this.remove(panel);
31823             }, this);
31824         }
31825         return ti;
31826     },
31827
31828     updatePanelTitle : function(panel, title){
31829         if(this.activePanel == panel){
31830             this.updateTitle(title);
31831         }
31832         if(this.tabs){
31833             var ti = this.tabs.getTab(panel.getEl().id);
31834             ti.setText(title);
31835             if(panel.tabTip !== undefined){
31836                 ti.setTooltip(panel.tabTip);
31837             }
31838         }
31839     },
31840
31841     updateTitle : function(title){
31842         if(this.titleTextEl && !this.config.title){
31843             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31844         }
31845     },
31846
31847     setActivePanel : function(panel){
31848         panel = this.getPanel(panel);
31849         if(this.activePanel && this.activePanel != panel){
31850             this.activePanel.setActiveState(false);
31851         }
31852         this.activePanel = panel;
31853         panel.setActiveState(true);
31854         if(this.panelSize){
31855             panel.setSize(this.panelSize.width, this.panelSize.height);
31856         }
31857         if(this.closeBtn){
31858             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31859         }
31860         this.updateTitle(panel.getTitle());
31861         if(this.tabs){
31862             this.fireEvent("invalidated", this);
31863         }
31864         this.fireEvent("panelactivated", this, panel);
31865     },
31866
31867     /**
31868      * Shows the specified panel.
31869      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31870      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31871      */
31872     showPanel : function(panel){
31873         if(panel = this.getPanel(panel)){
31874             if(this.tabs){
31875                 var tab = this.tabs.getTab(panel.getEl().id);
31876                 if(tab.isHidden()){
31877                     this.tabs.unhideTab(tab.id);
31878                 }
31879                 tab.activate();
31880             }else{
31881                 this.setActivePanel(panel);
31882             }
31883         }
31884         return panel;
31885     },
31886
31887     /**
31888      * Get the active panel for this region.
31889      * @return {Roo.ContentPanel} The active panel or null
31890      */
31891     getActivePanel : function(){
31892         return this.activePanel;
31893     },
31894
31895     validateVisibility : function(){
31896         if(this.panels.getCount() < 1){
31897             this.updateTitle("&#160;");
31898             this.closeBtn.hide();
31899             this.hide();
31900         }else{
31901             if(!this.isVisible()){
31902                 this.show();
31903             }
31904         }
31905     },
31906
31907     /**
31908      * Adds the passed ContentPanel(s) to this region.
31909      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31910      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31911      */
31912     add : function(panel){
31913         if(arguments.length > 1){
31914             for(var i = 0, len = arguments.length; i < len; i++) {
31915                 this.add(arguments[i]);
31916             }
31917             return null;
31918         }
31919         if(this.hasPanel(panel)){
31920             this.showPanel(panel);
31921             return panel;
31922         }
31923         panel.setRegion(this);
31924         this.panels.add(panel);
31925         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31926             this.bodyEl.dom.appendChild(panel.getEl().dom);
31927             if(panel.background !== true){
31928                 this.setActivePanel(panel);
31929             }
31930             this.fireEvent("paneladded", this, panel);
31931             return panel;
31932         }
31933         if(!this.tabs){
31934             this.initTabs();
31935         }else{
31936             this.initPanelAsTab(panel);
31937         }
31938         if(panel.background !== true){
31939             this.tabs.activate(panel.getEl().id);
31940         }
31941         this.fireEvent("paneladded", this, panel);
31942         return panel;
31943     },
31944
31945     /**
31946      * Hides the tab for the specified panel.
31947      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31948      */
31949     hidePanel : function(panel){
31950         if(this.tabs && (panel = this.getPanel(panel))){
31951             this.tabs.hideTab(panel.getEl().id);
31952         }
31953     },
31954
31955     /**
31956      * Unhides the tab for a previously hidden panel.
31957      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31958      */
31959     unhidePanel : function(panel){
31960         if(this.tabs && (panel = this.getPanel(panel))){
31961             this.tabs.unhideTab(panel.getEl().id);
31962         }
31963     },
31964
31965     clearPanels : function(){
31966         while(this.panels.getCount() > 0){
31967              this.remove(this.panels.first());
31968         }
31969     },
31970
31971     /**
31972      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31973      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31974      * @param {Boolean} preservePanel Overrides the config preservePanel option
31975      * @return {Roo.ContentPanel} The panel that was removed
31976      */
31977     remove : function(panel, preservePanel){
31978         panel = this.getPanel(panel);
31979         if(!panel){
31980             return null;
31981         }
31982         var e = {};
31983         this.fireEvent("beforeremove", this, panel, e);
31984         if(e.cancel === true){
31985             return null;
31986         }
31987         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31988         var panelId = panel.getId();
31989         this.panels.removeKey(panelId);
31990         if(preservePanel){
31991             document.body.appendChild(panel.getEl().dom);
31992         }
31993         if(this.tabs){
31994             this.tabs.removeTab(panel.getEl().id);
31995         }else if (!preservePanel){
31996             this.bodyEl.dom.removeChild(panel.getEl().dom);
31997         }
31998         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
31999             var p = this.panels.first();
32000             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32001             tempEl.appendChild(p.getEl().dom);
32002             this.bodyEl.update("");
32003             this.bodyEl.dom.appendChild(p.getEl().dom);
32004             tempEl = null;
32005             this.updateTitle(p.getTitle());
32006             this.tabs = null;
32007             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32008             this.setActivePanel(p);
32009         }
32010         panel.setRegion(null);
32011         if(this.activePanel == panel){
32012             this.activePanel = null;
32013         }
32014         if(this.config.autoDestroy !== false && preservePanel !== true){
32015             try{panel.destroy();}catch(e){}
32016         }
32017         this.fireEvent("panelremoved", this, panel);
32018         return panel;
32019     },
32020
32021     /**
32022      * Returns the TabPanel component used by this region
32023      * @return {Roo.TabPanel}
32024      */
32025     getTabs : function(){
32026         return this.tabs;
32027     },
32028
32029     createTool : function(parentEl, className){
32030         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32031             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32032         btn.addClassOnOver("x-layout-tools-button-over");
32033         return btn;
32034     }
32035 });/*
32036  * Based on:
32037  * Ext JS Library 1.1.1
32038  * Copyright(c) 2006-2007, Ext JS, LLC.
32039  *
32040  * Originally Released Under LGPL - original licence link has changed is not relivant.
32041  *
32042  * Fork - LGPL
32043  * <script type="text/javascript">
32044  */
32045  
32046
32047
32048 /**
32049  * @class Roo.SplitLayoutRegion
32050  * @extends Roo.LayoutRegion
32051  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32052  */
32053 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32054     this.cursor = cursor;
32055     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32056 };
32057
32058 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32059     splitTip : "Drag to resize.",
32060     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32061     useSplitTips : false,
32062
32063     applyConfig : function(config){
32064         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32065         if(config.split){
32066             if(!this.split){
32067                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32068                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32069                 /** The SplitBar for this region 
32070                 * @type Roo.SplitBar */
32071                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32072                 this.split.on("moved", this.onSplitMove, this);
32073                 this.split.useShim = config.useShim === true;
32074                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32075                 if(this.useSplitTips){
32076                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32077                 }
32078                 if(config.collapsible){
32079                     this.split.el.on("dblclick", this.collapse,  this);
32080                 }
32081             }
32082             if(typeof config.minSize != "undefined"){
32083                 this.split.minSize = config.minSize;
32084             }
32085             if(typeof config.maxSize != "undefined"){
32086                 this.split.maxSize = config.maxSize;
32087             }
32088             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32089                 this.hideSplitter();
32090             }
32091         }
32092     },
32093
32094     getHMaxSize : function(){
32095          var cmax = this.config.maxSize || 10000;
32096          var center = this.mgr.getRegion("center");
32097          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32098     },
32099
32100     getVMaxSize : function(){
32101          var cmax = this.config.maxSize || 10000;
32102          var center = this.mgr.getRegion("center");
32103          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32104     },
32105
32106     onSplitMove : function(split, newSize){
32107         this.fireEvent("resized", this, newSize);
32108     },
32109     
32110     /** 
32111      * Returns the {@link Roo.SplitBar} for this region.
32112      * @return {Roo.SplitBar}
32113      */
32114     getSplitBar : function(){
32115         return this.split;
32116     },
32117     
32118     hide : function(){
32119         this.hideSplitter();
32120         Roo.SplitLayoutRegion.superclass.hide.call(this);
32121     },
32122
32123     hideSplitter : function(){
32124         if(this.split){
32125             this.split.el.setLocation(-2000,-2000);
32126             this.split.el.hide();
32127         }
32128     },
32129
32130     show : function(){
32131         if(this.split){
32132             this.split.el.show();
32133         }
32134         Roo.SplitLayoutRegion.superclass.show.call(this);
32135     },
32136     
32137     beforeSlide: function(){
32138         if(Roo.isGecko){// firefox overflow auto bug workaround
32139             this.bodyEl.clip();
32140             if(this.tabs) this.tabs.bodyEl.clip();
32141             if(this.activePanel){
32142                 this.activePanel.getEl().clip();
32143                 
32144                 if(this.activePanel.beforeSlide){
32145                     this.activePanel.beforeSlide();
32146                 }
32147             }
32148         }
32149     },
32150     
32151     afterSlide : function(){
32152         if(Roo.isGecko){// firefox overflow auto bug workaround
32153             this.bodyEl.unclip();
32154             if(this.tabs) this.tabs.bodyEl.unclip();
32155             if(this.activePanel){
32156                 this.activePanel.getEl().unclip();
32157                 if(this.activePanel.afterSlide){
32158                     this.activePanel.afterSlide();
32159                 }
32160             }
32161         }
32162     },
32163
32164     initAutoHide : function(){
32165         if(this.autoHide !== false){
32166             if(!this.autoHideHd){
32167                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32168                 this.autoHideHd = {
32169                     "mouseout": function(e){
32170                         if(!e.within(this.el, true)){
32171                             st.delay(500);
32172                         }
32173                     },
32174                     "mouseover" : function(e){
32175                         st.cancel();
32176                     },
32177                     scope : this
32178                 };
32179             }
32180             this.el.on(this.autoHideHd);
32181         }
32182     },
32183
32184     clearAutoHide : function(){
32185         if(this.autoHide !== false){
32186             this.el.un("mouseout", this.autoHideHd.mouseout);
32187             this.el.un("mouseover", this.autoHideHd.mouseover);
32188         }
32189     },
32190
32191     clearMonitor : function(){
32192         Roo.get(document).un("click", this.slideInIf, this);
32193     },
32194
32195     // these names are backwards but not changed for compat
32196     slideOut : function(){
32197         if(this.isSlid || this.el.hasActiveFx()){
32198             return;
32199         }
32200         this.isSlid = true;
32201         if(this.collapseBtn){
32202             this.collapseBtn.hide();
32203         }
32204         this.closeBtnState = this.closeBtn.getStyle('display');
32205         this.closeBtn.hide();
32206         if(this.stickBtn){
32207             this.stickBtn.show();
32208         }
32209         this.el.show();
32210         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32211         this.beforeSlide();
32212         this.el.setStyle("z-index", 10001);
32213         this.el.slideIn(this.getSlideAnchor(), {
32214             callback: function(){
32215                 this.afterSlide();
32216                 this.initAutoHide();
32217                 Roo.get(document).on("click", this.slideInIf, this);
32218                 this.fireEvent("slideshow", this);
32219             },
32220             scope: this,
32221             block: true
32222         });
32223     },
32224
32225     afterSlideIn : function(){
32226         this.clearAutoHide();
32227         this.isSlid = false;
32228         this.clearMonitor();
32229         this.el.setStyle("z-index", "");
32230         if(this.collapseBtn){
32231             this.collapseBtn.show();
32232         }
32233         this.closeBtn.setStyle('display', this.closeBtnState);
32234         if(this.stickBtn){
32235             this.stickBtn.hide();
32236         }
32237         this.fireEvent("slidehide", this);
32238     },
32239
32240     slideIn : function(cb){
32241         if(!this.isSlid || this.el.hasActiveFx()){
32242             Roo.callback(cb);
32243             return;
32244         }
32245         this.isSlid = false;
32246         this.beforeSlide();
32247         this.el.slideOut(this.getSlideAnchor(), {
32248             callback: function(){
32249                 this.el.setLeftTop(-10000, -10000);
32250                 this.afterSlide();
32251                 this.afterSlideIn();
32252                 Roo.callback(cb);
32253             },
32254             scope: this,
32255             block: true
32256         });
32257     },
32258     
32259     slideInIf : function(e){
32260         if(!e.within(this.el)){
32261             this.slideIn();
32262         }
32263     },
32264
32265     animateCollapse : function(){
32266         this.beforeSlide();
32267         this.el.setStyle("z-index", 20000);
32268         var anchor = this.getSlideAnchor();
32269         this.el.slideOut(anchor, {
32270             callback : function(){
32271                 this.el.setStyle("z-index", "");
32272                 this.collapsedEl.slideIn(anchor, {duration:.3});
32273                 this.afterSlide();
32274                 this.el.setLocation(-10000,-10000);
32275                 this.el.hide();
32276                 this.fireEvent("collapsed", this);
32277             },
32278             scope: this,
32279             block: true
32280         });
32281     },
32282
32283     animateExpand : function(){
32284         this.beforeSlide();
32285         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32286         this.el.setStyle("z-index", 20000);
32287         this.collapsedEl.hide({
32288             duration:.1
32289         });
32290         this.el.slideIn(this.getSlideAnchor(), {
32291             callback : function(){
32292                 this.el.setStyle("z-index", "");
32293                 this.afterSlide();
32294                 if(this.split){
32295                     this.split.el.show();
32296                 }
32297                 this.fireEvent("invalidated", this);
32298                 this.fireEvent("expanded", this);
32299             },
32300             scope: this,
32301             block: true
32302         });
32303     },
32304
32305     anchors : {
32306         "west" : "left",
32307         "east" : "right",
32308         "north" : "top",
32309         "south" : "bottom"
32310     },
32311
32312     sanchors : {
32313         "west" : "l",
32314         "east" : "r",
32315         "north" : "t",
32316         "south" : "b"
32317     },
32318
32319     canchors : {
32320         "west" : "tl-tr",
32321         "east" : "tr-tl",
32322         "north" : "tl-bl",
32323         "south" : "bl-tl"
32324     },
32325
32326     getAnchor : function(){
32327         return this.anchors[this.position];
32328     },
32329
32330     getCollapseAnchor : function(){
32331         return this.canchors[this.position];
32332     },
32333
32334     getSlideAnchor : function(){
32335         return this.sanchors[this.position];
32336     },
32337
32338     getAlignAdj : function(){
32339         var cm = this.cmargins;
32340         switch(this.position){
32341             case "west":
32342                 return [0, 0];
32343             break;
32344             case "east":
32345                 return [0, 0];
32346             break;
32347             case "north":
32348                 return [0, 0];
32349             break;
32350             case "south":
32351                 return [0, 0];
32352             break;
32353         }
32354     },
32355
32356     getExpandAdj : function(){
32357         var c = this.collapsedEl, cm = this.cmargins;
32358         switch(this.position){
32359             case "west":
32360                 return [-(cm.right+c.getWidth()+cm.left), 0];
32361             break;
32362             case "east":
32363                 return [cm.right+c.getWidth()+cm.left, 0];
32364             break;
32365             case "north":
32366                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32367             break;
32368             case "south":
32369                 return [0, cm.top+cm.bottom+c.getHeight()];
32370             break;
32371         }
32372     }
32373 });/*
32374  * Based on:
32375  * Ext JS Library 1.1.1
32376  * Copyright(c) 2006-2007, Ext JS, LLC.
32377  *
32378  * Originally Released Under LGPL - original licence link has changed is not relivant.
32379  *
32380  * Fork - LGPL
32381  * <script type="text/javascript">
32382  */
32383 /*
32384  * These classes are private internal classes
32385  */
32386 Roo.CenterLayoutRegion = function(mgr, config){
32387     Roo.LayoutRegion.call(this, mgr, config, "center");
32388     this.visible = true;
32389     this.minWidth = config.minWidth || 20;
32390     this.minHeight = config.minHeight || 20;
32391 };
32392
32393 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32394     hide : function(){
32395         // center panel can't be hidden
32396     },
32397     
32398     show : function(){
32399         // center panel can't be hidden
32400     },
32401     
32402     getMinWidth: function(){
32403         return this.minWidth;
32404     },
32405     
32406     getMinHeight: function(){
32407         return this.minHeight;
32408     }
32409 });
32410
32411
32412 Roo.NorthLayoutRegion = function(mgr, config){
32413     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32414     if(this.split){
32415         this.split.placement = Roo.SplitBar.TOP;
32416         this.split.orientation = Roo.SplitBar.VERTICAL;
32417         this.split.el.addClass("x-layout-split-v");
32418     }
32419     var size = config.initialSize || config.height;
32420     if(typeof size != "undefined"){
32421         this.el.setHeight(size);
32422     }
32423 };
32424 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32425     orientation: Roo.SplitBar.VERTICAL,
32426     getBox : function(){
32427         if(this.collapsed){
32428             return this.collapsedEl.getBox();
32429         }
32430         var box = this.el.getBox();
32431         if(this.split){
32432             box.height += this.split.el.getHeight();
32433         }
32434         return box;
32435     },
32436     
32437     updateBox : function(box){
32438         if(this.split && !this.collapsed){
32439             box.height -= this.split.el.getHeight();
32440             this.split.el.setLeft(box.x);
32441             this.split.el.setTop(box.y+box.height);
32442             this.split.el.setWidth(box.width);
32443         }
32444         if(this.collapsed){
32445             this.updateBody(box.width, null);
32446         }
32447         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32448     }
32449 });
32450
32451 Roo.SouthLayoutRegion = function(mgr, config){
32452     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32453     if(this.split){
32454         this.split.placement = Roo.SplitBar.BOTTOM;
32455         this.split.orientation = Roo.SplitBar.VERTICAL;
32456         this.split.el.addClass("x-layout-split-v");
32457     }
32458     var size = config.initialSize || config.height;
32459     if(typeof size != "undefined"){
32460         this.el.setHeight(size);
32461     }
32462 };
32463 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32464     orientation: Roo.SplitBar.VERTICAL,
32465     getBox : function(){
32466         if(this.collapsed){
32467             return this.collapsedEl.getBox();
32468         }
32469         var box = this.el.getBox();
32470         if(this.split){
32471             var sh = this.split.el.getHeight();
32472             box.height += sh;
32473             box.y -= sh;
32474         }
32475         return box;
32476     },
32477     
32478     updateBox : function(box){
32479         if(this.split && !this.collapsed){
32480             var sh = this.split.el.getHeight();
32481             box.height -= sh;
32482             box.y += sh;
32483             this.split.el.setLeft(box.x);
32484             this.split.el.setTop(box.y-sh);
32485             this.split.el.setWidth(box.width);
32486         }
32487         if(this.collapsed){
32488             this.updateBody(box.width, null);
32489         }
32490         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32491     }
32492 });
32493
32494 Roo.EastLayoutRegion = function(mgr, config){
32495     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32496     if(this.split){
32497         this.split.placement = Roo.SplitBar.RIGHT;
32498         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32499         this.split.el.addClass("x-layout-split-h");
32500     }
32501     var size = config.initialSize || config.width;
32502     if(typeof size != "undefined"){
32503         this.el.setWidth(size);
32504     }
32505 };
32506 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32507     orientation: Roo.SplitBar.HORIZONTAL,
32508     getBox : function(){
32509         if(this.collapsed){
32510             return this.collapsedEl.getBox();
32511         }
32512         var box = this.el.getBox();
32513         if(this.split){
32514             var sw = this.split.el.getWidth();
32515             box.width += sw;
32516             box.x -= sw;
32517         }
32518         return box;
32519     },
32520
32521     updateBox : function(box){
32522         if(this.split && !this.collapsed){
32523             var sw = this.split.el.getWidth();
32524             box.width -= sw;
32525             this.split.el.setLeft(box.x);
32526             this.split.el.setTop(box.y);
32527             this.split.el.setHeight(box.height);
32528             box.x += sw;
32529         }
32530         if(this.collapsed){
32531             this.updateBody(null, box.height);
32532         }
32533         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32534     }
32535 });
32536
32537 Roo.WestLayoutRegion = function(mgr, config){
32538     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32539     if(this.split){
32540         this.split.placement = Roo.SplitBar.LEFT;
32541         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32542         this.split.el.addClass("x-layout-split-h");
32543     }
32544     var size = config.initialSize || config.width;
32545     if(typeof size != "undefined"){
32546         this.el.setWidth(size);
32547     }
32548 };
32549 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32550     orientation: Roo.SplitBar.HORIZONTAL,
32551     getBox : function(){
32552         if(this.collapsed){
32553             return this.collapsedEl.getBox();
32554         }
32555         var box = this.el.getBox();
32556         if(this.split){
32557             box.width += this.split.el.getWidth();
32558         }
32559         return box;
32560     },
32561     
32562     updateBox : function(box){
32563         if(this.split && !this.collapsed){
32564             var sw = this.split.el.getWidth();
32565             box.width -= sw;
32566             this.split.el.setLeft(box.x+box.width);
32567             this.split.el.setTop(box.y);
32568             this.split.el.setHeight(box.height);
32569         }
32570         if(this.collapsed){
32571             this.updateBody(null, box.height);
32572         }
32573         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32574     }
32575 });
32576 /*
32577  * Based on:
32578  * Ext JS Library 1.1.1
32579  * Copyright(c) 2006-2007, Ext JS, LLC.
32580  *
32581  * Originally Released Under LGPL - original licence link has changed is not relivant.
32582  *
32583  * Fork - LGPL
32584  * <script type="text/javascript">
32585  */
32586  
32587  
32588 /*
32589  * Private internal class for reading and applying state
32590  */
32591 Roo.LayoutStateManager = function(layout){
32592      // default empty state
32593      this.state = {
32594         north: {},
32595         south: {},
32596         east: {},
32597         west: {}       
32598     };
32599 };
32600
32601 Roo.LayoutStateManager.prototype = {
32602     init : function(layout, provider){
32603         this.provider = provider;
32604         var state = provider.get(layout.id+"-layout-state");
32605         if(state){
32606             var wasUpdating = layout.isUpdating();
32607             if(!wasUpdating){
32608                 layout.beginUpdate();
32609             }
32610             for(var key in state){
32611                 if(typeof state[key] != "function"){
32612                     var rstate = state[key];
32613                     var r = layout.getRegion(key);
32614                     if(r && rstate){
32615                         if(rstate.size){
32616                             r.resizeTo(rstate.size);
32617                         }
32618                         if(rstate.collapsed == true){
32619                             r.collapse(true);
32620                         }else{
32621                             r.expand(null, true);
32622                         }
32623                     }
32624                 }
32625             }
32626             if(!wasUpdating){
32627                 layout.endUpdate();
32628             }
32629             this.state = state; 
32630         }
32631         this.layout = layout;
32632         layout.on("regionresized", this.onRegionResized, this);
32633         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32634         layout.on("regionexpanded", this.onRegionExpanded, this);
32635     },
32636     
32637     storeState : function(){
32638         this.provider.set(this.layout.id+"-layout-state", this.state);
32639     },
32640     
32641     onRegionResized : function(region, newSize){
32642         this.state[region.getPosition()].size = newSize;
32643         this.storeState();
32644     },
32645     
32646     onRegionCollapsed : function(region){
32647         this.state[region.getPosition()].collapsed = true;
32648         this.storeState();
32649     },
32650     
32651     onRegionExpanded : function(region){
32652         this.state[region.getPosition()].collapsed = false;
32653         this.storeState();
32654     }
32655 };/*
32656  * Based on:
32657  * Ext JS Library 1.1.1
32658  * Copyright(c) 2006-2007, Ext JS, LLC.
32659  *
32660  * Originally Released Under LGPL - original licence link has changed is not relivant.
32661  *
32662  * Fork - LGPL
32663  * <script type="text/javascript">
32664  */
32665 /**
32666  * @class Roo.ContentPanel
32667  * @extends Roo.util.Observable
32668  * A basic ContentPanel element.
32669  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32670  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32671  * @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
32672  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32673  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32674  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32675  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32676  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32677  * @cfg {String} title          The title for this panel
32678  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32679  * @cfg {String} url            Calls {@link #setUrl} with this value
32680  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32681  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32682  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32683  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32684
32685  * @constructor
32686  * Create a new ContentPanel.
32687  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32688  * @param {String/Object} config A string to set only the title or a config object
32689  * @param {String} content (optional) Set the HTML content for this panel
32690  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32691  */
32692 Roo.ContentPanel = function(el, config, content){
32693     
32694      
32695     /*
32696     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32697         config = el;
32698         el = Roo.id();
32699     }
32700     if (config && config.parentLayout) { 
32701         el = config.parentLayout.el.createChild(); 
32702     }
32703     */
32704     if(el.autoCreate){ // xtype is available if this is called from factory
32705         config = el;
32706         el = Roo.id();
32707     }
32708     this.el = Roo.get(el);
32709     if(!this.el && config && config.autoCreate){
32710         if(typeof config.autoCreate == "object"){
32711             if(!config.autoCreate.id){
32712                 config.autoCreate.id = config.id||el;
32713             }
32714             this.el = Roo.DomHelper.append(document.body,
32715                         config.autoCreate, true);
32716         }else{
32717             this.el = Roo.DomHelper.append(document.body,
32718                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32719         }
32720     }
32721     this.closable = false;
32722     this.loaded = false;
32723     this.active = false;
32724     if(typeof config == "string"){
32725         this.title = config;
32726     }else{
32727         Roo.apply(this, config);
32728     }
32729     
32730     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32731         this.wrapEl = this.el.wrap();
32732         this.toolbar.container = this.el.insertSibling(false, 'before');
32733         this.toolbar = new Roo.Toolbar(this.toolbar);
32734     }
32735     
32736     
32737     
32738     if(this.resizeEl){
32739         this.resizeEl = Roo.get(this.resizeEl, true);
32740     }else{
32741         this.resizeEl = this.el;
32742     }
32743     this.addEvents({
32744         /**
32745          * @event activate
32746          * Fires when this panel is activated. 
32747          * @param {Roo.ContentPanel} this
32748          */
32749         "activate" : true,
32750         /**
32751          * @event deactivate
32752          * Fires when this panel is activated. 
32753          * @param {Roo.ContentPanel} this
32754          */
32755         "deactivate" : true,
32756
32757         /**
32758          * @event resize
32759          * Fires when this panel is resized if fitToFrame is true.
32760          * @param {Roo.ContentPanel} this
32761          * @param {Number} width The width after any component adjustments
32762          * @param {Number} height The height after any component adjustments
32763          */
32764         "resize" : true,
32765         
32766          /**
32767          * @event render
32768          * Fires when this tab is created
32769          * @param {Roo.ContentPanel} this
32770          */
32771         "render" : true
32772         
32773         
32774         
32775     });
32776     if(this.autoScroll){
32777         this.resizeEl.setStyle("overflow", "auto");
32778     } else {
32779         // fix randome scrolling
32780         this.el.on('scroll', function() {
32781             Roo.log('fix random scolling');
32782             this.scrollTo('top',0); 
32783         });
32784     }
32785     content = content || this.content;
32786     if(content){
32787         this.setContent(content);
32788     }
32789     if(config && config.url){
32790         this.setUrl(this.url, this.params, this.loadOnce);
32791     }
32792     
32793     
32794     
32795     Roo.ContentPanel.superclass.constructor.call(this);
32796     
32797     this.fireEvent('render', this);
32798 };
32799
32800 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32801     tabTip:'',
32802     setRegion : function(region){
32803         this.region = region;
32804         if(region){
32805            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32806         }else{
32807            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32808         } 
32809     },
32810     
32811     /**
32812      * Returns the toolbar for this Panel if one was configured. 
32813      * @return {Roo.Toolbar} 
32814      */
32815     getToolbar : function(){
32816         return this.toolbar;
32817     },
32818     
32819     setActiveState : function(active){
32820         this.active = active;
32821         if(!active){
32822             this.fireEvent("deactivate", this);
32823         }else{
32824             this.fireEvent("activate", this);
32825         }
32826     },
32827     /**
32828      * Updates this panel's element
32829      * @param {String} content The new content
32830      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32831     */
32832     setContent : function(content, loadScripts){
32833         this.el.update(content, loadScripts);
32834     },
32835
32836     ignoreResize : function(w, h){
32837         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32838             return true;
32839         }else{
32840             this.lastSize = {width: w, height: h};
32841             return false;
32842         }
32843     },
32844     /**
32845      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32846      * @return {Roo.UpdateManager} The UpdateManager
32847      */
32848     getUpdateManager : function(){
32849         return this.el.getUpdateManager();
32850     },
32851      /**
32852      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32853      * @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:
32854 <pre><code>
32855 panel.load({
32856     url: "your-url.php",
32857     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32858     callback: yourFunction,
32859     scope: yourObject, //(optional scope)
32860     discardUrl: false,
32861     nocache: false,
32862     text: "Loading...",
32863     timeout: 30,
32864     scripts: false
32865 });
32866 </code></pre>
32867      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32868      * 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.
32869      * @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}
32870      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32871      * @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.
32872      * @return {Roo.ContentPanel} this
32873      */
32874     load : function(){
32875         var um = this.el.getUpdateManager();
32876         um.update.apply(um, arguments);
32877         return this;
32878     },
32879
32880
32881     /**
32882      * 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.
32883      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32884      * @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)
32885      * @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)
32886      * @return {Roo.UpdateManager} The UpdateManager
32887      */
32888     setUrl : function(url, params, loadOnce){
32889         if(this.refreshDelegate){
32890             this.removeListener("activate", this.refreshDelegate);
32891         }
32892         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32893         this.on("activate", this.refreshDelegate);
32894         return this.el.getUpdateManager();
32895     },
32896     
32897     _handleRefresh : function(url, params, loadOnce){
32898         if(!loadOnce || !this.loaded){
32899             var updater = this.el.getUpdateManager();
32900             updater.update(url, params, this._setLoaded.createDelegate(this));
32901         }
32902     },
32903     
32904     _setLoaded : function(){
32905         this.loaded = true;
32906     }, 
32907     
32908     /**
32909      * Returns this panel's id
32910      * @return {String} 
32911      */
32912     getId : function(){
32913         return this.el.id;
32914     },
32915     
32916     /** 
32917      * Returns this panel's element - used by regiosn to add.
32918      * @return {Roo.Element} 
32919      */
32920     getEl : function(){
32921         return this.wrapEl || this.el;
32922     },
32923     
32924     adjustForComponents : function(width, height){
32925         if(this.resizeEl != this.el){
32926             width -= this.el.getFrameWidth('lr');
32927             height -= this.el.getFrameWidth('tb');
32928         }
32929         if(this.toolbar){
32930             var te = this.toolbar.getEl();
32931             height -= te.getHeight();
32932             te.setWidth(width);
32933         }
32934         if(this.adjustments){
32935             width += this.adjustments[0];
32936             height += this.adjustments[1];
32937         }
32938         return {"width": width, "height": height};
32939     },
32940     
32941     setSize : function(width, height){
32942         if(this.fitToFrame && !this.ignoreResize(width, height)){
32943             if(this.fitContainer && this.resizeEl != this.el){
32944                 this.el.setSize(width, height);
32945             }
32946             var size = this.adjustForComponents(width, height);
32947             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32948             this.fireEvent('resize', this, size.width, size.height);
32949         }
32950     },
32951     
32952     /**
32953      * Returns this panel's title
32954      * @return {String} 
32955      */
32956     getTitle : function(){
32957         return this.title;
32958     },
32959     
32960     /**
32961      * Set this panel's title
32962      * @param {String} title
32963      */
32964     setTitle : function(title){
32965         this.title = title;
32966         if(this.region){
32967             this.region.updatePanelTitle(this, title);
32968         }
32969     },
32970     
32971     /**
32972      * Returns true is this panel was configured to be closable
32973      * @return {Boolean} 
32974      */
32975     isClosable : function(){
32976         return this.closable;
32977     },
32978     
32979     beforeSlide : function(){
32980         this.el.clip();
32981         this.resizeEl.clip();
32982     },
32983     
32984     afterSlide : function(){
32985         this.el.unclip();
32986         this.resizeEl.unclip();
32987     },
32988     
32989     /**
32990      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
32991      *   Will fail silently if the {@link #setUrl} method has not been called.
32992      *   This does not activate the panel, just updates its content.
32993      */
32994     refresh : function(){
32995         if(this.refreshDelegate){
32996            this.loaded = false;
32997            this.refreshDelegate();
32998         }
32999     },
33000     
33001     /**
33002      * Destroys this panel
33003      */
33004     destroy : function(){
33005         this.el.removeAllListeners();
33006         var tempEl = document.createElement("span");
33007         tempEl.appendChild(this.el.dom);
33008         tempEl.innerHTML = "";
33009         this.el.remove();
33010         this.el = null;
33011     },
33012     
33013     /**
33014      * form - if the content panel contains a form - this is a reference to it.
33015      * @type {Roo.form.Form}
33016      */
33017     form : false,
33018     /**
33019      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33020      *    This contains a reference to it.
33021      * @type {Roo.View}
33022      */
33023     view : false,
33024     
33025       /**
33026      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33027      * <pre><code>
33028
33029 layout.addxtype({
33030        xtype : 'Form',
33031        items: [ .... ]
33032    }
33033 );
33034
33035 </code></pre>
33036      * @param {Object} cfg Xtype definition of item to add.
33037      */
33038     
33039     addxtype : function(cfg) {
33040         // add form..
33041         if (cfg.xtype.match(/^Form$/)) {
33042             var el = this.el.createChild();
33043
33044             this.form = new  Roo.form.Form(cfg);
33045             
33046             
33047             if ( this.form.allItems.length) this.form.render(el.dom);
33048             return this.form;
33049         }
33050         // should only have one of theses..
33051         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33052             // views..
33053             cfg.el = this.el.appendChild(document.createElement("div"));
33054             // factory?
33055             
33056             var ret = new Roo.factory(cfg);
33057             ret.render && ret.render(false, ''); // render blank..
33058             this.view = ret;
33059             return ret;
33060         }
33061         return false;
33062     }
33063 });
33064
33065 /**
33066  * @class Roo.GridPanel
33067  * @extends Roo.ContentPanel
33068  * @constructor
33069  * Create a new GridPanel.
33070  * @param {Roo.grid.Grid} grid The grid for this panel
33071  * @param {String/Object} config A string to set only the panel's title, or a config object
33072  */
33073 Roo.GridPanel = function(grid, config){
33074     
33075   
33076     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33077         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33078         
33079     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33080     
33081     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33082     
33083     if(this.toolbar){
33084         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33085     }
33086     // xtype created footer. - not sure if will work as we normally have to render first..
33087     if (this.footer && !this.footer.el && this.footer.xtype) {
33088         
33089         this.footer.container = this.grid.getView().getFooterPanel(true);
33090         this.footer.dataSource = this.grid.dataSource;
33091         this.footer = Roo.factory(this.footer, Roo);
33092         
33093     }
33094     
33095     grid.monitorWindowResize = false; // turn off autosizing
33096     grid.autoHeight = false;
33097     grid.autoWidth = false;
33098     this.grid = grid;
33099     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33100 };
33101
33102 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33103     getId : function(){
33104         return this.grid.id;
33105     },
33106     
33107     /**
33108      * Returns the grid for this panel
33109      * @return {Roo.grid.Grid} 
33110      */
33111     getGrid : function(){
33112         return this.grid;    
33113     },
33114     
33115     setSize : function(width, height){
33116         if(!this.ignoreResize(width, height)){
33117             var grid = this.grid;
33118             var size = this.adjustForComponents(width, height);
33119             grid.getGridEl().setSize(size.width, size.height);
33120             grid.autoSize();
33121         }
33122     },
33123     
33124     beforeSlide : function(){
33125         this.grid.getView().scroller.clip();
33126     },
33127     
33128     afterSlide : function(){
33129         this.grid.getView().scroller.unclip();
33130     },
33131     
33132     destroy : function(){
33133         this.grid.destroy();
33134         delete this.grid;
33135         Roo.GridPanel.superclass.destroy.call(this); 
33136     }
33137 });
33138
33139
33140 /**
33141  * @class Roo.NestedLayoutPanel
33142  * @extends Roo.ContentPanel
33143  * @constructor
33144  * Create a new NestedLayoutPanel.
33145  * 
33146  * 
33147  * @param {Roo.BorderLayout} layout The layout for this panel
33148  * @param {String/Object} config A string to set only the title or a config object
33149  */
33150 Roo.NestedLayoutPanel = function(layout, config)
33151 {
33152     // construct with only one argument..
33153     /* FIXME - implement nicer consturctors
33154     if (layout.layout) {
33155         config = layout;
33156         layout = config.layout;
33157         delete config.layout;
33158     }
33159     if (layout.xtype && !layout.getEl) {
33160         // then layout needs constructing..
33161         layout = Roo.factory(layout, Roo);
33162     }
33163     */
33164     
33165     
33166     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33167     
33168     layout.monitorWindowResize = false; // turn off autosizing
33169     this.layout = layout;
33170     this.layout.getEl().addClass("x-layout-nested-layout");
33171     
33172     
33173     
33174     
33175 };
33176
33177 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33178
33179     setSize : function(width, height){
33180         if(!this.ignoreResize(width, height)){
33181             var size = this.adjustForComponents(width, height);
33182             var el = this.layout.getEl();
33183             el.setSize(size.width, size.height);
33184             var touch = el.dom.offsetWidth;
33185             this.layout.layout();
33186             // ie requires a double layout on the first pass
33187             if(Roo.isIE && !this.initialized){
33188                 this.initialized = true;
33189                 this.layout.layout();
33190             }
33191         }
33192     },
33193     
33194     // activate all subpanels if not currently active..
33195     
33196     setActiveState : function(active){
33197         this.active = active;
33198         if(!active){
33199             this.fireEvent("deactivate", this);
33200             return;
33201         }
33202         
33203         this.fireEvent("activate", this);
33204         // not sure if this should happen before or after..
33205         if (!this.layout) {
33206             return; // should not happen..
33207         }
33208         var reg = false;
33209         for (var r in this.layout.regions) {
33210             reg = this.layout.getRegion(r);
33211             if (reg.getActivePanel()) {
33212                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33213                 reg.setActivePanel(reg.getActivePanel());
33214                 continue;
33215             }
33216             if (!reg.panels.length) {
33217                 continue;
33218             }
33219             reg.showPanel(reg.getPanel(0));
33220         }
33221         
33222         
33223         
33224         
33225     },
33226     
33227     /**
33228      * Returns the nested BorderLayout for this panel
33229      * @return {Roo.BorderLayout} 
33230      */
33231     getLayout : function(){
33232         return this.layout;
33233     },
33234     
33235      /**
33236      * Adds a xtype elements to the layout of the nested panel
33237      * <pre><code>
33238
33239 panel.addxtype({
33240        xtype : 'ContentPanel',
33241        region: 'west',
33242        items: [ .... ]
33243    }
33244 );
33245
33246 panel.addxtype({
33247         xtype : 'NestedLayoutPanel',
33248         region: 'west',
33249         layout: {
33250            center: { },
33251            west: { }   
33252         },
33253         items : [ ... list of content panels or nested layout panels.. ]
33254    }
33255 );
33256 </code></pre>
33257      * @param {Object} cfg Xtype definition of item to add.
33258      */
33259     addxtype : function(cfg) {
33260         return this.layout.addxtype(cfg);
33261     
33262     }
33263 });
33264
33265 Roo.ScrollPanel = function(el, config, content){
33266     config = config || {};
33267     config.fitToFrame = true;
33268     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33269     
33270     this.el.dom.style.overflow = "hidden";
33271     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33272     this.el.removeClass("x-layout-inactive-content");
33273     this.el.on("mousewheel", this.onWheel, this);
33274
33275     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33276     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33277     up.unselectable(); down.unselectable();
33278     up.on("click", this.scrollUp, this);
33279     down.on("click", this.scrollDown, this);
33280     up.addClassOnOver("x-scroller-btn-over");
33281     down.addClassOnOver("x-scroller-btn-over");
33282     up.addClassOnClick("x-scroller-btn-click");
33283     down.addClassOnClick("x-scroller-btn-click");
33284     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33285
33286     this.resizeEl = this.el;
33287     this.el = wrap; this.up = up; this.down = down;
33288 };
33289
33290 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33291     increment : 100,
33292     wheelIncrement : 5,
33293     scrollUp : function(){
33294         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33295     },
33296
33297     scrollDown : function(){
33298         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33299     },
33300
33301     afterScroll : function(){
33302         var el = this.resizeEl;
33303         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33304         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33305         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33306     },
33307
33308     setSize : function(){
33309         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33310         this.afterScroll();
33311     },
33312
33313     onWheel : function(e){
33314         var d = e.getWheelDelta();
33315         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33316         this.afterScroll();
33317         e.stopEvent();
33318     },
33319
33320     setContent : function(content, loadScripts){
33321         this.resizeEl.update(content, loadScripts);
33322     }
33323
33324 });
33325
33326
33327
33328
33329
33330
33331
33332
33333
33334 /**
33335  * @class Roo.TreePanel
33336  * @extends Roo.ContentPanel
33337  * @constructor
33338  * Create a new TreePanel. - defaults to fit/scoll contents.
33339  * @param {String/Object} config A string to set only the panel's title, or a config object
33340  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33341  */
33342 Roo.TreePanel = function(config){
33343     var el = config.el;
33344     var tree = config.tree;
33345     delete config.tree; 
33346     delete config.el; // hopefull!
33347     
33348     // wrapper for IE7 strict & safari scroll issue
33349     
33350     var treeEl = el.createChild();
33351     config.resizeEl = treeEl;
33352     
33353     
33354     
33355     Roo.TreePanel.superclass.constructor.call(this, el, config);
33356  
33357  
33358     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33359     //console.log(tree);
33360     this.on('activate', function()
33361     {
33362         if (this.tree.rendered) {
33363             return;
33364         }
33365         //console.log('render tree');
33366         this.tree.render();
33367     });
33368     
33369     this.on('resize',  function (cp, w, h) {
33370             this.tree.innerCt.setWidth(w);
33371             this.tree.innerCt.setHeight(h);
33372             this.tree.innerCt.setStyle('overflow-y', 'auto');
33373     });
33374
33375         
33376     
33377 };
33378
33379 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33380     fitToFrame : true,
33381     autoScroll : true
33382 });
33383
33384
33385
33386
33387
33388
33389
33390
33391
33392
33393
33394 /*
33395  * Based on:
33396  * Ext JS Library 1.1.1
33397  * Copyright(c) 2006-2007, Ext JS, LLC.
33398  *
33399  * Originally Released Under LGPL - original licence link has changed is not relivant.
33400  *
33401  * Fork - LGPL
33402  * <script type="text/javascript">
33403  */
33404  
33405
33406 /**
33407  * @class Roo.ReaderLayout
33408  * @extends Roo.BorderLayout
33409  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33410  * center region containing two nested regions (a top one for a list view and one for item preview below),
33411  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33412  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33413  * expedites the setup of the overall layout and regions for this common application style.
33414  * Example:
33415  <pre><code>
33416 var reader = new Roo.ReaderLayout();
33417 var CP = Roo.ContentPanel;  // shortcut for adding
33418
33419 reader.beginUpdate();
33420 reader.add("north", new CP("north", "North"));
33421 reader.add("west", new CP("west", {title: "West"}));
33422 reader.add("east", new CP("east", {title: "East"}));
33423
33424 reader.regions.listView.add(new CP("listView", "List"));
33425 reader.regions.preview.add(new CP("preview", "Preview"));
33426 reader.endUpdate();
33427 </code></pre>
33428 * @constructor
33429 * Create a new ReaderLayout
33430 * @param {Object} config Configuration options
33431 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33432 * document.body if omitted)
33433 */
33434 Roo.ReaderLayout = function(config, renderTo){
33435     var c = config || {size:{}};
33436     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33437         north: c.north !== false ? Roo.apply({
33438             split:false,
33439             initialSize: 32,
33440             titlebar: false
33441         }, c.north) : false,
33442         west: c.west !== false ? Roo.apply({
33443             split:true,
33444             initialSize: 200,
33445             minSize: 175,
33446             maxSize: 400,
33447             titlebar: true,
33448             collapsible: true,
33449             animate: true,
33450             margins:{left:5,right:0,bottom:5,top:5},
33451             cmargins:{left:5,right:5,bottom:5,top:5}
33452         }, c.west) : false,
33453         east: c.east !== false ? Roo.apply({
33454             split:true,
33455             initialSize: 200,
33456             minSize: 175,
33457             maxSize: 400,
33458             titlebar: true,
33459             collapsible: true,
33460             animate: true,
33461             margins:{left:0,right:5,bottom:5,top:5},
33462             cmargins:{left:5,right:5,bottom:5,top:5}
33463         }, c.east) : false,
33464         center: Roo.apply({
33465             tabPosition: 'top',
33466             autoScroll:false,
33467             closeOnTab: true,
33468             titlebar:false,
33469             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33470         }, c.center)
33471     });
33472
33473     this.el.addClass('x-reader');
33474
33475     this.beginUpdate();
33476
33477     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33478         south: c.preview !== false ? Roo.apply({
33479             split:true,
33480             initialSize: 200,
33481             minSize: 100,
33482             autoScroll:true,
33483             collapsible:true,
33484             titlebar: true,
33485             cmargins:{top:5,left:0, right:0, bottom:0}
33486         }, c.preview) : false,
33487         center: Roo.apply({
33488             autoScroll:false,
33489             titlebar:false,
33490             minHeight:200
33491         }, c.listView)
33492     });
33493     this.add('center', new Roo.NestedLayoutPanel(inner,
33494             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33495
33496     this.endUpdate();
33497
33498     this.regions.preview = inner.getRegion('south');
33499     this.regions.listView = inner.getRegion('center');
33500 };
33501
33502 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33503  * Based on:
33504  * Ext JS Library 1.1.1
33505  * Copyright(c) 2006-2007, Ext JS, LLC.
33506  *
33507  * Originally Released Under LGPL - original licence link has changed is not relivant.
33508  *
33509  * Fork - LGPL
33510  * <script type="text/javascript">
33511  */
33512  
33513 /**
33514  * @class Roo.grid.Grid
33515  * @extends Roo.util.Observable
33516  * This class represents the primary interface of a component based grid control.
33517  * <br><br>Usage:<pre><code>
33518  var grid = new Roo.grid.Grid("my-container-id", {
33519      ds: myDataStore,
33520      cm: myColModel,
33521      selModel: mySelectionModel,
33522      autoSizeColumns: true,
33523      monitorWindowResize: false,
33524      trackMouseOver: true
33525  });
33526  // set any options
33527  grid.render();
33528  * </code></pre>
33529  * <b>Common Problems:</b><br/>
33530  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33531  * element will correct this<br/>
33532  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33533  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33534  * are unpredictable.<br/>
33535  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33536  * grid to calculate dimensions/offsets.<br/>
33537   * @constructor
33538  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33539  * The container MUST have some type of size defined for the grid to fill. The container will be
33540  * automatically set to position relative if it isn't already.
33541  * @param {Object} config A config object that sets properties on this grid.
33542  */
33543 Roo.grid.Grid = function(container, config){
33544         // initialize the container
33545         this.container = Roo.get(container);
33546         this.container.update("");
33547         this.container.setStyle("overflow", "hidden");
33548     this.container.addClass('x-grid-container');
33549
33550     this.id = this.container.id;
33551
33552     Roo.apply(this, config);
33553     // check and correct shorthanded configs
33554     if(this.ds){
33555         this.dataSource = this.ds;
33556         delete this.ds;
33557     }
33558     if(this.cm){
33559         this.colModel = this.cm;
33560         delete this.cm;
33561     }
33562     if(this.sm){
33563         this.selModel = this.sm;
33564         delete this.sm;
33565     }
33566
33567     if (this.selModel) {
33568         this.selModel = Roo.factory(this.selModel, Roo.grid);
33569         this.sm = this.selModel;
33570         this.sm.xmodule = this.xmodule || false;
33571     }
33572     if (typeof(this.colModel.config) == 'undefined') {
33573         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33574         this.cm = this.colModel;
33575         this.cm.xmodule = this.xmodule || false;
33576     }
33577     if (this.dataSource) {
33578         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33579         this.ds = this.dataSource;
33580         this.ds.xmodule = this.xmodule || false;
33581          
33582     }
33583     
33584     
33585     
33586     if(this.width){
33587         this.container.setWidth(this.width);
33588     }
33589
33590     if(this.height){
33591         this.container.setHeight(this.height);
33592     }
33593     /** @private */
33594         this.addEvents({
33595         // raw events
33596         /**
33597          * @event click
33598          * The raw click event for the entire grid.
33599          * @param {Roo.EventObject} e
33600          */
33601         "click" : true,
33602         /**
33603          * @event dblclick
33604          * The raw dblclick event for the entire grid.
33605          * @param {Roo.EventObject} e
33606          */
33607         "dblclick" : true,
33608         /**
33609          * @event contextmenu
33610          * The raw contextmenu event for the entire grid.
33611          * @param {Roo.EventObject} e
33612          */
33613         "contextmenu" : true,
33614         /**
33615          * @event mousedown
33616          * The raw mousedown event for the entire grid.
33617          * @param {Roo.EventObject} e
33618          */
33619         "mousedown" : true,
33620         /**
33621          * @event mouseup
33622          * The raw mouseup event for the entire grid.
33623          * @param {Roo.EventObject} e
33624          */
33625         "mouseup" : true,
33626         /**
33627          * @event mouseover
33628          * The raw mouseover event for the entire grid.
33629          * @param {Roo.EventObject} e
33630          */
33631         "mouseover" : true,
33632         /**
33633          * @event mouseout
33634          * The raw mouseout event for the entire grid.
33635          * @param {Roo.EventObject} e
33636          */
33637         "mouseout" : true,
33638         /**
33639          * @event keypress
33640          * The raw keypress event for the entire grid.
33641          * @param {Roo.EventObject} e
33642          */
33643         "keypress" : true,
33644         /**
33645          * @event keydown
33646          * The raw keydown event for the entire grid.
33647          * @param {Roo.EventObject} e
33648          */
33649         "keydown" : true,
33650
33651         // custom events
33652
33653         /**
33654          * @event cellclick
33655          * Fires when a cell is clicked
33656          * @param {Grid} this
33657          * @param {Number} rowIndex
33658          * @param {Number} columnIndex
33659          * @param {Roo.EventObject} e
33660          */
33661         "cellclick" : true,
33662         /**
33663          * @event celldblclick
33664          * Fires when a cell is double clicked
33665          * @param {Grid} this
33666          * @param {Number} rowIndex
33667          * @param {Number} columnIndex
33668          * @param {Roo.EventObject} e
33669          */
33670         "celldblclick" : true,
33671         /**
33672          * @event rowclick
33673          * Fires when a row is clicked
33674          * @param {Grid} this
33675          * @param {Number} rowIndex
33676          * @param {Roo.EventObject} e
33677          */
33678         "rowclick" : true,
33679         /**
33680          * @event rowdblclick
33681          * Fires when a row is double clicked
33682          * @param {Grid} this
33683          * @param {Number} rowIndex
33684          * @param {Roo.EventObject} e
33685          */
33686         "rowdblclick" : true,
33687         /**
33688          * @event headerclick
33689          * Fires when a header is clicked
33690          * @param {Grid} this
33691          * @param {Number} columnIndex
33692          * @param {Roo.EventObject} e
33693          */
33694         "headerclick" : true,
33695         /**
33696          * @event headerdblclick
33697          * Fires when a header cell is double clicked
33698          * @param {Grid} this
33699          * @param {Number} columnIndex
33700          * @param {Roo.EventObject} e
33701          */
33702         "headerdblclick" : true,
33703         /**
33704          * @event rowcontextmenu
33705          * Fires when a row is right clicked
33706          * @param {Grid} this
33707          * @param {Number} rowIndex
33708          * @param {Roo.EventObject} e
33709          */
33710         "rowcontextmenu" : true,
33711         /**
33712          * @event cellcontextmenu
33713          * Fires when a cell is right clicked
33714          * @param {Grid} this
33715          * @param {Number} rowIndex
33716          * @param {Number} cellIndex
33717          * @param {Roo.EventObject} e
33718          */
33719          "cellcontextmenu" : true,
33720         /**
33721          * @event headercontextmenu
33722          * Fires when a header is right clicked
33723          * @param {Grid} this
33724          * @param {Number} columnIndex
33725          * @param {Roo.EventObject} e
33726          */
33727         "headercontextmenu" : true,
33728         /**
33729          * @event bodyscroll
33730          * Fires when the body element is scrolled
33731          * @param {Number} scrollLeft
33732          * @param {Number} scrollTop
33733          */
33734         "bodyscroll" : true,
33735         /**
33736          * @event columnresize
33737          * Fires when the user resizes a column
33738          * @param {Number} columnIndex
33739          * @param {Number} newSize
33740          */
33741         "columnresize" : true,
33742         /**
33743          * @event columnmove
33744          * Fires when the user moves a column
33745          * @param {Number} oldIndex
33746          * @param {Number} newIndex
33747          */
33748         "columnmove" : true,
33749         /**
33750          * @event startdrag
33751          * Fires when row(s) start being dragged
33752          * @param {Grid} this
33753          * @param {Roo.GridDD} dd The drag drop object
33754          * @param {event} e The raw browser event
33755          */
33756         "startdrag" : true,
33757         /**
33758          * @event enddrag
33759          * Fires when a drag operation is complete
33760          * @param {Grid} this
33761          * @param {Roo.GridDD} dd The drag drop object
33762          * @param {event} e The raw browser event
33763          */
33764         "enddrag" : true,
33765         /**
33766          * @event dragdrop
33767          * Fires when dragged row(s) are dropped on a valid DD target
33768          * @param {Grid} this
33769          * @param {Roo.GridDD} dd The drag drop object
33770          * @param {String} targetId The target drag drop object
33771          * @param {event} e The raw browser event
33772          */
33773         "dragdrop" : true,
33774         /**
33775          * @event dragover
33776          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33777          * @param {Grid} this
33778          * @param {Roo.GridDD} dd The drag drop object
33779          * @param {String} targetId The target drag drop object
33780          * @param {event} e The raw browser event
33781          */
33782         "dragover" : true,
33783         /**
33784          * @event dragenter
33785          *  Fires when the dragged row(s) first cross another DD target while being dragged
33786          * @param {Grid} this
33787          * @param {Roo.GridDD} dd The drag drop object
33788          * @param {String} targetId The target drag drop object
33789          * @param {event} e The raw browser event
33790          */
33791         "dragenter" : true,
33792         /**
33793          * @event dragout
33794          * Fires when the dragged row(s) leave another DD target while being dragged
33795          * @param {Grid} this
33796          * @param {Roo.GridDD} dd The drag drop object
33797          * @param {String} targetId The target drag drop object
33798          * @param {event} e The raw browser event
33799          */
33800         "dragout" : true,
33801         /**
33802          * @event rowclass
33803          * Fires when a row is rendered, so you can change add a style to it.
33804          * @param {GridView} gridview   The grid view
33805          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33806          */
33807         'rowclass' : true,
33808
33809         /**
33810          * @event render
33811          * Fires when the grid is rendered
33812          * @param {Grid} grid
33813          */
33814         'render' : true
33815     });
33816
33817     Roo.grid.Grid.superclass.constructor.call(this);
33818 };
33819 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33820     
33821     /**
33822      * @cfg {String} ddGroup - drag drop group.
33823      */
33824
33825     /**
33826      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33827      */
33828     minColumnWidth : 25,
33829
33830     /**
33831      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33832      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33833      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33834      */
33835     autoSizeColumns : false,
33836
33837     /**
33838      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33839      */
33840     autoSizeHeaders : true,
33841
33842     /**
33843      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33844      */
33845     monitorWindowResize : true,
33846
33847     /**
33848      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33849      * rows measured to get a columns size. Default is 0 (all rows).
33850      */
33851     maxRowsToMeasure : 0,
33852
33853     /**
33854      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33855      */
33856     trackMouseOver : true,
33857
33858     /**
33859     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33860     */
33861     
33862     /**
33863     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33864     */
33865     enableDragDrop : false,
33866     
33867     /**
33868     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33869     */
33870     enableColumnMove : true,
33871     
33872     /**
33873     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33874     */
33875     enableColumnHide : true,
33876     
33877     /**
33878     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33879     */
33880     enableRowHeightSync : false,
33881     
33882     /**
33883     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33884     */
33885     stripeRows : true,
33886     
33887     /**
33888     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33889     */
33890     autoHeight : false,
33891
33892     /**
33893      * @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.
33894      */
33895     autoExpandColumn : false,
33896
33897     /**
33898     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33899     * Default is 50.
33900     */
33901     autoExpandMin : 50,
33902
33903     /**
33904     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33905     */
33906     autoExpandMax : 1000,
33907
33908     /**
33909     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33910     */
33911     view : null,
33912
33913     /**
33914     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33915     */
33916     loadMask : false,
33917     /**
33918     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33919     */
33920     dropTarget: false,
33921     
33922    
33923     
33924     // private
33925     rendered : false,
33926
33927     /**
33928     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33929     * of a fixed width. Default is false.
33930     */
33931     /**
33932     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33933     */
33934     /**
33935      * Called once after all setup has been completed and the grid is ready to be rendered.
33936      * @return {Roo.grid.Grid} this
33937      */
33938     render : function()
33939     {
33940         var c = this.container;
33941         // try to detect autoHeight/width mode
33942         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33943             this.autoHeight = true;
33944         }
33945         var view = this.getView();
33946         view.init(this);
33947
33948         c.on("click", this.onClick, this);
33949         c.on("dblclick", this.onDblClick, this);
33950         c.on("contextmenu", this.onContextMenu, this);
33951         c.on("keydown", this.onKeyDown, this);
33952
33953         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33954
33955         this.getSelectionModel().init(this);
33956
33957         view.render();
33958
33959         if(this.loadMask){
33960             this.loadMask = new Roo.LoadMask(this.container,
33961                     Roo.apply({store:this.dataSource}, this.loadMask));
33962         }
33963         
33964         
33965         if (this.toolbar && this.toolbar.xtype) {
33966             this.toolbar.container = this.getView().getHeaderPanel(true);
33967             this.toolbar = new Roo.Toolbar(this.toolbar);
33968         }
33969         if (this.footer && this.footer.xtype) {
33970             this.footer.dataSource = this.getDataSource();
33971             this.footer.container = this.getView().getFooterPanel(true);
33972             this.footer = Roo.factory(this.footer, Roo);
33973         }
33974         if (this.dropTarget && this.dropTarget.xtype) {
33975             delete this.dropTarget.xtype;
33976             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33977         }
33978         
33979         
33980         this.rendered = true;
33981         this.fireEvent('render', this);
33982         return this;
33983     },
33984
33985         /**
33986          * Reconfigures the grid to use a different Store and Column Model.
33987          * The View will be bound to the new objects and refreshed.
33988          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33989          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33990          */
33991     reconfigure : function(dataSource, colModel){
33992         if(this.loadMask){
33993             this.loadMask.destroy();
33994             this.loadMask = new Roo.LoadMask(this.container,
33995                     Roo.apply({store:dataSource}, this.loadMask));
33996         }
33997         this.view.bind(dataSource, colModel);
33998         this.dataSource = dataSource;
33999         this.colModel = colModel;
34000         this.view.refresh(true);
34001     },
34002
34003     // private
34004     onKeyDown : function(e){
34005         this.fireEvent("keydown", e);
34006     },
34007
34008     /**
34009      * Destroy this grid.
34010      * @param {Boolean} removeEl True to remove the element
34011      */
34012     destroy : function(removeEl, keepListeners){
34013         if(this.loadMask){
34014             this.loadMask.destroy();
34015         }
34016         var c = this.container;
34017         c.removeAllListeners();
34018         this.view.destroy();
34019         this.colModel.purgeListeners();
34020         if(!keepListeners){
34021             this.purgeListeners();
34022         }
34023         c.update("");
34024         if(removeEl === true){
34025             c.remove();
34026         }
34027     },
34028
34029     // private
34030     processEvent : function(name, e){
34031         this.fireEvent(name, e);
34032         var t = e.getTarget();
34033         var v = this.view;
34034         var header = v.findHeaderIndex(t);
34035         if(header !== false){
34036             this.fireEvent("header" + name, this, header, e);
34037         }else{
34038             var row = v.findRowIndex(t);
34039             var cell = v.findCellIndex(t);
34040             if(row !== false){
34041                 this.fireEvent("row" + name, this, row, e);
34042                 if(cell !== false){
34043                     this.fireEvent("cell" + name, this, row, cell, e);
34044                 }
34045             }
34046         }
34047     },
34048
34049     // private
34050     onClick : function(e){
34051         this.processEvent("click", e);
34052     },
34053
34054     // private
34055     onContextMenu : function(e, t){
34056         this.processEvent("contextmenu", e);
34057     },
34058
34059     // private
34060     onDblClick : function(e){
34061         this.processEvent("dblclick", e);
34062     },
34063
34064     // private
34065     walkCells : function(row, col, step, fn, scope){
34066         var cm = this.colModel, clen = cm.getColumnCount();
34067         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34068         if(step < 0){
34069             if(col < 0){
34070                 row--;
34071                 first = false;
34072             }
34073             while(row >= 0){
34074                 if(!first){
34075                     col = clen-1;
34076                 }
34077                 first = false;
34078                 while(col >= 0){
34079                     if(fn.call(scope || this, row, col, cm) === true){
34080                         return [row, col];
34081                     }
34082                     col--;
34083                 }
34084                 row--;
34085             }
34086         } else {
34087             if(col >= clen){
34088                 row++;
34089                 first = false;
34090             }
34091             while(row < rlen){
34092                 if(!first){
34093                     col = 0;
34094                 }
34095                 first = false;
34096                 while(col < clen){
34097                     if(fn.call(scope || this, row, col, cm) === true){
34098                         return [row, col];
34099                     }
34100                     col++;
34101                 }
34102                 row++;
34103             }
34104         }
34105         return null;
34106     },
34107
34108     // private
34109     getSelections : function(){
34110         return this.selModel.getSelections();
34111     },
34112
34113     /**
34114      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34115      * but if manual update is required this method will initiate it.
34116      */
34117     autoSize : function(){
34118         if(this.rendered){
34119             this.view.layout();
34120             if(this.view.adjustForScroll){
34121                 this.view.adjustForScroll();
34122             }
34123         }
34124     },
34125
34126     /**
34127      * Returns the grid's underlying element.
34128      * @return {Element} The element
34129      */
34130     getGridEl : function(){
34131         return this.container;
34132     },
34133
34134     // private for compatibility, overridden by editor grid
34135     stopEditing : function(){},
34136
34137     /**
34138      * Returns the grid's SelectionModel.
34139      * @return {SelectionModel}
34140      */
34141     getSelectionModel : function(){
34142         if(!this.selModel){
34143             this.selModel = new Roo.grid.RowSelectionModel();
34144         }
34145         return this.selModel;
34146     },
34147
34148     /**
34149      * Returns the grid's DataSource.
34150      * @return {DataSource}
34151      */
34152     getDataSource : function(){
34153         return this.dataSource;
34154     },
34155
34156     /**
34157      * Returns the grid's ColumnModel.
34158      * @return {ColumnModel}
34159      */
34160     getColumnModel : function(){
34161         return this.colModel;
34162     },
34163
34164     /**
34165      * Returns the grid's GridView object.
34166      * @return {GridView}
34167      */
34168     getView : function(){
34169         if(!this.view){
34170             this.view = new Roo.grid.GridView(this.viewConfig);
34171         }
34172         return this.view;
34173     },
34174     /**
34175      * Called to get grid's drag proxy text, by default returns this.ddText.
34176      * @return {String}
34177      */
34178     getDragDropText : function(){
34179         var count = this.selModel.getCount();
34180         return String.format(this.ddText, count, count == 1 ? '' : 's');
34181     }
34182 });
34183 /**
34184  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34185  * %0 is replaced with the number of selected rows.
34186  * @type String
34187  */
34188 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34189  * Based on:
34190  * Ext JS Library 1.1.1
34191  * Copyright(c) 2006-2007, Ext JS, LLC.
34192  *
34193  * Originally Released Under LGPL - original licence link has changed is not relivant.
34194  *
34195  * Fork - LGPL
34196  * <script type="text/javascript">
34197  */
34198  
34199 Roo.grid.AbstractGridView = function(){
34200         this.grid = null;
34201         
34202         this.events = {
34203             "beforerowremoved" : true,
34204             "beforerowsinserted" : true,
34205             "beforerefresh" : true,
34206             "rowremoved" : true,
34207             "rowsinserted" : true,
34208             "rowupdated" : true,
34209             "refresh" : true
34210         };
34211     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34212 };
34213
34214 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34215     rowClass : "x-grid-row",
34216     cellClass : "x-grid-cell",
34217     tdClass : "x-grid-td",
34218     hdClass : "x-grid-hd",
34219     splitClass : "x-grid-hd-split",
34220     
34221         init: function(grid){
34222         this.grid = grid;
34223                 var cid = this.grid.getGridEl().id;
34224         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34225         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34226         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34227         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34228         },
34229         
34230         getColumnRenderers : function(){
34231         var renderers = [];
34232         var cm = this.grid.colModel;
34233         var colCount = cm.getColumnCount();
34234         for(var i = 0; i < colCount; i++){
34235             renderers[i] = cm.getRenderer(i);
34236         }
34237         return renderers;
34238     },
34239     
34240     getColumnIds : function(){
34241         var ids = [];
34242         var cm = this.grid.colModel;
34243         var colCount = cm.getColumnCount();
34244         for(var i = 0; i < colCount; i++){
34245             ids[i] = cm.getColumnId(i);
34246         }
34247         return ids;
34248     },
34249     
34250     getDataIndexes : function(){
34251         if(!this.indexMap){
34252             this.indexMap = this.buildIndexMap();
34253         }
34254         return this.indexMap.colToData;
34255     },
34256     
34257     getColumnIndexByDataIndex : function(dataIndex){
34258         if(!this.indexMap){
34259             this.indexMap = this.buildIndexMap();
34260         }
34261         return this.indexMap.dataToCol[dataIndex];
34262     },
34263     
34264     /**
34265      * Set a css style for a column dynamically. 
34266      * @param {Number} colIndex The index of the column
34267      * @param {String} name The css property name
34268      * @param {String} value The css value
34269      */
34270     setCSSStyle : function(colIndex, name, value){
34271         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34272         Roo.util.CSS.updateRule(selector, name, value);
34273     },
34274     
34275     generateRules : function(cm){
34276         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34277         Roo.util.CSS.removeStyleSheet(rulesId);
34278         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34279             var cid = cm.getColumnId(i);
34280             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34281                          this.tdSelector, cid, " {\n}\n",
34282                          this.hdSelector, cid, " {\n}\n",
34283                          this.splitSelector, cid, " {\n}\n");
34284         }
34285         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34286     }
34287 });/*
34288  * Based on:
34289  * Ext JS Library 1.1.1
34290  * Copyright(c) 2006-2007, Ext JS, LLC.
34291  *
34292  * Originally Released Under LGPL - original licence link has changed is not relivant.
34293  *
34294  * Fork - LGPL
34295  * <script type="text/javascript">
34296  */
34297
34298 // private
34299 // This is a support class used internally by the Grid components
34300 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34301     this.grid = grid;
34302     this.view = grid.getView();
34303     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34304     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34305     if(hd2){
34306         this.setHandleElId(Roo.id(hd));
34307         this.setOuterHandleElId(Roo.id(hd2));
34308     }
34309     this.scroll = false;
34310 };
34311 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34312     maxDragWidth: 120,
34313     getDragData : function(e){
34314         var t = Roo.lib.Event.getTarget(e);
34315         var h = this.view.findHeaderCell(t);
34316         if(h){
34317             return {ddel: h.firstChild, header:h};
34318         }
34319         return false;
34320     },
34321
34322     onInitDrag : function(e){
34323         this.view.headersDisabled = true;
34324         var clone = this.dragData.ddel.cloneNode(true);
34325         clone.id = Roo.id();
34326         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34327         this.proxy.update(clone);
34328         return true;
34329     },
34330
34331     afterValidDrop : function(){
34332         var v = this.view;
34333         setTimeout(function(){
34334             v.headersDisabled = false;
34335         }, 50);
34336     },
34337
34338     afterInvalidDrop : function(){
34339         var v = this.view;
34340         setTimeout(function(){
34341             v.headersDisabled = false;
34342         }, 50);
34343     }
34344 });
34345 /*
34346  * Based on:
34347  * Ext JS Library 1.1.1
34348  * Copyright(c) 2006-2007, Ext JS, LLC.
34349  *
34350  * Originally Released Under LGPL - original licence link has changed is not relivant.
34351  *
34352  * Fork - LGPL
34353  * <script type="text/javascript">
34354  */
34355 // private
34356 // This is a support class used internally by the Grid components
34357 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34358     this.grid = grid;
34359     this.view = grid.getView();
34360     // split the proxies so they don't interfere with mouse events
34361     this.proxyTop = Roo.DomHelper.append(document.body, {
34362         cls:"col-move-top", html:"&#160;"
34363     }, true);
34364     this.proxyBottom = Roo.DomHelper.append(document.body, {
34365         cls:"col-move-bottom", html:"&#160;"
34366     }, true);
34367     this.proxyTop.hide = this.proxyBottom.hide = function(){
34368         this.setLeftTop(-100,-100);
34369         this.setStyle("visibility", "hidden");
34370     };
34371     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34372     // temporarily disabled
34373     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34374     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34375 };
34376 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34377     proxyOffsets : [-4, -9],
34378     fly: Roo.Element.fly,
34379
34380     getTargetFromEvent : function(e){
34381         var t = Roo.lib.Event.getTarget(e);
34382         var cindex = this.view.findCellIndex(t);
34383         if(cindex !== false){
34384             return this.view.getHeaderCell(cindex);
34385         }
34386         return null;
34387     },
34388
34389     nextVisible : function(h){
34390         var v = this.view, cm = this.grid.colModel;
34391         h = h.nextSibling;
34392         while(h){
34393             if(!cm.isHidden(v.getCellIndex(h))){
34394                 return h;
34395             }
34396             h = h.nextSibling;
34397         }
34398         return null;
34399     },
34400
34401     prevVisible : function(h){
34402         var v = this.view, cm = this.grid.colModel;
34403         h = h.prevSibling;
34404         while(h){
34405             if(!cm.isHidden(v.getCellIndex(h))){
34406                 return h;
34407             }
34408             h = h.prevSibling;
34409         }
34410         return null;
34411     },
34412
34413     positionIndicator : function(h, n, e){
34414         var x = Roo.lib.Event.getPageX(e);
34415         var r = Roo.lib.Dom.getRegion(n.firstChild);
34416         var px, pt, py = r.top + this.proxyOffsets[1];
34417         if((r.right - x) <= (r.right-r.left)/2){
34418             px = r.right+this.view.borderWidth;
34419             pt = "after";
34420         }else{
34421             px = r.left;
34422             pt = "before";
34423         }
34424         var oldIndex = this.view.getCellIndex(h);
34425         var newIndex = this.view.getCellIndex(n);
34426
34427         if(this.grid.colModel.isFixed(newIndex)){
34428             return false;
34429         }
34430
34431         var locked = this.grid.colModel.isLocked(newIndex);
34432
34433         if(pt == "after"){
34434             newIndex++;
34435         }
34436         if(oldIndex < newIndex){
34437             newIndex--;
34438         }
34439         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34440             return false;
34441         }
34442         px +=  this.proxyOffsets[0];
34443         this.proxyTop.setLeftTop(px, py);
34444         this.proxyTop.show();
34445         if(!this.bottomOffset){
34446             this.bottomOffset = this.view.mainHd.getHeight();
34447         }
34448         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34449         this.proxyBottom.show();
34450         return pt;
34451     },
34452
34453     onNodeEnter : function(n, dd, e, data){
34454         if(data.header != n){
34455             this.positionIndicator(data.header, n, e);
34456         }
34457     },
34458
34459     onNodeOver : function(n, dd, e, data){
34460         var result = false;
34461         if(data.header != n){
34462             result = this.positionIndicator(data.header, n, e);
34463         }
34464         if(!result){
34465             this.proxyTop.hide();
34466             this.proxyBottom.hide();
34467         }
34468         return result ? this.dropAllowed : this.dropNotAllowed;
34469     },
34470
34471     onNodeOut : function(n, dd, e, data){
34472         this.proxyTop.hide();
34473         this.proxyBottom.hide();
34474     },
34475
34476     onNodeDrop : function(n, dd, e, data){
34477         var h = data.header;
34478         if(h != n){
34479             var cm = this.grid.colModel;
34480             var x = Roo.lib.Event.getPageX(e);
34481             var r = Roo.lib.Dom.getRegion(n.firstChild);
34482             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34483             var oldIndex = this.view.getCellIndex(h);
34484             var newIndex = this.view.getCellIndex(n);
34485             var locked = cm.isLocked(newIndex);
34486             if(pt == "after"){
34487                 newIndex++;
34488             }
34489             if(oldIndex < newIndex){
34490                 newIndex--;
34491             }
34492             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34493                 return false;
34494             }
34495             cm.setLocked(oldIndex, locked, true);
34496             cm.moveColumn(oldIndex, newIndex);
34497             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34498             return true;
34499         }
34500         return false;
34501     }
34502 });
34503 /*
34504  * Based on:
34505  * Ext JS Library 1.1.1
34506  * Copyright(c) 2006-2007, Ext JS, LLC.
34507  *
34508  * Originally Released Under LGPL - original licence link has changed is not relivant.
34509  *
34510  * Fork - LGPL
34511  * <script type="text/javascript">
34512  */
34513   
34514 /**
34515  * @class Roo.grid.GridView
34516  * @extends Roo.util.Observable
34517  *
34518  * @constructor
34519  * @param {Object} config
34520  */
34521 Roo.grid.GridView = function(config){
34522     Roo.grid.GridView.superclass.constructor.call(this);
34523     this.el = null;
34524
34525     Roo.apply(this, config);
34526 };
34527
34528 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34529
34530     
34531     rowClass : "x-grid-row",
34532
34533     cellClass : "x-grid-col",
34534
34535     tdClass : "x-grid-td",
34536
34537     hdClass : "x-grid-hd",
34538
34539     splitClass : "x-grid-split",
34540
34541     sortClasses : ["sort-asc", "sort-desc"],
34542
34543     enableMoveAnim : false,
34544
34545     hlColor: "C3DAF9",
34546
34547     dh : Roo.DomHelper,
34548
34549     fly : Roo.Element.fly,
34550
34551     css : Roo.util.CSS,
34552
34553     borderWidth: 1,
34554
34555     splitOffset: 3,
34556
34557     scrollIncrement : 22,
34558
34559     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34560
34561     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34562
34563     bind : function(ds, cm){
34564         if(this.ds){
34565             this.ds.un("load", this.onLoad, this);
34566             this.ds.un("datachanged", this.onDataChange, this);
34567             this.ds.un("add", this.onAdd, this);
34568             this.ds.un("remove", this.onRemove, this);
34569             this.ds.un("update", this.onUpdate, this);
34570             this.ds.un("clear", this.onClear, this);
34571         }
34572         if(ds){
34573             ds.on("load", this.onLoad, this);
34574             ds.on("datachanged", this.onDataChange, this);
34575             ds.on("add", this.onAdd, this);
34576             ds.on("remove", this.onRemove, this);
34577             ds.on("update", this.onUpdate, this);
34578             ds.on("clear", this.onClear, this);
34579         }
34580         this.ds = ds;
34581
34582         if(this.cm){
34583             this.cm.un("widthchange", this.onColWidthChange, this);
34584             this.cm.un("headerchange", this.onHeaderChange, this);
34585             this.cm.un("hiddenchange", this.onHiddenChange, this);
34586             this.cm.un("columnmoved", this.onColumnMove, this);
34587             this.cm.un("columnlockchange", this.onColumnLock, this);
34588         }
34589         if(cm){
34590             this.generateRules(cm);
34591             cm.on("widthchange", this.onColWidthChange, this);
34592             cm.on("headerchange", this.onHeaderChange, this);
34593             cm.on("hiddenchange", this.onHiddenChange, this);
34594             cm.on("columnmoved", this.onColumnMove, this);
34595             cm.on("columnlockchange", this.onColumnLock, this);
34596         }
34597         this.cm = cm;
34598     },
34599
34600     init: function(grid){
34601         Roo.grid.GridView.superclass.init.call(this, grid);
34602
34603         this.bind(grid.dataSource, grid.colModel);
34604
34605         grid.on("headerclick", this.handleHeaderClick, this);
34606
34607         if(grid.trackMouseOver){
34608             grid.on("mouseover", this.onRowOver, this);
34609             grid.on("mouseout", this.onRowOut, this);
34610         }
34611         grid.cancelTextSelection = function(){};
34612         this.gridId = grid.id;
34613
34614         var tpls = this.templates || {};
34615
34616         if(!tpls.master){
34617             tpls.master = new Roo.Template(
34618                '<div class="x-grid" hidefocus="true">',
34619                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34620                   '<div class="x-grid-topbar"></div>',
34621                   '<div class="x-grid-scroller"><div></div></div>',
34622                   '<div class="x-grid-locked">',
34623                       '<div class="x-grid-header">{lockedHeader}</div>',
34624                       '<div class="x-grid-body">{lockedBody}</div>',
34625                   "</div>",
34626                   '<div class="x-grid-viewport">',
34627                       '<div class="x-grid-header">{header}</div>',
34628                       '<div class="x-grid-body">{body}</div>',
34629                   "</div>",
34630                   '<div class="x-grid-bottombar"></div>',
34631                  
34632                   '<div class="x-grid-resize-proxy">&#160;</div>',
34633                "</div>"
34634             );
34635             tpls.master.disableformats = true;
34636         }
34637
34638         if(!tpls.header){
34639             tpls.header = new Roo.Template(
34640                '<table border="0" cellspacing="0" cellpadding="0">',
34641                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34642                "</table>{splits}"
34643             );
34644             tpls.header.disableformats = true;
34645         }
34646         tpls.header.compile();
34647
34648         if(!tpls.hcell){
34649             tpls.hcell = new Roo.Template(
34650                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34651                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34652                 "</div></td>"
34653              );
34654              tpls.hcell.disableFormats = true;
34655         }
34656         tpls.hcell.compile();
34657
34658         if(!tpls.hsplit){
34659             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34660             tpls.hsplit.disableFormats = true;
34661         }
34662         tpls.hsplit.compile();
34663
34664         if(!tpls.body){
34665             tpls.body = new Roo.Template(
34666                '<table border="0" cellspacing="0" cellpadding="0">',
34667                "<tbody>{rows}</tbody>",
34668                "</table>"
34669             );
34670             tpls.body.disableFormats = true;
34671         }
34672         tpls.body.compile();
34673
34674         if(!tpls.row){
34675             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34676             tpls.row.disableFormats = true;
34677         }
34678         tpls.row.compile();
34679
34680         if(!tpls.cell){
34681             tpls.cell = new Roo.Template(
34682                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34683                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34684                 "</td>"
34685             );
34686             tpls.cell.disableFormats = true;
34687         }
34688         tpls.cell.compile();
34689
34690         this.templates = tpls;
34691     },
34692
34693     // remap these for backwards compat
34694     onColWidthChange : function(){
34695         this.updateColumns.apply(this, arguments);
34696     },
34697     onHeaderChange : function(){
34698         this.updateHeaders.apply(this, arguments);
34699     }, 
34700     onHiddenChange : function(){
34701         this.handleHiddenChange.apply(this, arguments);
34702     },
34703     onColumnMove : function(){
34704         this.handleColumnMove.apply(this, arguments);
34705     },
34706     onColumnLock : function(){
34707         this.handleLockChange.apply(this, arguments);
34708     },
34709
34710     onDataChange : function(){
34711         this.refresh();
34712         this.updateHeaderSortState();
34713     },
34714
34715     onClear : function(){
34716         this.refresh();
34717     },
34718
34719     onUpdate : function(ds, record){
34720         this.refreshRow(record);
34721     },
34722
34723     refreshRow : function(record){
34724         var ds = this.ds, index;
34725         if(typeof record == 'number'){
34726             index = record;
34727             record = ds.getAt(index);
34728         }else{
34729             index = ds.indexOf(record);
34730         }
34731         this.insertRows(ds, index, index, true);
34732         this.onRemove(ds, record, index+1, true);
34733         this.syncRowHeights(index, index);
34734         this.layout();
34735         this.fireEvent("rowupdated", this, index, record);
34736     },
34737
34738     onAdd : function(ds, records, index){
34739         this.insertRows(ds, index, index + (records.length-1));
34740     },
34741
34742     onRemove : function(ds, record, index, isUpdate){
34743         if(isUpdate !== true){
34744             this.fireEvent("beforerowremoved", this, index, record);
34745         }
34746         var bt = this.getBodyTable(), lt = this.getLockedTable();
34747         if(bt.rows[index]){
34748             bt.firstChild.removeChild(bt.rows[index]);
34749         }
34750         if(lt.rows[index]){
34751             lt.firstChild.removeChild(lt.rows[index]);
34752         }
34753         if(isUpdate !== true){
34754             this.stripeRows(index);
34755             this.syncRowHeights(index, index);
34756             this.layout();
34757             this.fireEvent("rowremoved", this, index, record);
34758         }
34759     },
34760
34761     onLoad : function(){
34762         this.scrollToTop();
34763     },
34764
34765     /**
34766      * Scrolls the grid to the top
34767      */
34768     scrollToTop : function(){
34769         if(this.scroller){
34770             this.scroller.dom.scrollTop = 0;
34771             this.syncScroll();
34772         }
34773     },
34774
34775     /**
34776      * Gets a panel in the header of the grid that can be used for toolbars etc.
34777      * After modifying the contents of this panel a call to grid.autoSize() may be
34778      * required to register any changes in size.
34779      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34780      * @return Roo.Element
34781      */
34782     getHeaderPanel : function(doShow){
34783         if(doShow){
34784             this.headerPanel.show();
34785         }
34786         return this.headerPanel;
34787     },
34788
34789     /**
34790      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34791      * After modifying the contents of this panel a call to grid.autoSize() may be
34792      * required to register any changes in size.
34793      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34794      * @return Roo.Element
34795      */
34796     getFooterPanel : function(doShow){
34797         if(doShow){
34798             this.footerPanel.show();
34799         }
34800         return this.footerPanel;
34801     },
34802
34803     initElements : function(){
34804         var E = Roo.Element;
34805         var el = this.grid.getGridEl().dom.firstChild;
34806         var cs = el.childNodes;
34807
34808         this.el = new E(el);
34809         
34810          this.focusEl = new E(el.firstChild);
34811         this.focusEl.swallowEvent("click", true);
34812         
34813         this.headerPanel = new E(cs[1]);
34814         this.headerPanel.enableDisplayMode("block");
34815
34816         this.scroller = new E(cs[2]);
34817         this.scrollSizer = new E(this.scroller.dom.firstChild);
34818
34819         this.lockedWrap = new E(cs[3]);
34820         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34821         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34822
34823         this.mainWrap = new E(cs[4]);
34824         this.mainHd = new E(this.mainWrap.dom.firstChild);
34825         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34826
34827         this.footerPanel = new E(cs[5]);
34828         this.footerPanel.enableDisplayMode("block");
34829
34830         this.resizeProxy = new E(cs[6]);
34831
34832         this.headerSelector = String.format(
34833            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34834            this.lockedHd.id, this.mainHd.id
34835         );
34836
34837         this.splitterSelector = String.format(
34838            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34839            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34840         );
34841     },
34842     idToCssName : function(s)
34843     {
34844         return s.replace(/[^a-z0-9]+/ig, '-');
34845     },
34846
34847     getHeaderCell : function(index){
34848         return Roo.DomQuery.select(this.headerSelector)[index];
34849     },
34850
34851     getHeaderCellMeasure : function(index){
34852         return this.getHeaderCell(index).firstChild;
34853     },
34854
34855     getHeaderCellText : function(index){
34856         return this.getHeaderCell(index).firstChild.firstChild;
34857     },
34858
34859     getLockedTable : function(){
34860         return this.lockedBody.dom.firstChild;
34861     },
34862
34863     getBodyTable : function(){
34864         return this.mainBody.dom.firstChild;
34865     },
34866
34867     getLockedRow : function(index){
34868         return this.getLockedTable().rows[index];
34869     },
34870
34871     getRow : function(index){
34872         return this.getBodyTable().rows[index];
34873     },
34874
34875     getRowComposite : function(index){
34876         if(!this.rowEl){
34877             this.rowEl = new Roo.CompositeElementLite();
34878         }
34879         var els = [], lrow, mrow;
34880         if(lrow = this.getLockedRow(index)){
34881             els.push(lrow);
34882         }
34883         if(mrow = this.getRow(index)){
34884             els.push(mrow);
34885         }
34886         this.rowEl.elements = els;
34887         return this.rowEl;
34888     },
34889     /**
34890      * Gets the 'td' of the cell
34891      * 
34892      * @param {Integer} rowIndex row to select
34893      * @param {Integer} colIndex column to select
34894      * 
34895      * @return {Object} 
34896      */
34897     getCell : function(rowIndex, colIndex){
34898         var locked = this.cm.getLockedCount();
34899         var source;
34900         if(colIndex < locked){
34901             source = this.lockedBody.dom.firstChild;
34902         }else{
34903             source = this.mainBody.dom.firstChild;
34904             colIndex -= locked;
34905         }
34906         return source.rows[rowIndex].childNodes[colIndex];
34907     },
34908
34909     getCellText : function(rowIndex, colIndex){
34910         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34911     },
34912
34913     getCellBox : function(cell){
34914         var b = this.fly(cell).getBox();
34915         if(Roo.isOpera){ // opera fails to report the Y
34916             b.y = cell.offsetTop + this.mainBody.getY();
34917         }
34918         return b;
34919     },
34920
34921     getCellIndex : function(cell){
34922         var id = String(cell.className).match(this.cellRE);
34923         if(id){
34924             return parseInt(id[1], 10);
34925         }
34926         return 0;
34927     },
34928
34929     findHeaderIndex : function(n){
34930         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34931         return r ? this.getCellIndex(r) : false;
34932     },
34933
34934     findHeaderCell : function(n){
34935         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34936         return r ? r : false;
34937     },
34938
34939     findRowIndex : function(n){
34940         if(!n){
34941             return false;
34942         }
34943         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34944         return r ? r.rowIndex : false;
34945     },
34946
34947     findCellIndex : function(node){
34948         var stop = this.el.dom;
34949         while(node && node != stop){
34950             if(this.findRE.test(node.className)){
34951                 return this.getCellIndex(node);
34952             }
34953             node = node.parentNode;
34954         }
34955         return false;
34956     },
34957
34958     getColumnId : function(index){
34959         return this.cm.getColumnId(index);
34960     },
34961
34962     getSplitters : function()
34963     {
34964         if(this.splitterSelector){
34965            return Roo.DomQuery.select(this.splitterSelector);
34966         }else{
34967             return null;
34968       }
34969     },
34970
34971     getSplitter : function(index){
34972         return this.getSplitters()[index];
34973     },
34974
34975     onRowOver : function(e, t){
34976         var row;
34977         if((row = this.findRowIndex(t)) !== false){
34978             this.getRowComposite(row).addClass("x-grid-row-over");
34979         }
34980     },
34981
34982     onRowOut : function(e, t){
34983         var row;
34984         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34985             this.getRowComposite(row).removeClass("x-grid-row-over");
34986         }
34987     },
34988
34989     renderHeaders : function(){
34990         var cm = this.cm;
34991         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
34992         var cb = [], lb = [], sb = [], lsb = [], p = {};
34993         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34994             p.cellId = "x-grid-hd-0-" + i;
34995             p.splitId = "x-grid-csplit-0-" + i;
34996             p.id = cm.getColumnId(i);
34997             p.title = cm.getColumnTooltip(i) || "";
34998             p.value = cm.getColumnHeader(i) || "";
34999             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35000             if(!cm.isLocked(i)){
35001                 cb[cb.length] = ct.apply(p);
35002                 sb[sb.length] = st.apply(p);
35003             }else{
35004                 lb[lb.length] = ct.apply(p);
35005                 lsb[lsb.length] = st.apply(p);
35006             }
35007         }
35008         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35009                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35010     },
35011
35012     updateHeaders : function(){
35013         var html = this.renderHeaders();
35014         this.lockedHd.update(html[0]);
35015         this.mainHd.update(html[1]);
35016     },
35017
35018     /**
35019      * Focuses the specified row.
35020      * @param {Number} row The row index
35021      */
35022     focusRow : function(row)
35023     {
35024         //Roo.log('GridView.focusRow');
35025         var x = this.scroller.dom.scrollLeft;
35026         this.focusCell(row, 0, false);
35027         this.scroller.dom.scrollLeft = x;
35028     },
35029
35030     /**
35031      * Focuses the specified cell.
35032      * @param {Number} row The row index
35033      * @param {Number} col The column index
35034      * @param {Boolean} hscroll false to disable horizontal scrolling
35035      */
35036     focusCell : function(row, col, hscroll)
35037     {
35038         //Roo.log('GridView.focusCell');
35039         var el = this.ensureVisible(row, col, hscroll);
35040         this.focusEl.alignTo(el, "tl-tl");
35041         if(Roo.isGecko){
35042             this.focusEl.focus();
35043         }else{
35044             this.focusEl.focus.defer(1, this.focusEl);
35045         }
35046     },
35047
35048     /**
35049      * Scrolls the specified cell into view
35050      * @param {Number} row The row index
35051      * @param {Number} col The column index
35052      * @param {Boolean} hscroll false to disable horizontal scrolling
35053      */
35054     ensureVisible : function(row, col, hscroll)
35055     {
35056         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35057         //return null; //disable for testing.
35058         if(typeof row != "number"){
35059             row = row.rowIndex;
35060         }
35061         if(row < 0 && row >= this.ds.getCount()){
35062             return  null;
35063         }
35064         col = (col !== undefined ? col : 0);
35065         var cm = this.grid.colModel;
35066         while(cm.isHidden(col)){
35067             col++;
35068         }
35069
35070         var el = this.getCell(row, col);
35071         if(!el){
35072             return null;
35073         }
35074         var c = this.scroller.dom;
35075
35076         var ctop = parseInt(el.offsetTop, 10);
35077         var cleft = parseInt(el.offsetLeft, 10);
35078         var cbot = ctop + el.offsetHeight;
35079         var cright = cleft + el.offsetWidth;
35080         
35081         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35082         var stop = parseInt(c.scrollTop, 10);
35083         var sleft = parseInt(c.scrollLeft, 10);
35084         var sbot = stop + ch;
35085         var sright = sleft + c.clientWidth;
35086         /*
35087         Roo.log('GridView.ensureVisible:' +
35088                 ' ctop:' + ctop +
35089                 ' c.clientHeight:' + c.clientHeight +
35090                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35091                 ' stop:' + stop +
35092                 ' cbot:' + cbot +
35093                 ' sbot:' + sbot +
35094                 ' ch:' + ch  
35095                 );
35096         */
35097         if(ctop < stop){
35098              c.scrollTop = ctop;
35099             //Roo.log("set scrolltop to ctop DISABLE?");
35100         }else if(cbot > sbot){
35101             //Roo.log("set scrolltop to cbot-ch");
35102             c.scrollTop = cbot-ch;
35103         }
35104         
35105         if(hscroll !== false){
35106             if(cleft < sleft){
35107                 c.scrollLeft = cleft;
35108             }else if(cright > sright){
35109                 c.scrollLeft = cright-c.clientWidth;
35110             }
35111         }
35112          
35113         return el;
35114     },
35115
35116     updateColumns : function(){
35117         this.grid.stopEditing();
35118         var cm = this.grid.colModel, colIds = this.getColumnIds();
35119         //var totalWidth = cm.getTotalWidth();
35120         var pos = 0;
35121         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35122             //if(cm.isHidden(i)) continue;
35123             var w = cm.getColumnWidth(i);
35124             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35125             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35126         }
35127         this.updateSplitters();
35128     },
35129
35130     generateRules : function(cm){
35131         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35132         Roo.util.CSS.removeStyleSheet(rulesId);
35133         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35134             var cid = cm.getColumnId(i);
35135             var align = '';
35136             if(cm.config[i].align){
35137                 align = 'text-align:'+cm.config[i].align+';';
35138             }
35139             var hidden = '';
35140             if(cm.isHidden(i)){
35141                 hidden = 'display:none;';
35142             }
35143             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35144             ruleBuf.push(
35145                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35146                     this.hdSelector, cid, " {\n", align, width, "}\n",
35147                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35148                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35149         }
35150         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35151     },
35152
35153     updateSplitters : function(){
35154         var cm = this.cm, s = this.getSplitters();
35155         if(s){ // splitters not created yet
35156             var pos = 0, locked = true;
35157             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35158                 if(cm.isHidden(i)) continue;
35159                 var w = cm.getColumnWidth(i); // make sure it's a number
35160                 if(!cm.isLocked(i) && locked){
35161                     pos = 0;
35162                     locked = false;
35163                 }
35164                 pos += w;
35165                 s[i].style.left = (pos-this.splitOffset) + "px";
35166             }
35167         }
35168     },
35169
35170     handleHiddenChange : function(colModel, colIndex, hidden){
35171         if(hidden){
35172             this.hideColumn(colIndex);
35173         }else{
35174             this.unhideColumn(colIndex);
35175         }
35176     },
35177
35178     hideColumn : function(colIndex){
35179         var cid = this.getColumnId(colIndex);
35180         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35181         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35182         if(Roo.isSafari){
35183             this.updateHeaders();
35184         }
35185         this.updateSplitters();
35186         this.layout();
35187     },
35188
35189     unhideColumn : function(colIndex){
35190         var cid = this.getColumnId(colIndex);
35191         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35192         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35193
35194         if(Roo.isSafari){
35195             this.updateHeaders();
35196         }
35197         this.updateSplitters();
35198         this.layout();
35199     },
35200
35201     insertRows : function(dm, firstRow, lastRow, isUpdate){
35202         if(firstRow == 0 && lastRow == dm.getCount()-1){
35203             this.refresh();
35204         }else{
35205             if(!isUpdate){
35206                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35207             }
35208             var s = this.getScrollState();
35209             var markup = this.renderRows(firstRow, lastRow);
35210             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35211             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35212             this.restoreScroll(s);
35213             if(!isUpdate){
35214                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35215                 this.syncRowHeights(firstRow, lastRow);
35216                 this.stripeRows(firstRow);
35217                 this.layout();
35218             }
35219         }
35220     },
35221
35222     bufferRows : function(markup, target, index){
35223         var before = null, trows = target.rows, tbody = target.tBodies[0];
35224         if(index < trows.length){
35225             before = trows[index];
35226         }
35227         var b = document.createElement("div");
35228         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35229         var rows = b.firstChild.rows;
35230         for(var i = 0, len = rows.length; i < len; i++){
35231             if(before){
35232                 tbody.insertBefore(rows[0], before);
35233             }else{
35234                 tbody.appendChild(rows[0]);
35235             }
35236         }
35237         b.innerHTML = "";
35238         b = null;
35239     },
35240
35241     deleteRows : function(dm, firstRow, lastRow){
35242         if(dm.getRowCount()<1){
35243             this.fireEvent("beforerefresh", this);
35244             this.mainBody.update("");
35245             this.lockedBody.update("");
35246             this.fireEvent("refresh", this);
35247         }else{
35248             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35249             var bt = this.getBodyTable();
35250             var tbody = bt.firstChild;
35251             var rows = bt.rows;
35252             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35253                 tbody.removeChild(rows[firstRow]);
35254             }
35255             this.stripeRows(firstRow);
35256             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35257         }
35258     },
35259
35260     updateRows : function(dataSource, firstRow, lastRow){
35261         var s = this.getScrollState();
35262         this.refresh();
35263         this.restoreScroll(s);
35264     },
35265
35266     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35267         if(!noRefresh){
35268            this.refresh();
35269         }
35270         this.updateHeaderSortState();
35271     },
35272
35273     getScrollState : function(){
35274         
35275         var sb = this.scroller.dom;
35276         return {left: sb.scrollLeft, top: sb.scrollTop};
35277     },
35278
35279     stripeRows : function(startRow){
35280         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35281             return;
35282         }
35283         startRow = startRow || 0;
35284         var rows = this.getBodyTable().rows;
35285         var lrows = this.getLockedTable().rows;
35286         var cls = ' x-grid-row-alt ';
35287         for(var i = startRow, len = rows.length; i < len; i++){
35288             var row = rows[i], lrow = lrows[i];
35289             var isAlt = ((i+1) % 2 == 0);
35290             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35291             if(isAlt == hasAlt){
35292                 continue;
35293             }
35294             if(isAlt){
35295                 row.className += " x-grid-row-alt";
35296             }else{
35297                 row.className = row.className.replace("x-grid-row-alt", "");
35298             }
35299             if(lrow){
35300                 lrow.className = row.className;
35301             }
35302         }
35303     },
35304
35305     restoreScroll : function(state){
35306         //Roo.log('GridView.restoreScroll');
35307         var sb = this.scroller.dom;
35308         sb.scrollLeft = state.left;
35309         sb.scrollTop = state.top;
35310         this.syncScroll();
35311     },
35312
35313     syncScroll : function(){
35314         //Roo.log('GridView.syncScroll');
35315         var sb = this.scroller.dom;
35316         var sh = this.mainHd.dom;
35317         var bs = this.mainBody.dom;
35318         var lv = this.lockedBody.dom;
35319         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35320         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35321     },
35322
35323     handleScroll : function(e){
35324         this.syncScroll();
35325         var sb = this.scroller.dom;
35326         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35327         e.stopEvent();
35328     },
35329
35330     handleWheel : function(e){
35331         var d = e.getWheelDelta();
35332         this.scroller.dom.scrollTop -= d*22;
35333         // set this here to prevent jumpy scrolling on large tables
35334         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35335         e.stopEvent();
35336     },
35337
35338     renderRows : function(startRow, endRow){
35339         // pull in all the crap needed to render rows
35340         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35341         var colCount = cm.getColumnCount();
35342
35343         if(ds.getCount() < 1){
35344             return ["", ""];
35345         }
35346
35347         // build a map for all the columns
35348         var cs = [];
35349         for(var i = 0; i < colCount; i++){
35350             var name = cm.getDataIndex(i);
35351             cs[i] = {
35352                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35353                 renderer : cm.getRenderer(i),
35354                 id : cm.getColumnId(i),
35355                 locked : cm.isLocked(i)
35356             };
35357         }
35358
35359         startRow = startRow || 0;
35360         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35361
35362         // records to render
35363         var rs = ds.getRange(startRow, endRow);
35364
35365         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35366     },
35367
35368     // As much as I hate to duplicate code, this was branched because FireFox really hates
35369     // [].join("") on strings. The performance difference was substantial enough to
35370     // branch this function
35371     doRender : Roo.isGecko ?
35372             function(cs, rs, ds, startRow, colCount, stripe){
35373                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35374                 // buffers
35375                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35376                 
35377                 var hasListener = this.grid.hasListener('rowclass');
35378                 var rowcfg = {};
35379                 for(var j = 0, len = rs.length; j < len; j++){
35380                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35381                     for(var i = 0; i < colCount; i++){
35382                         c = cs[i];
35383                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35384                         p.id = c.id;
35385                         p.css = p.attr = "";
35386                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35387                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35388                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35389                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35390                         }
35391                         var markup = ct.apply(p);
35392                         if(!c.locked){
35393                             cb+= markup;
35394                         }else{
35395                             lcb+= markup;
35396                         }
35397                     }
35398                     var alt = [];
35399                     if(stripe && ((rowIndex+1) % 2 == 0)){
35400                         alt.push("x-grid-row-alt")
35401                     }
35402                     if(r.dirty){
35403                         alt.push(  " x-grid-dirty-row");
35404                     }
35405                     rp.cells = lcb;
35406                     if(this.getRowClass){
35407                         alt.push(this.getRowClass(r, rowIndex));
35408                     }
35409                     if (hasListener) {
35410                         rowcfg = {
35411                              
35412                             record: r,
35413                             rowIndex : rowIndex,
35414                             rowClass : ''
35415                         }
35416                         this.grid.fireEvent('rowclass', this, rowcfg);
35417                         alt.push(rowcfg.rowClass);
35418                     }
35419                     rp.alt = alt.join(" ");
35420                     lbuf+= rt.apply(rp);
35421                     rp.cells = cb;
35422                     buf+=  rt.apply(rp);
35423                 }
35424                 return [lbuf, buf];
35425             } :
35426             function(cs, rs, ds, startRow, colCount, stripe){
35427                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35428                 // buffers
35429                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35430                 var hasListener = this.grid.hasListener('rowclass');
35431  
35432                 var rowcfg = {};
35433                 for(var j = 0, len = rs.length; j < len; j++){
35434                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35435                     for(var i = 0; i < colCount; i++){
35436                         c = cs[i];
35437                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35438                         p.id = c.id;
35439                         p.css = p.attr = "";
35440                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35441                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35442                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35443                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35444                         }
35445                         
35446                         var markup = ct.apply(p);
35447                         if(!c.locked){
35448                             cb[cb.length] = markup;
35449                         }else{
35450                             lcb[lcb.length] = markup;
35451                         }
35452                     }
35453                     var alt = [];
35454                     if(stripe && ((rowIndex+1) % 2 == 0)){
35455                         alt.push( "x-grid-row-alt");
35456                     }
35457                     if(r.dirty){
35458                         alt.push(" x-grid-dirty-row");
35459                     }
35460                     rp.cells = lcb;
35461                     if(this.getRowClass){
35462                         alt.push( this.getRowClass(r, rowIndex));
35463                     }
35464                     if (hasListener) {
35465                         rowcfg = {
35466                              
35467                             record: r,
35468                             rowIndex : rowIndex,
35469                             rowClass : ''
35470                         }
35471                         this.grid.fireEvent('rowclass', this, rowcfg);
35472                         alt.push(rowcfg.rowClass);
35473                     }
35474                     rp.alt = alt.join(" ");
35475                     rp.cells = lcb.join("");
35476                     lbuf[lbuf.length] = rt.apply(rp);
35477                     rp.cells = cb.join("");
35478                     buf[buf.length] =  rt.apply(rp);
35479                 }
35480                 return [lbuf.join(""), buf.join("")];
35481             },
35482
35483     renderBody : function(){
35484         var markup = this.renderRows();
35485         var bt = this.templates.body;
35486         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35487     },
35488
35489     /**
35490      * Refreshes the grid
35491      * @param {Boolean} headersToo
35492      */
35493     refresh : function(headersToo){
35494         this.fireEvent("beforerefresh", this);
35495         this.grid.stopEditing();
35496         var result = this.renderBody();
35497         this.lockedBody.update(result[0]);
35498         this.mainBody.update(result[1]);
35499         if(headersToo === true){
35500             this.updateHeaders();
35501             this.updateColumns();
35502             this.updateSplitters();
35503             this.updateHeaderSortState();
35504         }
35505         this.syncRowHeights();
35506         this.layout();
35507         this.fireEvent("refresh", this);
35508     },
35509
35510     handleColumnMove : function(cm, oldIndex, newIndex){
35511         this.indexMap = null;
35512         var s = this.getScrollState();
35513         this.refresh(true);
35514         this.restoreScroll(s);
35515         this.afterMove(newIndex);
35516     },
35517
35518     afterMove : function(colIndex){
35519         if(this.enableMoveAnim && Roo.enableFx){
35520             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35521         }
35522         // if multisort - fix sortOrder, and reload..
35523         if (this.grid.dataSource.multiSort) {
35524             // the we can call sort again..
35525             var dm = this.grid.dataSource;
35526             var cm = this.grid.colModel;
35527             var so = [];
35528             for(var i = 0; i < cm.config.length; i++ ) {
35529                 
35530                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35531                     continue; // dont' bother, it's not in sort list or being set.
35532                 }
35533                 
35534                 so.push(cm.config[i].dataIndex);
35535             };
35536             dm.sortOrder = so;
35537             dm.load(dm.lastOptions);
35538             
35539             
35540         }
35541         
35542     },
35543
35544     updateCell : function(dm, rowIndex, dataIndex){
35545         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35546         if(typeof colIndex == "undefined"){ // not present in grid
35547             return;
35548         }
35549         var cm = this.grid.colModel;
35550         var cell = this.getCell(rowIndex, colIndex);
35551         var cellText = this.getCellText(rowIndex, colIndex);
35552
35553         var p = {
35554             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35555             id : cm.getColumnId(colIndex),
35556             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35557         };
35558         var renderer = cm.getRenderer(colIndex);
35559         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35560         if(typeof val == "undefined" || val === "") val = "&#160;";
35561         cellText.innerHTML = val;
35562         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35563         this.syncRowHeights(rowIndex, rowIndex);
35564     },
35565
35566     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35567         var maxWidth = 0;
35568         if(this.grid.autoSizeHeaders){
35569             var h = this.getHeaderCellMeasure(colIndex);
35570             maxWidth = Math.max(maxWidth, h.scrollWidth);
35571         }
35572         var tb, index;
35573         if(this.cm.isLocked(colIndex)){
35574             tb = this.getLockedTable();
35575             index = colIndex;
35576         }else{
35577             tb = this.getBodyTable();
35578             index = colIndex - this.cm.getLockedCount();
35579         }
35580         if(tb && tb.rows){
35581             var rows = tb.rows;
35582             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35583             for(var i = 0; i < stopIndex; i++){
35584                 var cell = rows[i].childNodes[index].firstChild;
35585                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35586             }
35587         }
35588         return maxWidth + /*margin for error in IE*/ 5;
35589     },
35590     /**
35591      * Autofit a column to its content.
35592      * @param {Number} colIndex
35593      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35594      */
35595      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35596          if(this.cm.isHidden(colIndex)){
35597              return; // can't calc a hidden column
35598          }
35599         if(forceMinSize){
35600             var cid = this.cm.getColumnId(colIndex);
35601             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35602            if(this.grid.autoSizeHeaders){
35603                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35604            }
35605         }
35606         var newWidth = this.calcColumnWidth(colIndex);
35607         this.cm.setColumnWidth(colIndex,
35608             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35609         if(!suppressEvent){
35610             this.grid.fireEvent("columnresize", colIndex, newWidth);
35611         }
35612     },
35613
35614     /**
35615      * Autofits all columns to their content and then expands to fit any extra space in the grid
35616      */
35617      autoSizeColumns : function(){
35618         var cm = this.grid.colModel;
35619         var colCount = cm.getColumnCount();
35620         for(var i = 0; i < colCount; i++){
35621             this.autoSizeColumn(i, true, true);
35622         }
35623         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35624             this.fitColumns();
35625         }else{
35626             this.updateColumns();
35627             this.layout();
35628         }
35629     },
35630
35631     /**
35632      * Autofits all columns to the grid's width proportionate with their current size
35633      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35634      */
35635     fitColumns : function(reserveScrollSpace){
35636         var cm = this.grid.colModel;
35637         var colCount = cm.getColumnCount();
35638         var cols = [];
35639         var width = 0;
35640         var i, w;
35641         for (i = 0; i < colCount; i++){
35642             if(!cm.isHidden(i) && !cm.isFixed(i)){
35643                 w = cm.getColumnWidth(i);
35644                 cols.push(i);
35645                 cols.push(w);
35646                 width += w;
35647             }
35648         }
35649         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35650         if(reserveScrollSpace){
35651             avail -= 17;
35652         }
35653         var frac = (avail - cm.getTotalWidth())/width;
35654         while (cols.length){
35655             w = cols.pop();
35656             i = cols.pop();
35657             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35658         }
35659         this.updateColumns();
35660         this.layout();
35661     },
35662
35663     onRowSelect : function(rowIndex){
35664         var row = this.getRowComposite(rowIndex);
35665         row.addClass("x-grid-row-selected");
35666     },
35667
35668     onRowDeselect : function(rowIndex){
35669         var row = this.getRowComposite(rowIndex);
35670         row.removeClass("x-grid-row-selected");
35671     },
35672
35673     onCellSelect : function(row, col){
35674         var cell = this.getCell(row, col);
35675         if(cell){
35676             Roo.fly(cell).addClass("x-grid-cell-selected");
35677         }
35678     },
35679
35680     onCellDeselect : function(row, col){
35681         var cell = this.getCell(row, col);
35682         if(cell){
35683             Roo.fly(cell).removeClass("x-grid-cell-selected");
35684         }
35685     },
35686
35687     updateHeaderSortState : function(){
35688         
35689         // sort state can be single { field: xxx, direction : yyy}
35690         // or   { xxx=>ASC , yyy : DESC ..... }
35691         
35692         var mstate = {};
35693         if (!this.ds.multiSort) { 
35694             var state = this.ds.getSortState();
35695             if(!state){
35696                 return;
35697             }
35698             mstate[state.field] = state.direction;
35699             // FIXME... - this is not used here.. but might be elsewhere..
35700             this.sortState = state;
35701             
35702         } else {
35703             mstate = this.ds.sortToggle;
35704         }
35705         //remove existing sort classes..
35706         
35707         var sc = this.sortClasses;
35708         var hds = this.el.select(this.headerSelector).removeClass(sc);
35709         
35710         for(var f in mstate) {
35711         
35712             var sortColumn = this.cm.findColumnIndex(f);
35713             
35714             if(sortColumn != -1){
35715                 var sortDir = mstate[f];        
35716                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35717             }
35718         }
35719         
35720          
35721         
35722     },
35723
35724
35725     handleHeaderClick : function(g, index){
35726         if(this.headersDisabled){
35727             return;
35728         }
35729         var dm = g.dataSource, cm = g.colModel;
35730         if(!cm.isSortable(index)){
35731             return;
35732         }
35733         g.stopEditing();
35734         
35735         if (dm.multiSort) {
35736             // update the sortOrder
35737             var so = [];
35738             for(var i = 0; i < cm.config.length; i++ ) {
35739                 
35740                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35741                     continue; // dont' bother, it's not in sort list or being set.
35742                 }
35743                 
35744                 so.push(cm.config[i].dataIndex);
35745             };
35746             dm.sortOrder = so;
35747         }
35748         
35749         
35750         dm.sort(cm.getDataIndex(index));
35751     },
35752
35753
35754     destroy : function(){
35755         if(this.colMenu){
35756             this.colMenu.removeAll();
35757             Roo.menu.MenuMgr.unregister(this.colMenu);
35758             this.colMenu.getEl().remove();
35759             delete this.colMenu;
35760         }
35761         if(this.hmenu){
35762             this.hmenu.removeAll();
35763             Roo.menu.MenuMgr.unregister(this.hmenu);
35764             this.hmenu.getEl().remove();
35765             delete this.hmenu;
35766         }
35767         if(this.grid.enableColumnMove){
35768             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35769             if(dds){
35770                 for(var dd in dds){
35771                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35772                         var elid = dds[dd].dragElId;
35773                         dds[dd].unreg();
35774                         Roo.get(elid).remove();
35775                     } else if(dds[dd].config.isTarget){
35776                         dds[dd].proxyTop.remove();
35777                         dds[dd].proxyBottom.remove();
35778                         dds[dd].unreg();
35779                     }
35780                     if(Roo.dd.DDM.locationCache[dd]){
35781                         delete Roo.dd.DDM.locationCache[dd];
35782                     }
35783                 }
35784                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35785             }
35786         }
35787         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35788         this.bind(null, null);
35789         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35790     },
35791
35792     handleLockChange : function(){
35793         this.refresh(true);
35794     },
35795
35796     onDenyColumnLock : function(){
35797
35798     },
35799
35800     onDenyColumnHide : function(){
35801
35802     },
35803
35804     handleHdMenuClick : function(item){
35805         var index = this.hdCtxIndex;
35806         var cm = this.cm, ds = this.ds;
35807         switch(item.id){
35808             case "asc":
35809                 ds.sort(cm.getDataIndex(index), "ASC");
35810                 break;
35811             case "desc":
35812                 ds.sort(cm.getDataIndex(index), "DESC");
35813                 break;
35814             case "lock":
35815                 var lc = cm.getLockedCount();
35816                 if(cm.getColumnCount(true) <= lc+1){
35817                     this.onDenyColumnLock();
35818                     return;
35819                 }
35820                 if(lc != index){
35821                     cm.setLocked(index, true, true);
35822                     cm.moveColumn(index, lc);
35823                     this.grid.fireEvent("columnmove", index, lc);
35824                 }else{
35825                     cm.setLocked(index, true);
35826                 }
35827             break;
35828             case "unlock":
35829                 var lc = cm.getLockedCount();
35830                 if((lc-1) != index){
35831                     cm.setLocked(index, false, true);
35832                     cm.moveColumn(index, lc-1);
35833                     this.grid.fireEvent("columnmove", index, lc-1);
35834                 }else{
35835                     cm.setLocked(index, false);
35836                 }
35837             break;
35838             default:
35839                 index = cm.getIndexById(item.id.substr(4));
35840                 if(index != -1){
35841                     if(item.checked && cm.getColumnCount(true) <= 1){
35842                         this.onDenyColumnHide();
35843                         return false;
35844                     }
35845                     cm.setHidden(index, item.checked);
35846                 }
35847         }
35848         return true;
35849     },
35850
35851     beforeColMenuShow : function(){
35852         var cm = this.cm,  colCount = cm.getColumnCount();
35853         this.colMenu.removeAll();
35854         for(var i = 0; i < colCount; i++){
35855             this.colMenu.add(new Roo.menu.CheckItem({
35856                 id: "col-"+cm.getColumnId(i),
35857                 text: cm.getColumnHeader(i),
35858                 checked: !cm.isHidden(i),
35859                 hideOnClick:false
35860             }));
35861         }
35862     },
35863
35864     handleHdCtx : function(g, index, e){
35865         e.stopEvent();
35866         var hd = this.getHeaderCell(index);
35867         this.hdCtxIndex = index;
35868         var ms = this.hmenu.items, cm = this.cm;
35869         ms.get("asc").setDisabled(!cm.isSortable(index));
35870         ms.get("desc").setDisabled(!cm.isSortable(index));
35871         if(this.grid.enableColLock !== false){
35872             ms.get("lock").setDisabled(cm.isLocked(index));
35873             ms.get("unlock").setDisabled(!cm.isLocked(index));
35874         }
35875         this.hmenu.show(hd, "tl-bl");
35876     },
35877
35878     handleHdOver : function(e){
35879         var hd = this.findHeaderCell(e.getTarget());
35880         if(hd && !this.headersDisabled){
35881             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35882                this.fly(hd).addClass("x-grid-hd-over");
35883             }
35884         }
35885     },
35886
35887     handleHdOut : function(e){
35888         var hd = this.findHeaderCell(e.getTarget());
35889         if(hd){
35890             this.fly(hd).removeClass("x-grid-hd-over");
35891         }
35892     },
35893
35894     handleSplitDblClick : function(e, t){
35895         var i = this.getCellIndex(t);
35896         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35897             this.autoSizeColumn(i, true);
35898             this.layout();
35899         }
35900     },
35901
35902     render : function(){
35903
35904         var cm = this.cm;
35905         var colCount = cm.getColumnCount();
35906
35907         if(this.grid.monitorWindowResize === true){
35908             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35909         }
35910         var header = this.renderHeaders();
35911         var body = this.templates.body.apply({rows:""});
35912         var html = this.templates.master.apply({
35913             lockedBody: body,
35914             body: body,
35915             lockedHeader: header[0],
35916             header: header[1]
35917         });
35918
35919         //this.updateColumns();
35920
35921         this.grid.getGridEl().dom.innerHTML = html;
35922
35923         this.initElements();
35924         
35925         // a kludge to fix the random scolling effect in webkit
35926         this.el.on("scroll", function() {
35927             this.el.dom.scrollTop=0; // hopefully not recursive..
35928         },this);
35929
35930         this.scroller.on("scroll", this.handleScroll, this);
35931         this.lockedBody.on("mousewheel", this.handleWheel, this);
35932         this.mainBody.on("mousewheel", this.handleWheel, this);
35933
35934         this.mainHd.on("mouseover", this.handleHdOver, this);
35935         this.mainHd.on("mouseout", this.handleHdOut, this);
35936         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35937                 {delegate: "."+this.splitClass});
35938
35939         this.lockedHd.on("mouseover", this.handleHdOver, this);
35940         this.lockedHd.on("mouseout", this.handleHdOut, this);
35941         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35942                 {delegate: "."+this.splitClass});
35943
35944         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35945             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35946         }
35947
35948         this.updateSplitters();
35949
35950         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35951             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35952             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35953         }
35954
35955         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35956             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35957             this.hmenu.add(
35958                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35959                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35960             );
35961             if(this.grid.enableColLock !== false){
35962                 this.hmenu.add('-',
35963                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35964                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35965                 );
35966             }
35967             if(this.grid.enableColumnHide !== false){
35968
35969                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35970                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35971                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35972
35973                 this.hmenu.add('-',
35974                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35975                 );
35976             }
35977             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35978
35979             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35980         }
35981
35982         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35983             this.dd = new Roo.grid.GridDragZone(this.grid, {
35984                 ddGroup : this.grid.ddGroup || 'GridDD'
35985             });
35986         }
35987
35988         /*
35989         for(var i = 0; i < colCount; i++){
35990             if(cm.isHidden(i)){
35991                 this.hideColumn(i);
35992             }
35993             if(cm.config[i].align){
35994                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35995                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35996             }
35997         }*/
35998         
35999         this.updateHeaderSortState();
36000
36001         this.beforeInitialResize();
36002         this.layout(true);
36003
36004         // two part rendering gives faster view to the user
36005         this.renderPhase2.defer(1, this);
36006     },
36007
36008     renderPhase2 : function(){
36009         // render the rows now
36010         this.refresh();
36011         if(this.grid.autoSizeColumns){
36012             this.autoSizeColumns();
36013         }
36014     },
36015
36016     beforeInitialResize : function(){
36017
36018     },
36019
36020     onColumnSplitterMoved : function(i, w){
36021         this.userResized = true;
36022         var cm = this.grid.colModel;
36023         cm.setColumnWidth(i, w, true);
36024         var cid = cm.getColumnId(i);
36025         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36026         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36027         this.updateSplitters();
36028         this.layout();
36029         this.grid.fireEvent("columnresize", i, w);
36030     },
36031
36032     syncRowHeights : function(startIndex, endIndex){
36033         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36034             startIndex = startIndex || 0;
36035             var mrows = this.getBodyTable().rows;
36036             var lrows = this.getLockedTable().rows;
36037             var len = mrows.length-1;
36038             endIndex = Math.min(endIndex || len, len);
36039             for(var i = startIndex; i <= endIndex; i++){
36040                 var m = mrows[i], l = lrows[i];
36041                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36042                 m.style.height = l.style.height = h + "px";
36043             }
36044         }
36045     },
36046
36047     layout : function(initialRender, is2ndPass){
36048         var g = this.grid;
36049         var auto = g.autoHeight;
36050         var scrollOffset = 16;
36051         var c = g.getGridEl(), cm = this.cm,
36052                 expandCol = g.autoExpandColumn,
36053                 gv = this;
36054         //c.beginMeasure();
36055
36056         if(!c.dom.offsetWidth){ // display:none?
36057             if(initialRender){
36058                 this.lockedWrap.show();
36059                 this.mainWrap.show();
36060             }
36061             return;
36062         }
36063
36064         var hasLock = this.cm.isLocked(0);
36065
36066         var tbh = this.headerPanel.getHeight();
36067         var bbh = this.footerPanel.getHeight();
36068
36069         if(auto){
36070             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36071             var newHeight = ch + c.getBorderWidth("tb");
36072             if(g.maxHeight){
36073                 newHeight = Math.min(g.maxHeight, newHeight);
36074             }
36075             c.setHeight(newHeight);
36076         }
36077
36078         if(g.autoWidth){
36079             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36080         }
36081
36082         var s = this.scroller;
36083
36084         var csize = c.getSize(true);
36085
36086         this.el.setSize(csize.width, csize.height);
36087
36088         this.headerPanel.setWidth(csize.width);
36089         this.footerPanel.setWidth(csize.width);
36090
36091         var hdHeight = this.mainHd.getHeight();
36092         var vw = csize.width;
36093         var vh = csize.height - (tbh + bbh);
36094
36095         s.setSize(vw, vh);
36096
36097         var bt = this.getBodyTable();
36098         var ltWidth = hasLock ?
36099                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36100
36101         var scrollHeight = bt.offsetHeight;
36102         var scrollWidth = ltWidth + bt.offsetWidth;
36103         var vscroll = false, hscroll = false;
36104
36105         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36106
36107         var lw = this.lockedWrap, mw = this.mainWrap;
36108         var lb = this.lockedBody, mb = this.mainBody;
36109
36110         setTimeout(function(){
36111             var t = s.dom.offsetTop;
36112             var w = s.dom.clientWidth,
36113                 h = s.dom.clientHeight;
36114
36115             lw.setTop(t);
36116             lw.setSize(ltWidth, h);
36117
36118             mw.setLeftTop(ltWidth, t);
36119             mw.setSize(w-ltWidth, h);
36120
36121             lb.setHeight(h-hdHeight);
36122             mb.setHeight(h-hdHeight);
36123
36124             if(is2ndPass !== true && !gv.userResized && expandCol){
36125                 // high speed resize without full column calculation
36126                 
36127                 var ci = cm.getIndexById(expandCol);
36128                 if (ci < 0) {
36129                     ci = cm.findColumnIndex(expandCol);
36130                 }
36131                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36132                 var expandId = cm.getColumnId(ci);
36133                 var  tw = cm.getTotalWidth(false);
36134                 var currentWidth = cm.getColumnWidth(ci);
36135                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36136                 if(currentWidth != cw){
36137                     cm.setColumnWidth(ci, cw, true);
36138                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36139                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36140                     gv.updateSplitters();
36141                     gv.layout(false, true);
36142                 }
36143             }
36144
36145             if(initialRender){
36146                 lw.show();
36147                 mw.show();
36148             }
36149             //c.endMeasure();
36150         }, 10);
36151     },
36152
36153     onWindowResize : function(){
36154         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36155             return;
36156         }
36157         this.layout();
36158     },
36159
36160     appendFooter : function(parentEl){
36161         return null;
36162     },
36163
36164     sortAscText : "Sort Ascending",
36165     sortDescText : "Sort Descending",
36166     lockText : "Lock Column",
36167     unlockText : "Unlock Column",
36168     columnsText : "Columns"
36169 });
36170
36171
36172 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36173     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36174     this.proxy.el.addClass('x-grid3-col-dd');
36175 };
36176
36177 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36178     handleMouseDown : function(e){
36179
36180     },
36181
36182     callHandleMouseDown : function(e){
36183         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36184     }
36185 });
36186 /*
36187  * Based on:
36188  * Ext JS Library 1.1.1
36189  * Copyright(c) 2006-2007, Ext JS, LLC.
36190  *
36191  * Originally Released Under LGPL - original licence link has changed is not relivant.
36192  *
36193  * Fork - LGPL
36194  * <script type="text/javascript">
36195  */
36196  
36197 // private
36198 // This is a support class used internally by the Grid components
36199 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36200     this.grid = grid;
36201     this.view = grid.getView();
36202     this.proxy = this.view.resizeProxy;
36203     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
36204         "gridSplitters" + this.grid.getGridEl().id, {
36205         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
36206     });
36207     this.setHandleElId(Roo.id(hd));
36208     this.setOuterHandleElId(Roo.id(hd2));
36209     this.scroll = false;
36210 };
36211 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36212     fly: Roo.Element.fly,
36213
36214     b4StartDrag : function(x, y){
36215         this.view.headersDisabled = true;
36216         this.proxy.setHeight(this.view.mainWrap.getHeight());
36217         var w = this.cm.getColumnWidth(this.cellIndex);
36218         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36219         this.resetConstraints();
36220         this.setXConstraint(minw, 1000);
36221         this.setYConstraint(0, 0);
36222         this.minX = x - minw;
36223         this.maxX = x + 1000;
36224         this.startPos = x;
36225         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36226     },
36227
36228
36229     handleMouseDown : function(e){
36230         ev = Roo.EventObject.setEvent(e);
36231         var t = this.fly(ev.getTarget());
36232         if(t.hasClass("x-grid-split")){
36233             this.cellIndex = this.view.getCellIndex(t.dom);
36234             this.split = t.dom;
36235             this.cm = this.grid.colModel;
36236             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36237                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36238             }
36239         }
36240     },
36241
36242     endDrag : function(e){
36243         this.view.headersDisabled = false;
36244         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36245         var diff = endX - this.startPos;
36246         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
36247     },
36248
36249     autoOffset : function(){
36250         this.setDelta(0,0);
36251     }
36252 });/*
36253  * Based on:
36254  * Ext JS Library 1.1.1
36255  * Copyright(c) 2006-2007, Ext JS, LLC.
36256  *
36257  * Originally Released Under LGPL - original licence link has changed is not relivant.
36258  *
36259  * Fork - LGPL
36260  * <script type="text/javascript">
36261  */
36262  
36263 // private
36264 // This is a support class used internally by the Grid components
36265 Roo.grid.GridDragZone = function(grid, config){
36266     this.view = grid.getView();
36267     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36268     if(this.view.lockedBody){
36269         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36270         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36271     }
36272     this.scroll = false;
36273     this.grid = grid;
36274     this.ddel = document.createElement('div');
36275     this.ddel.className = 'x-grid-dd-wrap';
36276 };
36277
36278 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36279     ddGroup : "GridDD",
36280
36281     getDragData : function(e){
36282         var t = Roo.lib.Event.getTarget(e);
36283         var rowIndex = this.view.findRowIndex(t);
36284         if(rowIndex !== false){
36285             var sm = this.grid.selModel;
36286             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36287               //  sm.mouseDown(e, t);
36288             //}
36289             if (e.hasModifier()){
36290                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36291             }
36292             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
36293         }
36294         return false;
36295     },
36296
36297     onInitDrag : function(e){
36298         var data = this.dragData;
36299         this.ddel.innerHTML = this.grid.getDragDropText();
36300         this.proxy.update(this.ddel);
36301         // fire start drag?
36302     },
36303
36304     afterRepair : function(){
36305         this.dragging = false;
36306     },
36307
36308     getRepairXY : function(e, data){
36309         return false;
36310     },
36311
36312     onEndDrag : function(data, e){
36313         // fire end drag?
36314     },
36315
36316     onValidDrop : function(dd, e, id){
36317         // fire drag drop?
36318         this.hideProxy();
36319     },
36320
36321     beforeInvalidDrop : function(e, id){
36322
36323     }
36324 });/*
36325  * Based on:
36326  * Ext JS Library 1.1.1
36327  * Copyright(c) 2006-2007, Ext JS, LLC.
36328  *
36329  * Originally Released Under LGPL - original licence link has changed is not relivant.
36330  *
36331  * Fork - LGPL
36332  * <script type="text/javascript">
36333  */
36334  
36335
36336 /**
36337  * @class Roo.grid.ColumnModel
36338  * @extends Roo.util.Observable
36339  * This is the default implementation of a ColumnModel used by the Grid. It defines
36340  * the columns in the grid.
36341  * <br>Usage:<br>
36342  <pre><code>
36343  var colModel = new Roo.grid.ColumnModel([
36344         {header: "Ticker", width: 60, sortable: true, locked: true},
36345         {header: "Company Name", width: 150, sortable: true},
36346         {header: "Market Cap.", width: 100, sortable: true},
36347         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36348         {header: "Employees", width: 100, sortable: true, resizable: false}
36349  ]);
36350  </code></pre>
36351  * <p>
36352  
36353  * The config options listed for this class are options which may appear in each
36354  * individual column definition.
36355  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36356  * @constructor
36357  * @param {Object} config An Array of column config objects. See this class's
36358  * config objects for details.
36359 */
36360 Roo.grid.ColumnModel = function(config){
36361         /**
36362      * The config passed into the constructor
36363      */
36364     this.config = config;
36365     this.lookup = {};
36366
36367     // if no id, create one
36368     // if the column does not have a dataIndex mapping,
36369     // map it to the order it is in the config
36370     for(var i = 0, len = config.length; i < len; i++){
36371         var c = config[i];
36372         if(typeof c.dataIndex == "undefined"){
36373             c.dataIndex = i;
36374         }
36375         if(typeof c.renderer == "string"){
36376             c.renderer = Roo.util.Format[c.renderer];
36377         }
36378         if(typeof c.id == "undefined"){
36379             c.id = Roo.id();
36380         }
36381         if(c.editor && c.editor.xtype){
36382             c.editor  = Roo.factory(c.editor, Roo.grid);
36383         }
36384         if(c.editor && c.editor.isFormField){
36385             c.editor = new Roo.grid.GridEditor(c.editor);
36386         }
36387         this.lookup[c.id] = c;
36388     }
36389
36390     /**
36391      * The width of columns which have no width specified (defaults to 100)
36392      * @type Number
36393      */
36394     this.defaultWidth = 100;
36395
36396     /**
36397      * Default sortable of columns which have no sortable specified (defaults to false)
36398      * @type Boolean
36399      */
36400     this.defaultSortable = false;
36401
36402     this.addEvents({
36403         /**
36404              * @event widthchange
36405              * Fires when the width of a column changes.
36406              * @param {ColumnModel} this
36407              * @param {Number} columnIndex The column index
36408              * @param {Number} newWidth The new width
36409              */
36410             "widthchange": true,
36411         /**
36412              * @event headerchange
36413              * Fires when the text of a header changes.
36414              * @param {ColumnModel} this
36415              * @param {Number} columnIndex The column index
36416              * @param {Number} newText The new header text
36417              */
36418             "headerchange": true,
36419         /**
36420              * @event hiddenchange
36421              * Fires when a column is hidden or "unhidden".
36422              * @param {ColumnModel} this
36423              * @param {Number} columnIndex The column index
36424              * @param {Boolean} hidden true if hidden, false otherwise
36425              */
36426             "hiddenchange": true,
36427             /**
36428          * @event columnmoved
36429          * Fires when a column is moved.
36430          * @param {ColumnModel} this
36431          * @param {Number} oldIndex
36432          * @param {Number} newIndex
36433          */
36434         "columnmoved" : true,
36435         /**
36436          * @event columlockchange
36437          * Fires when a column's locked state is changed
36438          * @param {ColumnModel} this
36439          * @param {Number} colIndex
36440          * @param {Boolean} locked true if locked
36441          */
36442         "columnlockchange" : true
36443     });
36444     Roo.grid.ColumnModel.superclass.constructor.call(this);
36445 };
36446 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36447     /**
36448      * @cfg {String} header The header text to display in the Grid view.
36449      */
36450     /**
36451      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36452      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36453      * specified, the column's index is used as an index into the Record's data Array.
36454      */
36455     /**
36456      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36457      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36458      */
36459     /**
36460      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36461      * Defaults to the value of the {@link #defaultSortable} property.
36462      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36463      */
36464     /**
36465      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36466      */
36467     /**
36468      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36469      */
36470     /**
36471      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36472      */
36473     /**
36474      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36475      */
36476     /**
36477      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36478      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36479      * default renderer uses the raw data value.
36480      */
36481        /**
36482      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36483      */
36484     /**
36485      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36486      */
36487
36488     /**
36489      * Returns the id of the column at the specified index.
36490      * @param {Number} index The column index
36491      * @return {String} the id
36492      */
36493     getColumnId : function(index){
36494         return this.config[index].id;
36495     },
36496
36497     /**
36498      * Returns the column for a specified id.
36499      * @param {String} id The column id
36500      * @return {Object} the column
36501      */
36502     getColumnById : function(id){
36503         return this.lookup[id];
36504     },
36505
36506     
36507     /**
36508      * Returns the column for a specified dataIndex.
36509      * @param {String} dataIndex The column dataIndex
36510      * @return {Object|Boolean} the column or false if not found
36511      */
36512     getColumnByDataIndex: function(dataIndex){
36513         var index = this.findColumnIndex(dataIndex);
36514         return index > -1 ? this.config[index] : false;
36515     },
36516     
36517     /**
36518      * Returns the index for a specified column id.
36519      * @param {String} id The column id
36520      * @return {Number} the index, or -1 if not found
36521      */
36522     getIndexById : function(id){
36523         for(var i = 0, len = this.config.length; i < len; i++){
36524             if(this.config[i].id == id){
36525                 return i;
36526             }
36527         }
36528         return -1;
36529     },
36530     
36531     /**
36532      * Returns the index for a specified column dataIndex.
36533      * @param {String} dataIndex The column dataIndex
36534      * @return {Number} the index, or -1 if not found
36535      */
36536     
36537     findColumnIndex : function(dataIndex){
36538         for(var i = 0, len = this.config.length; i < len; i++){
36539             if(this.config[i].dataIndex == dataIndex){
36540                 return i;
36541             }
36542         }
36543         return -1;
36544     },
36545     
36546     
36547     moveColumn : function(oldIndex, newIndex){
36548         var c = this.config[oldIndex];
36549         this.config.splice(oldIndex, 1);
36550         this.config.splice(newIndex, 0, c);
36551         this.dataMap = null;
36552         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36553     },
36554
36555     isLocked : function(colIndex){
36556         return this.config[colIndex].locked === true;
36557     },
36558
36559     setLocked : function(colIndex, value, suppressEvent){
36560         if(this.isLocked(colIndex) == value){
36561             return;
36562         }
36563         this.config[colIndex].locked = value;
36564         if(!suppressEvent){
36565             this.fireEvent("columnlockchange", this, colIndex, value);
36566         }
36567     },
36568
36569     getTotalLockedWidth : function(){
36570         var totalWidth = 0;
36571         for(var i = 0; i < this.config.length; i++){
36572             if(this.isLocked(i) && !this.isHidden(i)){
36573                 this.totalWidth += this.getColumnWidth(i);
36574             }
36575         }
36576         return totalWidth;
36577     },
36578
36579     getLockedCount : function(){
36580         for(var i = 0, len = this.config.length; i < len; i++){
36581             if(!this.isLocked(i)){
36582                 return i;
36583             }
36584         }
36585     },
36586
36587     /**
36588      * Returns the number of columns.
36589      * @return {Number}
36590      */
36591     getColumnCount : function(visibleOnly){
36592         if(visibleOnly === true){
36593             var c = 0;
36594             for(var i = 0, len = this.config.length; i < len; i++){
36595                 if(!this.isHidden(i)){
36596                     c++;
36597                 }
36598             }
36599             return c;
36600         }
36601         return this.config.length;
36602     },
36603
36604     /**
36605      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36606      * @param {Function} fn
36607      * @param {Object} scope (optional)
36608      * @return {Array} result
36609      */
36610     getColumnsBy : function(fn, scope){
36611         var r = [];
36612         for(var i = 0, len = this.config.length; i < len; i++){
36613             var c = this.config[i];
36614             if(fn.call(scope||this, c, i) === true){
36615                 r[r.length] = c;
36616             }
36617         }
36618         return r;
36619     },
36620
36621     /**
36622      * Returns true if the specified column is sortable.
36623      * @param {Number} col The column index
36624      * @return {Boolean}
36625      */
36626     isSortable : function(col){
36627         if(typeof this.config[col].sortable == "undefined"){
36628             return this.defaultSortable;
36629         }
36630         return this.config[col].sortable;
36631     },
36632
36633     /**
36634      * Returns the rendering (formatting) function defined for the column.
36635      * @param {Number} col The column index.
36636      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36637      */
36638     getRenderer : function(col){
36639         if(!this.config[col].renderer){
36640             return Roo.grid.ColumnModel.defaultRenderer;
36641         }
36642         return this.config[col].renderer;
36643     },
36644
36645     /**
36646      * Sets the rendering (formatting) function for a column.
36647      * @param {Number} col The column index
36648      * @param {Function} fn The function to use to process the cell's raw data
36649      * to return HTML markup for the grid view. The render function is called with
36650      * the following parameters:<ul>
36651      * <li>Data value.</li>
36652      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36653      * <li>css A CSS style string to apply to the table cell.</li>
36654      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36655      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36656      * <li>Row index</li>
36657      * <li>Column index</li>
36658      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36659      */
36660     setRenderer : function(col, fn){
36661         this.config[col].renderer = fn;
36662     },
36663
36664     /**
36665      * Returns the width for the specified column.
36666      * @param {Number} col The column index
36667      * @return {Number}
36668      */
36669     getColumnWidth : function(col){
36670         return this.config[col].width * 1 || this.defaultWidth;
36671     },
36672
36673     /**
36674      * Sets the width for a column.
36675      * @param {Number} col The column index
36676      * @param {Number} width The new width
36677      */
36678     setColumnWidth : function(col, width, suppressEvent){
36679         this.config[col].width = width;
36680         this.totalWidth = null;
36681         if(!suppressEvent){
36682              this.fireEvent("widthchange", this, col, width);
36683         }
36684     },
36685
36686     /**
36687      * Returns the total width of all columns.
36688      * @param {Boolean} includeHidden True to include hidden column widths
36689      * @return {Number}
36690      */
36691     getTotalWidth : function(includeHidden){
36692         if(!this.totalWidth){
36693             this.totalWidth = 0;
36694             for(var i = 0, len = this.config.length; i < len; i++){
36695                 if(includeHidden || !this.isHidden(i)){
36696                     this.totalWidth += this.getColumnWidth(i);
36697                 }
36698             }
36699         }
36700         return this.totalWidth;
36701     },
36702
36703     /**
36704      * Returns the header for the specified column.
36705      * @param {Number} col The column index
36706      * @return {String}
36707      */
36708     getColumnHeader : function(col){
36709         return this.config[col].header;
36710     },
36711
36712     /**
36713      * Sets the header for a column.
36714      * @param {Number} col The column index
36715      * @param {String} header The new header
36716      */
36717     setColumnHeader : function(col, header){
36718         this.config[col].header = header;
36719         this.fireEvent("headerchange", this, col, header);
36720     },
36721
36722     /**
36723      * Returns the tooltip for the specified column.
36724      * @param {Number} col The column index
36725      * @return {String}
36726      */
36727     getColumnTooltip : function(col){
36728             return this.config[col].tooltip;
36729     },
36730     /**
36731      * Sets the tooltip for a column.
36732      * @param {Number} col The column index
36733      * @param {String} tooltip The new tooltip
36734      */
36735     setColumnTooltip : function(col, tooltip){
36736             this.config[col].tooltip = tooltip;
36737     },
36738
36739     /**
36740      * Returns the dataIndex for the specified column.
36741      * @param {Number} col The column index
36742      * @return {Number}
36743      */
36744     getDataIndex : function(col){
36745         return this.config[col].dataIndex;
36746     },
36747
36748     /**
36749      * Sets the dataIndex for a column.
36750      * @param {Number} col The column index
36751      * @param {Number} dataIndex The new dataIndex
36752      */
36753     setDataIndex : function(col, dataIndex){
36754         this.config[col].dataIndex = dataIndex;
36755     },
36756
36757     
36758     
36759     /**
36760      * Returns true if the cell is editable.
36761      * @param {Number} colIndex The column index
36762      * @param {Number} rowIndex The row index
36763      * @return {Boolean}
36764      */
36765     isCellEditable : function(colIndex, rowIndex){
36766         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36767     },
36768
36769     /**
36770      * Returns the editor defined for the cell/column.
36771      * return false or null to disable editing.
36772      * @param {Number} colIndex The column index
36773      * @param {Number} rowIndex The row index
36774      * @return {Object}
36775      */
36776     getCellEditor : function(colIndex, rowIndex){
36777         return this.config[colIndex].editor;
36778     },
36779
36780     /**
36781      * Sets if a column is editable.
36782      * @param {Number} col The column index
36783      * @param {Boolean} editable True if the column is editable
36784      */
36785     setEditable : function(col, editable){
36786         this.config[col].editable = editable;
36787     },
36788
36789
36790     /**
36791      * Returns true if the column is hidden.
36792      * @param {Number} colIndex The column index
36793      * @return {Boolean}
36794      */
36795     isHidden : function(colIndex){
36796         return this.config[colIndex].hidden;
36797     },
36798
36799
36800     /**
36801      * Returns true if the column width cannot be changed
36802      */
36803     isFixed : function(colIndex){
36804         return this.config[colIndex].fixed;
36805     },
36806
36807     /**
36808      * Returns true if the column can be resized
36809      * @return {Boolean}
36810      */
36811     isResizable : function(colIndex){
36812         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36813     },
36814     /**
36815      * Sets if a column is hidden.
36816      * @param {Number} colIndex The column index
36817      * @param {Boolean} hidden True if the column is hidden
36818      */
36819     setHidden : function(colIndex, hidden){
36820         this.config[colIndex].hidden = hidden;
36821         this.totalWidth = null;
36822         this.fireEvent("hiddenchange", this, colIndex, hidden);
36823     },
36824
36825     /**
36826      * Sets the editor for a column.
36827      * @param {Number} col The column index
36828      * @param {Object} editor The editor object
36829      */
36830     setEditor : function(col, editor){
36831         this.config[col].editor = editor;
36832     }
36833 });
36834
36835 Roo.grid.ColumnModel.defaultRenderer = function(value){
36836         if(typeof value == "string" && value.length < 1){
36837             return "&#160;";
36838         }
36839         return value;
36840 };
36841
36842 // Alias for backwards compatibility
36843 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36844 /*
36845  * Based on:
36846  * Ext JS Library 1.1.1
36847  * Copyright(c) 2006-2007, Ext JS, LLC.
36848  *
36849  * Originally Released Under LGPL - original licence link has changed is not relivant.
36850  *
36851  * Fork - LGPL
36852  * <script type="text/javascript">
36853  */
36854
36855 /**
36856  * @class Roo.grid.AbstractSelectionModel
36857  * @extends Roo.util.Observable
36858  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36859  * implemented by descendant classes.  This class should not be directly instantiated.
36860  * @constructor
36861  */
36862 Roo.grid.AbstractSelectionModel = function(){
36863     this.locked = false;
36864     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36865 };
36866
36867 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36868     /** @ignore Called by the grid automatically. Do not call directly. */
36869     init : function(grid){
36870         this.grid = grid;
36871         this.initEvents();
36872     },
36873
36874     /**
36875      * Locks the selections.
36876      */
36877     lock : function(){
36878         this.locked = true;
36879     },
36880
36881     /**
36882      * Unlocks the selections.
36883      */
36884     unlock : function(){
36885         this.locked = false;
36886     },
36887
36888     /**
36889      * Returns true if the selections are locked.
36890      * @return {Boolean}
36891      */
36892     isLocked : function(){
36893         return this.locked;
36894     }
36895 });/*
36896  * Based on:
36897  * Ext JS Library 1.1.1
36898  * Copyright(c) 2006-2007, Ext JS, LLC.
36899  *
36900  * Originally Released Under LGPL - original licence link has changed is not relivant.
36901  *
36902  * Fork - LGPL
36903  * <script type="text/javascript">
36904  */
36905 /**
36906  * @extends Roo.grid.AbstractSelectionModel
36907  * @class Roo.grid.RowSelectionModel
36908  * The default SelectionModel used by {@link Roo.grid.Grid}.
36909  * It supports multiple selections and keyboard selection/navigation. 
36910  * @constructor
36911  * @param {Object} config
36912  */
36913 Roo.grid.RowSelectionModel = function(config){
36914     Roo.apply(this, config);
36915     this.selections = new Roo.util.MixedCollection(false, function(o){
36916         return o.id;
36917     });
36918
36919     this.last = false;
36920     this.lastActive = false;
36921
36922     this.addEvents({
36923         /**
36924              * @event selectionchange
36925              * Fires when the selection changes
36926              * @param {SelectionModel} this
36927              */
36928             "selectionchange" : true,
36929         /**
36930              * @event afterselectionchange
36931              * Fires after the selection changes (eg. by key press or clicking)
36932              * @param {SelectionModel} this
36933              */
36934             "afterselectionchange" : true,
36935         /**
36936              * @event beforerowselect
36937              * Fires when a row is selected being selected, return false to cancel.
36938              * @param {SelectionModel} this
36939              * @param {Number} rowIndex The selected index
36940              * @param {Boolean} keepExisting False if other selections will be cleared
36941              */
36942             "beforerowselect" : true,
36943         /**
36944              * @event rowselect
36945              * Fires when a row is selected.
36946              * @param {SelectionModel} this
36947              * @param {Number} rowIndex The selected index
36948              * @param {Roo.data.Record} r The record
36949              */
36950             "rowselect" : true,
36951         /**
36952              * @event rowdeselect
36953              * Fires when a row is deselected.
36954              * @param {SelectionModel} this
36955              * @param {Number} rowIndex The selected index
36956              */
36957         "rowdeselect" : true
36958     });
36959     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36960     this.locked = false;
36961 };
36962
36963 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36964     /**
36965      * @cfg {Boolean} singleSelect
36966      * True to allow selection of only one row at a time (defaults to false)
36967      */
36968     singleSelect : false,
36969
36970     // private
36971     initEvents : function(){
36972
36973         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36974             this.grid.on("mousedown", this.handleMouseDown, this);
36975         }else{ // allow click to work like normal
36976             this.grid.on("rowclick", this.handleDragableRowClick, this);
36977         }
36978
36979         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36980             "up" : function(e){
36981                 if(!e.shiftKey){
36982                     this.selectPrevious(e.shiftKey);
36983                 }else if(this.last !== false && this.lastActive !== false){
36984                     var last = this.last;
36985                     this.selectRange(this.last,  this.lastActive-1);
36986                     this.grid.getView().focusRow(this.lastActive);
36987                     if(last !== false){
36988                         this.last = last;
36989                     }
36990                 }else{
36991                     this.selectFirstRow();
36992                 }
36993                 this.fireEvent("afterselectionchange", this);
36994             },
36995             "down" : function(e){
36996                 if(!e.shiftKey){
36997                     this.selectNext(e.shiftKey);
36998                 }else if(this.last !== false && this.lastActive !== false){
36999                     var last = this.last;
37000                     this.selectRange(this.last,  this.lastActive+1);
37001                     this.grid.getView().focusRow(this.lastActive);
37002                     if(last !== false){
37003                         this.last = last;
37004                     }
37005                 }else{
37006                     this.selectFirstRow();
37007                 }
37008                 this.fireEvent("afterselectionchange", this);
37009             },
37010             scope: this
37011         });
37012
37013         var view = this.grid.view;
37014         view.on("refresh", this.onRefresh, this);
37015         view.on("rowupdated", this.onRowUpdated, this);
37016         view.on("rowremoved", this.onRemove, this);
37017     },
37018
37019     // private
37020     onRefresh : function(){
37021         var ds = this.grid.dataSource, i, v = this.grid.view;
37022         var s = this.selections;
37023         s.each(function(r){
37024             if((i = ds.indexOfId(r.id)) != -1){
37025                 v.onRowSelect(i);
37026             }else{
37027                 s.remove(r);
37028             }
37029         });
37030     },
37031
37032     // private
37033     onRemove : function(v, index, r){
37034         this.selections.remove(r);
37035     },
37036
37037     // private
37038     onRowUpdated : function(v, index, r){
37039         if(this.isSelected(r)){
37040             v.onRowSelect(index);
37041         }
37042     },
37043
37044     /**
37045      * Select records.
37046      * @param {Array} records The records to select
37047      * @param {Boolean} keepExisting (optional) True to keep existing selections
37048      */
37049     selectRecords : function(records, keepExisting){
37050         if(!keepExisting){
37051             this.clearSelections();
37052         }
37053         var ds = this.grid.dataSource;
37054         for(var i = 0, len = records.length; i < len; i++){
37055             this.selectRow(ds.indexOf(records[i]), true);
37056         }
37057     },
37058
37059     /**
37060      * Gets the number of selected rows.
37061      * @return {Number}
37062      */
37063     getCount : function(){
37064         return this.selections.length;
37065     },
37066
37067     /**
37068      * Selects the first row in the grid.
37069      */
37070     selectFirstRow : function(){
37071         this.selectRow(0);
37072     },
37073
37074     /**
37075      * Select the last row.
37076      * @param {Boolean} keepExisting (optional) True to keep existing selections
37077      */
37078     selectLastRow : function(keepExisting){
37079         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
37080     },
37081
37082     /**
37083      * Selects the row immediately following the last selected row.
37084      * @param {Boolean} keepExisting (optional) True to keep existing selections
37085      */
37086     selectNext : function(keepExisting){
37087         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
37088             this.selectRow(this.last+1, keepExisting);
37089             this.grid.getView().focusRow(this.last);
37090         }
37091     },
37092
37093     /**
37094      * Selects the row that precedes the last selected row.
37095      * @param {Boolean} keepExisting (optional) True to keep existing selections
37096      */
37097     selectPrevious : function(keepExisting){
37098         if(this.last){
37099             this.selectRow(this.last-1, keepExisting);
37100             this.grid.getView().focusRow(this.last);
37101         }
37102     },
37103
37104     /**
37105      * Returns the selected records
37106      * @return {Array} Array of selected records
37107      */
37108     getSelections : function(){
37109         return [].concat(this.selections.items);
37110     },
37111
37112     /**
37113      * Returns the first selected record.
37114      * @return {Record}
37115      */
37116     getSelected : function(){
37117         return this.selections.itemAt(0);
37118     },
37119
37120
37121     /**
37122      * Clears all selections.
37123      */
37124     clearSelections : function(fast){
37125         if(this.locked) return;
37126         if(fast !== true){
37127             var ds = this.grid.dataSource;
37128             var s = this.selections;
37129             s.each(function(r){
37130                 this.deselectRow(ds.indexOfId(r.id));
37131             }, this);
37132             s.clear();
37133         }else{
37134             this.selections.clear();
37135         }
37136         this.last = false;
37137     },
37138
37139
37140     /**
37141      * Selects all rows.
37142      */
37143     selectAll : function(){
37144         if(this.locked) return;
37145         this.selections.clear();
37146         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
37147             this.selectRow(i, true);
37148         }
37149     },
37150
37151     /**
37152      * Returns True if there is a selection.
37153      * @return {Boolean}
37154      */
37155     hasSelection : function(){
37156         return this.selections.length > 0;
37157     },
37158
37159     /**
37160      * Returns True if the specified row is selected.
37161      * @param {Number/Record} record The record or index of the record to check
37162      * @return {Boolean}
37163      */
37164     isSelected : function(index){
37165         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
37166         return (r && this.selections.key(r.id) ? true : false);
37167     },
37168
37169     /**
37170      * Returns True if the specified record id is selected.
37171      * @param {String} id The id of record to check
37172      * @return {Boolean}
37173      */
37174     isIdSelected : function(id){
37175         return (this.selections.key(id) ? true : false);
37176     },
37177
37178     // private
37179     handleMouseDown : function(e, t){
37180         var view = this.grid.getView(), rowIndex;
37181         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37182             return;
37183         };
37184         if(e.shiftKey && this.last !== false){
37185             var last = this.last;
37186             this.selectRange(last, rowIndex, e.ctrlKey);
37187             this.last = last; // reset the last
37188             view.focusRow(rowIndex);
37189         }else{
37190             var isSelected = this.isSelected(rowIndex);
37191             if(e.button !== 0 && isSelected){
37192                 view.focusRow(rowIndex);
37193             }else if(e.ctrlKey && isSelected){
37194                 this.deselectRow(rowIndex);
37195             }else if(!isSelected){
37196                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37197                 view.focusRow(rowIndex);
37198             }
37199         }
37200         this.fireEvent("afterselectionchange", this);
37201     },
37202     // private
37203     handleDragableRowClick :  function(grid, rowIndex, e) 
37204     {
37205         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37206             this.selectRow(rowIndex, false);
37207             grid.view.focusRow(rowIndex);
37208              this.fireEvent("afterselectionchange", this);
37209         }
37210     },
37211     
37212     /**
37213      * Selects multiple rows.
37214      * @param {Array} rows Array of the indexes of the row to select
37215      * @param {Boolean} keepExisting (optional) True to keep existing selections
37216      */
37217     selectRows : function(rows, keepExisting){
37218         if(!keepExisting){
37219             this.clearSelections();
37220         }
37221         for(var i = 0, len = rows.length; i < len; i++){
37222             this.selectRow(rows[i], true);
37223         }
37224     },
37225
37226     /**
37227      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37228      * @param {Number} startRow The index of the first row in the range
37229      * @param {Number} endRow The index of the last row in the range
37230      * @param {Boolean} keepExisting (optional) True to retain existing selections
37231      */
37232     selectRange : function(startRow, endRow, keepExisting){
37233         if(this.locked) return;
37234         if(!keepExisting){
37235             this.clearSelections();
37236         }
37237         if(startRow <= endRow){
37238             for(var i = startRow; i <= endRow; i++){
37239                 this.selectRow(i, true);
37240             }
37241         }else{
37242             for(var i = startRow; i >= endRow; i--){
37243                 this.selectRow(i, true);
37244             }
37245         }
37246     },
37247
37248     /**
37249      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37250      * @param {Number} startRow The index of the first row in the range
37251      * @param {Number} endRow The index of the last row in the range
37252      */
37253     deselectRange : function(startRow, endRow, preventViewNotify){
37254         if(this.locked) return;
37255         for(var i = startRow; i <= endRow; i++){
37256             this.deselectRow(i, preventViewNotify);
37257         }
37258     },
37259
37260     /**
37261      * Selects a row.
37262      * @param {Number} row The index of the row to select
37263      * @param {Boolean} keepExisting (optional) True to keep existing selections
37264      */
37265     selectRow : function(index, keepExisting, preventViewNotify){
37266         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
37267         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37268             if(!keepExisting || this.singleSelect){
37269                 this.clearSelections();
37270             }
37271             var r = this.grid.dataSource.getAt(index);
37272             this.selections.add(r);
37273             this.last = this.lastActive = index;
37274             if(!preventViewNotify){
37275                 this.grid.getView().onRowSelect(index);
37276             }
37277             this.fireEvent("rowselect", this, index, r);
37278             this.fireEvent("selectionchange", this);
37279         }
37280     },
37281
37282     /**
37283      * Deselects a row.
37284      * @param {Number} row The index of the row to deselect
37285      */
37286     deselectRow : function(index, preventViewNotify){
37287         if(this.locked) return;
37288         if(this.last == index){
37289             this.last = false;
37290         }
37291         if(this.lastActive == index){
37292             this.lastActive = false;
37293         }
37294         var r = this.grid.dataSource.getAt(index);
37295         this.selections.remove(r);
37296         if(!preventViewNotify){
37297             this.grid.getView().onRowDeselect(index);
37298         }
37299         this.fireEvent("rowdeselect", this, index);
37300         this.fireEvent("selectionchange", this);
37301     },
37302
37303     // private
37304     restoreLast : function(){
37305         if(this._last){
37306             this.last = this._last;
37307         }
37308     },
37309
37310     // private
37311     acceptsNav : function(row, col, cm){
37312         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37313     },
37314
37315     // private
37316     onEditorKey : function(field, e){
37317         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37318         if(k == e.TAB){
37319             e.stopEvent();
37320             ed.completeEdit();
37321             if(e.shiftKey){
37322                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37323             }else{
37324                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37325             }
37326         }else if(k == e.ENTER && !e.ctrlKey){
37327             e.stopEvent();
37328             ed.completeEdit();
37329             if(e.shiftKey){
37330                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37331             }else{
37332                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37333             }
37334         }else if(k == e.ESC){
37335             ed.cancelEdit();
37336         }
37337         if(newCell){
37338             g.startEditing(newCell[0], newCell[1]);
37339         }
37340     }
37341 });/*
37342  * Based on:
37343  * Ext JS Library 1.1.1
37344  * Copyright(c) 2006-2007, Ext JS, LLC.
37345  *
37346  * Originally Released Under LGPL - original licence link has changed is not relivant.
37347  *
37348  * Fork - LGPL
37349  * <script type="text/javascript">
37350  */
37351 /**
37352  * @class Roo.grid.CellSelectionModel
37353  * @extends Roo.grid.AbstractSelectionModel
37354  * This class provides the basic implementation for cell selection in a grid.
37355  * @constructor
37356  * @param {Object} config The object containing the configuration of this model.
37357  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37358  */
37359 Roo.grid.CellSelectionModel = function(config){
37360     Roo.apply(this, config);
37361
37362     this.selection = null;
37363
37364     this.addEvents({
37365         /**
37366              * @event beforerowselect
37367              * Fires before a cell is selected.
37368              * @param {SelectionModel} this
37369              * @param {Number} rowIndex The selected row index
37370              * @param {Number} colIndex The selected cell index
37371              */
37372             "beforecellselect" : true,
37373         /**
37374              * @event cellselect
37375              * Fires when a cell is selected.
37376              * @param {SelectionModel} this
37377              * @param {Number} rowIndex The selected row index
37378              * @param {Number} colIndex The selected cell index
37379              */
37380             "cellselect" : true,
37381         /**
37382              * @event selectionchange
37383              * Fires when the active selection changes.
37384              * @param {SelectionModel} this
37385              * @param {Object} selection null for no selection or an object (o) with two properties
37386                 <ul>
37387                 <li>o.record: the record object for the row the selection is in</li>
37388                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37389                 </ul>
37390              */
37391             "selectionchange" : true,
37392         /**
37393              * @event tabend
37394              * Fires when the tab (or enter) was pressed on the last editable cell
37395              * You can use this to trigger add new row.
37396              * @param {SelectionModel} this
37397              */
37398             "tabend" : true,
37399          /**
37400              * @event beforeeditnext
37401              * Fires before the next editable sell is made active
37402              * You can use this to skip to another cell or fire the tabend
37403              *    if you set cell to false
37404              * @param {Object} eventdata object : { cell : [ row, col ] } 
37405              */
37406             "beforeeditnext" : true
37407     });
37408     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37409 };
37410
37411 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37412     
37413     enter_is_tab: false,
37414
37415     /** @ignore */
37416     initEvents : function(){
37417         this.grid.on("mousedown", this.handleMouseDown, this);
37418         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37419         var view = this.grid.view;
37420         view.on("refresh", this.onViewChange, this);
37421         view.on("rowupdated", this.onRowUpdated, this);
37422         view.on("beforerowremoved", this.clearSelections, this);
37423         view.on("beforerowsinserted", this.clearSelections, this);
37424         if(this.grid.isEditor){
37425             this.grid.on("beforeedit", this.beforeEdit,  this);
37426         }
37427     },
37428
37429         //private
37430     beforeEdit : function(e){
37431         this.select(e.row, e.column, false, true, e.record);
37432     },
37433
37434         //private
37435     onRowUpdated : function(v, index, r){
37436         if(this.selection && this.selection.record == r){
37437             v.onCellSelect(index, this.selection.cell[1]);
37438         }
37439     },
37440
37441         //private
37442     onViewChange : function(){
37443         this.clearSelections(true);
37444     },
37445
37446         /**
37447          * Returns the currently selected cell,.
37448          * @return {Array} The selected cell (row, column) or null if none selected.
37449          */
37450     getSelectedCell : function(){
37451         return this.selection ? this.selection.cell : null;
37452     },
37453
37454     /**
37455      * Clears all selections.
37456      * @param {Boolean} true to prevent the gridview from being notified about the change.
37457      */
37458     clearSelections : function(preventNotify){
37459         var s = this.selection;
37460         if(s){
37461             if(preventNotify !== true){
37462                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37463             }
37464             this.selection = null;
37465             this.fireEvent("selectionchange", this, null);
37466         }
37467     },
37468
37469     /**
37470      * Returns true if there is a selection.
37471      * @return {Boolean}
37472      */
37473     hasSelection : function(){
37474         return this.selection ? true : false;
37475     },
37476
37477     /** @ignore */
37478     handleMouseDown : function(e, t){
37479         var v = this.grid.getView();
37480         if(this.isLocked()){
37481             return;
37482         };
37483         var row = v.findRowIndex(t);
37484         var cell = v.findCellIndex(t);
37485         if(row !== false && cell !== false){
37486             this.select(row, cell);
37487         }
37488     },
37489
37490     /**
37491      * Selects a cell.
37492      * @param {Number} rowIndex
37493      * @param {Number} collIndex
37494      */
37495     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37496         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37497             this.clearSelections();
37498             r = r || this.grid.dataSource.getAt(rowIndex);
37499             this.selection = {
37500                 record : r,
37501                 cell : [rowIndex, colIndex]
37502             };
37503             if(!preventViewNotify){
37504                 var v = this.grid.getView();
37505                 v.onCellSelect(rowIndex, colIndex);
37506                 if(preventFocus !== true){
37507                     v.focusCell(rowIndex, colIndex);
37508                 }
37509             }
37510             this.fireEvent("cellselect", this, rowIndex, colIndex);
37511             this.fireEvent("selectionchange", this, this.selection);
37512         }
37513     },
37514
37515         //private
37516     isSelectable : function(rowIndex, colIndex, cm){
37517         return !cm.isHidden(colIndex);
37518     },
37519
37520     /** @ignore */
37521     handleKeyDown : function(e){
37522         //Roo.log('Cell Sel Model handleKeyDown');
37523         if(!e.isNavKeyPress()){
37524             return;
37525         }
37526         var g = this.grid, s = this.selection;
37527         if(!s){
37528             e.stopEvent();
37529             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
37530             if(cell){
37531                 this.select(cell[0], cell[1]);
37532             }
37533             return;
37534         }
37535         var sm = this;
37536         var walk = function(row, col, step){
37537             return g.walkCells(row, col, step, sm.isSelectable,  sm);
37538         };
37539         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37540         var newCell;
37541
37542       
37543
37544         switch(k){
37545             case e.TAB:
37546                 // handled by onEditorKey
37547                 if (g.isEditor && g.editing) {
37548                     return;
37549                 }
37550                 if(e.shiftKey) {
37551                     newCell = walk(r, c-1, -1);
37552                 } else {
37553                     newCell = walk(r, c+1, 1);
37554                 }
37555                 break;
37556             
37557             case e.DOWN:
37558                newCell = walk(r+1, c, 1);
37559                 break;
37560             
37561             case e.UP:
37562                 newCell = walk(r-1, c, -1);
37563                 break;
37564             
37565             case e.RIGHT:
37566                 newCell = walk(r, c+1, 1);
37567                 break;
37568             
37569             case e.LEFT:
37570                 newCell = walk(r, c-1, -1);
37571                 break;
37572             
37573             case e.ENTER:
37574                 
37575                 if(g.isEditor && !g.editing){
37576                    g.startEditing(r, c);
37577                    e.stopEvent();
37578                    return;
37579                 }
37580                 
37581                 
37582              break;
37583         };
37584         if(newCell){
37585             this.select(newCell[0], newCell[1]);
37586             e.stopEvent();
37587             
37588         }
37589     },
37590
37591     acceptsNav : function(row, col, cm){
37592         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37593     },
37594     /**
37595      * Selects a cell.
37596      * @param {Number} field (not used) - as it's normally used as a listener
37597      * @param {Number} e - event - fake it by using
37598      *
37599      * var e = Roo.EventObjectImpl.prototype;
37600      * e.keyCode = e.TAB
37601      *
37602      * 
37603      */
37604     onEditorKey : function(field, e){
37605         
37606         var k = e.getKey(),
37607             newCell,
37608             g = this.grid,
37609             ed = g.activeEditor,
37610             forward = false;
37611         ///Roo.log('onEditorKey' + k);
37612         
37613         
37614         if (this.enter_is_tab && k == e.ENTER) {
37615             k = e.TAB;
37616         }
37617         
37618         if(k == e.TAB){
37619             if(e.shiftKey){
37620                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37621             }else{
37622                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37623                 forward = true;
37624             }
37625             
37626             e.stopEvent();
37627             
37628         } else if(k == e.ENTER &&  !e.ctrlKey){
37629             ed.completeEdit();
37630             e.stopEvent();
37631             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37632         
37633                 } else if(k == e.ESC){
37634             ed.cancelEdit();
37635         }
37636                 
37637         if (newCell) {
37638             var ecall = { cell : newCell, forward : forward };
37639             this.fireEvent('beforeeditnext', ecall );
37640             newCell = ecall.cell;
37641                         forward = ecall.forward;
37642         }
37643                 
37644         if(newCell){
37645             //Roo.log('next cell after edit');
37646             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37647         } else if (forward) {
37648             // tabbed past last
37649             this.fireEvent.defer(100, this, ['tabend',this]);
37650         }
37651     }
37652 });/*
37653  * Based on:
37654  * Ext JS Library 1.1.1
37655  * Copyright(c) 2006-2007, Ext JS, LLC.
37656  *
37657  * Originally Released Under LGPL - original licence link has changed is not relivant.
37658  *
37659  * Fork - LGPL
37660  * <script type="text/javascript">
37661  */
37662  
37663 /**
37664  * @class Roo.grid.EditorGrid
37665  * @extends Roo.grid.Grid
37666  * Class for creating and editable grid.
37667  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37668  * The container MUST have some type of size defined for the grid to fill. The container will be 
37669  * automatically set to position relative if it isn't already.
37670  * @param {Object} dataSource The data model to bind to
37671  * @param {Object} colModel The column model with info about this grid's columns
37672  */
37673 Roo.grid.EditorGrid = function(container, config){
37674     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37675     this.getGridEl().addClass("xedit-grid");
37676
37677     if(!this.selModel){
37678         this.selModel = new Roo.grid.CellSelectionModel();
37679     }
37680
37681     this.activeEditor = null;
37682
37683         this.addEvents({
37684             /**
37685              * @event beforeedit
37686              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37687              * <ul style="padding:5px;padding-left:16px;">
37688              * <li>grid - This grid</li>
37689              * <li>record - The record being edited</li>
37690              * <li>field - The field name being edited</li>
37691              * <li>value - The value for the field being edited.</li>
37692              * <li>row - The grid row index</li>
37693              * <li>column - The grid column index</li>
37694              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37695              * </ul>
37696              * @param {Object} e An edit event (see above for description)
37697              */
37698             "beforeedit" : true,
37699             /**
37700              * @event afteredit
37701              * Fires after a cell is edited. <br />
37702              * <ul style="padding:5px;padding-left:16px;">
37703              * <li>grid - This grid</li>
37704              * <li>record - The record being edited</li>
37705              * <li>field - The field name being edited</li>
37706              * <li>value - The value being set</li>
37707              * <li>originalValue - The original value for the field, before the edit.</li>
37708              * <li>row - The grid row index</li>
37709              * <li>column - The grid column index</li>
37710              * </ul>
37711              * @param {Object} e An edit event (see above for description)
37712              */
37713             "afteredit" : true,
37714             /**
37715              * @event validateedit
37716              * Fires after a cell is edited, but before the value is set in the record. 
37717          * You can use this to modify the value being set in the field, Return false
37718              * to cancel the change. The edit event object has the following properties <br />
37719              * <ul style="padding:5px;padding-left:16px;">
37720          * <li>editor - This editor</li>
37721              * <li>grid - This grid</li>
37722              * <li>record - The record being edited</li>
37723              * <li>field - The field name being edited</li>
37724              * <li>value - The value being set</li>
37725              * <li>originalValue - The original value for the field, before the edit.</li>
37726              * <li>row - The grid row index</li>
37727              * <li>column - The grid column index</li>
37728              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37729              * </ul>
37730              * @param {Object} e An edit event (see above for description)
37731              */
37732             "validateedit" : true
37733         });
37734     this.on("bodyscroll", this.stopEditing,  this);
37735     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37736 };
37737
37738 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37739     /**
37740      * @cfg {Number} clicksToEdit
37741      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37742      */
37743     clicksToEdit: 2,
37744
37745     // private
37746     isEditor : true,
37747     // private
37748     trackMouseOver: false, // causes very odd FF errors
37749
37750     onCellDblClick : function(g, row, col){
37751         this.startEditing(row, col);
37752     },
37753
37754     onEditComplete : function(ed, value, startValue){
37755         this.editing = false;
37756         this.activeEditor = null;
37757         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37758         var r = ed.record;
37759         var field = this.colModel.getDataIndex(ed.col);
37760         var e = {
37761             grid: this,
37762             record: r,
37763             field: field,
37764             originalValue: startValue,
37765             value: value,
37766             row: ed.row,
37767             column: ed.col,
37768             cancel:false,
37769             editor: ed
37770         };
37771         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37772         cell.show();
37773           
37774         if(String(value) !== String(startValue)){
37775             
37776             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37777                 r.set(field, e.value);
37778                 // if we are dealing with a combo box..
37779                 // then we also set the 'name' colum to be the displayField
37780                 if (ed.field.displayField && ed.field.name) {
37781                     r.set(ed.field.name, ed.field.el.dom.value);
37782                 }
37783                 
37784                 delete e.cancel; //?? why!!!
37785                 this.fireEvent("afteredit", e);
37786             }
37787         } else {
37788             this.fireEvent("afteredit", e); // always fire it!
37789         }
37790         this.view.focusCell(ed.row, ed.col);
37791     },
37792
37793     /**
37794      * Starts editing the specified for the specified row/column
37795      * @param {Number} rowIndex
37796      * @param {Number} colIndex
37797      */
37798     startEditing : function(row, col){
37799         this.stopEditing();
37800         if(this.colModel.isCellEditable(col, row)){
37801             this.view.ensureVisible(row, col, true);
37802           
37803             var r = this.dataSource.getAt(row);
37804             var field = this.colModel.getDataIndex(col);
37805             var cell = Roo.get(this.view.getCell(row,col));
37806             var e = {
37807                 grid: this,
37808                 record: r,
37809                 field: field,
37810                 value: r.data[field],
37811                 row: row,
37812                 column: col,
37813                 cancel:false 
37814             };
37815             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37816                 this.editing = true;
37817                 var ed = this.colModel.getCellEditor(col, row);
37818                 
37819                 if (!ed) {
37820                     return;
37821                 }
37822                 if(!ed.rendered){
37823                     ed.render(ed.parentEl || document.body);
37824                 }
37825                 ed.field.reset();
37826                
37827                 cell.hide();
37828                 
37829                 (function(){ // complex but required for focus issues in safari, ie and opera
37830                     ed.row = row;
37831                     ed.col = col;
37832                     ed.record = r;
37833                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37834                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37835                     this.activeEditor = ed;
37836                     var v = r.data[field];
37837                     ed.startEdit(this.view.getCell(row, col), v);
37838                     // combo's with 'displayField and name set
37839                     if (ed.field.displayField && ed.field.name) {
37840                         ed.field.el.dom.value = r.data[ed.field.name];
37841                     }
37842                     
37843                     
37844                 }).defer(50, this);
37845             }
37846         }
37847     },
37848         
37849     /**
37850      * Stops any active editing
37851      */
37852     stopEditing : function(){
37853         if(this.activeEditor){
37854             this.activeEditor.completeEdit();
37855         }
37856         this.activeEditor = null;
37857     }
37858 });/*
37859  * Based on:
37860  * Ext JS Library 1.1.1
37861  * Copyright(c) 2006-2007, Ext JS, LLC.
37862  *
37863  * Originally Released Under LGPL - original licence link has changed is not relivant.
37864  *
37865  * Fork - LGPL
37866  * <script type="text/javascript">
37867  */
37868
37869 // private - not really -- you end up using it !
37870 // This is a support class used internally by the Grid components
37871
37872 /**
37873  * @class Roo.grid.GridEditor
37874  * @extends Roo.Editor
37875  * Class for creating and editable grid elements.
37876  * @param {Object} config any settings (must include field)
37877  */
37878 Roo.grid.GridEditor = function(field, config){
37879     if (!config && field.field) {
37880         config = field;
37881         field = Roo.factory(config.field, Roo.form);
37882     }
37883     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37884     field.monitorTab = false;
37885 };
37886
37887 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37888     
37889     /**
37890      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37891      */
37892     
37893     alignment: "tl-tl",
37894     autoSize: "width",
37895     hideEl : false,
37896     cls: "x-small-editor x-grid-editor",
37897     shim:false,
37898     shadow:"frame"
37899 });/*
37900  * Based on:
37901  * Ext JS Library 1.1.1
37902  * Copyright(c) 2006-2007, Ext JS, LLC.
37903  *
37904  * Originally Released Under LGPL - original licence link has changed is not relivant.
37905  *
37906  * Fork - LGPL
37907  * <script type="text/javascript">
37908  */
37909   
37910
37911   
37912 Roo.grid.PropertyRecord = Roo.data.Record.create([
37913     {name:'name',type:'string'},  'value'
37914 ]);
37915
37916
37917 Roo.grid.PropertyStore = function(grid, source){
37918     this.grid = grid;
37919     this.store = new Roo.data.Store({
37920         recordType : Roo.grid.PropertyRecord
37921     });
37922     this.store.on('update', this.onUpdate,  this);
37923     if(source){
37924         this.setSource(source);
37925     }
37926     Roo.grid.PropertyStore.superclass.constructor.call(this);
37927 };
37928
37929
37930
37931 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37932     setSource : function(o){
37933         this.source = o;
37934         this.store.removeAll();
37935         var data = [];
37936         for(var k in o){
37937             if(this.isEditableValue(o[k])){
37938                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37939             }
37940         }
37941         this.store.loadRecords({records: data}, {}, true);
37942     },
37943
37944     onUpdate : function(ds, record, type){
37945         if(type == Roo.data.Record.EDIT){
37946             var v = record.data['value'];
37947             var oldValue = record.modified['value'];
37948             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37949                 this.source[record.id] = v;
37950                 record.commit();
37951                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37952             }else{
37953                 record.reject();
37954             }
37955         }
37956     },
37957
37958     getProperty : function(row){
37959        return this.store.getAt(row);
37960     },
37961
37962     isEditableValue: function(val){
37963         if(val && val instanceof Date){
37964             return true;
37965         }else if(typeof val == 'object' || typeof val == 'function'){
37966             return false;
37967         }
37968         return true;
37969     },
37970
37971     setValue : function(prop, value){
37972         this.source[prop] = value;
37973         this.store.getById(prop).set('value', value);
37974     },
37975
37976     getSource : function(){
37977         return this.source;
37978     }
37979 });
37980
37981 Roo.grid.PropertyColumnModel = function(grid, store){
37982     this.grid = grid;
37983     var g = Roo.grid;
37984     g.PropertyColumnModel.superclass.constructor.call(this, [
37985         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37986         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37987     ]);
37988     this.store = store;
37989     this.bselect = Roo.DomHelper.append(document.body, {
37990         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37991             {tag: 'option', value: 'true', html: 'true'},
37992             {tag: 'option', value: 'false', html: 'false'}
37993         ]
37994     });
37995     Roo.id(this.bselect);
37996     var f = Roo.form;
37997     this.editors = {
37998         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37999         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38000         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38001         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38002         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38003     };
38004     this.renderCellDelegate = this.renderCell.createDelegate(this);
38005     this.renderPropDelegate = this.renderProp.createDelegate(this);
38006 };
38007
38008 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38009     
38010     
38011     nameText : 'Name',
38012     valueText : 'Value',
38013     
38014     dateFormat : 'm/j/Y',
38015     
38016     
38017     renderDate : function(dateVal){
38018         return dateVal.dateFormat(this.dateFormat);
38019     },
38020
38021     renderBool : function(bVal){
38022         return bVal ? 'true' : 'false';
38023     },
38024
38025     isCellEditable : function(colIndex, rowIndex){
38026         return colIndex == 1;
38027     },
38028
38029     getRenderer : function(col){
38030         return col == 1 ?
38031             this.renderCellDelegate : this.renderPropDelegate;
38032     },
38033
38034     renderProp : function(v){
38035         return this.getPropertyName(v);
38036     },
38037
38038     renderCell : function(val){
38039         var rv = val;
38040         if(val instanceof Date){
38041             rv = this.renderDate(val);
38042         }else if(typeof val == 'boolean'){
38043             rv = this.renderBool(val);
38044         }
38045         return Roo.util.Format.htmlEncode(rv);
38046     },
38047
38048     getPropertyName : function(name){
38049         var pn = this.grid.propertyNames;
38050         return pn && pn[name] ? pn[name] : name;
38051     },
38052
38053     getCellEditor : function(colIndex, rowIndex){
38054         var p = this.store.getProperty(rowIndex);
38055         var n = p.data['name'], val = p.data['value'];
38056         
38057         if(typeof(this.grid.customEditors[n]) == 'string'){
38058             return this.editors[this.grid.customEditors[n]];
38059         }
38060         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38061             return this.grid.customEditors[n];
38062         }
38063         if(val instanceof Date){
38064             return this.editors['date'];
38065         }else if(typeof val == 'number'){
38066             return this.editors['number'];
38067         }else if(typeof val == 'boolean'){
38068             return this.editors['boolean'];
38069         }else{
38070             return this.editors['string'];
38071         }
38072     }
38073 });
38074
38075 /**
38076  * @class Roo.grid.PropertyGrid
38077  * @extends Roo.grid.EditorGrid
38078  * This class represents the  interface of a component based property grid control.
38079  * <br><br>Usage:<pre><code>
38080  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38081       
38082  });
38083  // set any options
38084  grid.render();
38085  * </code></pre>
38086   
38087  * @constructor
38088  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38089  * The container MUST have some type of size defined for the grid to fill. The container will be
38090  * automatically set to position relative if it isn't already.
38091  * @param {Object} config A config object that sets properties on this grid.
38092  */
38093 Roo.grid.PropertyGrid = function(container, config){
38094     config = config || {};
38095     var store = new Roo.grid.PropertyStore(this);
38096     this.store = store;
38097     var cm = new Roo.grid.PropertyColumnModel(this, store);
38098     store.store.sort('name', 'ASC');
38099     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38100         ds: store.store,
38101         cm: cm,
38102         enableColLock:false,
38103         enableColumnMove:false,
38104         stripeRows:false,
38105         trackMouseOver: false,
38106         clicksToEdit:1
38107     }, config));
38108     this.getGridEl().addClass('x-props-grid');
38109     this.lastEditRow = null;
38110     this.on('columnresize', this.onColumnResize, this);
38111     this.addEvents({
38112          /**
38113              * @event beforepropertychange
38114              * Fires before a property changes (return false to stop?)
38115              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38116              * @param {String} id Record Id
38117              * @param {String} newval New Value
38118          * @param {String} oldval Old Value
38119              */
38120         "beforepropertychange": true,
38121         /**
38122              * @event propertychange
38123              * Fires after a property changes
38124              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38125              * @param {String} id Record Id
38126              * @param {String} newval New Value
38127          * @param {String} oldval Old Value
38128              */
38129         "propertychange": true
38130     });
38131     this.customEditors = this.customEditors || {};
38132 };
38133 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38134     
38135      /**
38136      * @cfg {Object} customEditors map of colnames=> custom editors.
38137      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38138      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38139      * false disables editing of the field.
38140          */
38141     
38142       /**
38143      * @cfg {Object} propertyNames map of property Names to their displayed value
38144          */
38145     
38146     render : function(){
38147         Roo.grid.PropertyGrid.superclass.render.call(this);
38148         this.autoSize.defer(100, this);
38149     },
38150
38151     autoSize : function(){
38152         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38153         if(this.view){
38154             this.view.fitColumns();
38155         }
38156     },
38157
38158     onColumnResize : function(){
38159         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38160         this.autoSize();
38161     },
38162     /**
38163      * Sets the data for the Grid
38164      * accepts a Key => Value object of all the elements avaiable.
38165      * @param {Object} data  to appear in grid.
38166      */
38167     setSource : function(source){
38168         this.store.setSource(source);
38169         //this.autoSize();
38170     },
38171     /**
38172      * Gets all the data from the grid.
38173      * @return {Object} data  data stored in grid
38174      */
38175     getSource : function(){
38176         return this.store.getSource();
38177     }
38178 });/*
38179  * Based on:
38180  * Ext JS Library 1.1.1
38181  * Copyright(c) 2006-2007, Ext JS, LLC.
38182  *
38183  * Originally Released Under LGPL - original licence link has changed is not relivant.
38184  *
38185  * Fork - LGPL
38186  * <script type="text/javascript">
38187  */
38188  
38189 /**
38190  * @class Roo.LoadMask
38191  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38192  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38193  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38194  * element's UpdateManager load indicator and will be destroyed after the initial load.
38195  * @constructor
38196  * Create a new LoadMask
38197  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38198  * @param {Object} config The config object
38199  */
38200 Roo.LoadMask = function(el, config){
38201     this.el = Roo.get(el);
38202     Roo.apply(this, config);
38203     if(this.store){
38204         this.store.on('beforeload', this.onBeforeLoad, this);
38205         this.store.on('load', this.onLoad, this);
38206         this.store.on('loadexception', this.onLoadException, this);
38207         this.removeMask = false;
38208     }else{
38209         var um = this.el.getUpdateManager();
38210         um.showLoadIndicator = false; // disable the default indicator
38211         um.on('beforeupdate', this.onBeforeLoad, this);
38212         um.on('update', this.onLoad, this);
38213         um.on('failure', this.onLoad, this);
38214         this.removeMask = true;
38215     }
38216 };
38217
38218 Roo.LoadMask.prototype = {
38219     /**
38220      * @cfg {Boolean} removeMask
38221      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38222      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38223      */
38224     /**
38225      * @cfg {String} msg
38226      * The text to display in a centered loading message box (defaults to 'Loading...')
38227      */
38228     msg : 'Loading...',
38229     /**
38230      * @cfg {String} msgCls
38231      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38232      */
38233     msgCls : 'x-mask-loading',
38234
38235     /**
38236      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38237      * @type Boolean
38238      */
38239     disabled: false,
38240
38241     /**
38242      * Disables the mask to prevent it from being displayed
38243      */
38244     disable : function(){
38245        this.disabled = true;
38246     },
38247
38248     /**
38249      * Enables the mask so that it can be displayed
38250      */
38251     enable : function(){
38252         this.disabled = false;
38253     },
38254     
38255     onLoadException : function()
38256     {
38257         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38258             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38259         }
38260         this.el.unmask(this.removeMask);
38261     },
38262     // private
38263     onLoad : function()
38264     {
38265         this.el.unmask(this.removeMask);
38266     },
38267
38268     // private
38269     onBeforeLoad : function(){
38270         if(!this.disabled){
38271             this.el.mask(this.msg, this.msgCls);
38272         }
38273     },
38274
38275     // private
38276     destroy : function(){
38277         if(this.store){
38278             this.store.un('beforeload', this.onBeforeLoad, this);
38279             this.store.un('load', this.onLoad, this);
38280             this.store.un('loadexception', this.onLoadException, this);
38281         }else{
38282             var um = this.el.getUpdateManager();
38283             um.un('beforeupdate', this.onBeforeLoad, this);
38284             um.un('update', this.onLoad, this);
38285             um.un('failure', this.onLoad, this);
38286         }
38287     }
38288 };/*
38289  * Based on:
38290  * Ext JS Library 1.1.1
38291  * Copyright(c) 2006-2007, Ext JS, LLC.
38292  *
38293  * Originally Released Under LGPL - original licence link has changed is not relivant.
38294  *
38295  * Fork - LGPL
38296  * <script type="text/javascript">
38297  */
38298
38299
38300 /**
38301  * @class Roo.XTemplate
38302  * @extends Roo.Template
38303  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38304 <pre><code>
38305 var t = new Roo.XTemplate(
38306         '&lt;select name="{name}"&gt;',
38307                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38308         '&lt;/select&gt;'
38309 );
38310  
38311 // then append, applying the master template values
38312  </code></pre>
38313  *
38314  * Supported features:
38315  *
38316  *  Tags:
38317
38318 <pre><code>
38319       {a_variable} - output encoded.
38320       {a_variable.format:("Y-m-d")} - call a method on the variable
38321       {a_variable:raw} - unencoded output
38322       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38323       {a_variable:this.method_on_template(...)} - call a method on the template object.
38324  
38325 </code></pre>
38326  *  The tpl tag:
38327 <pre><code>
38328         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38329         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38330         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38331         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38332   
38333         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38334         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38335 </code></pre>
38336  *      
38337  */
38338 Roo.XTemplate = function()
38339 {
38340     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38341     if (this.html) {
38342         this.compile();
38343     }
38344 };
38345
38346
38347 Roo.extend(Roo.XTemplate, Roo.Template, {
38348
38349     /**
38350      * The various sub templates
38351      */
38352     tpls : false,
38353     /**
38354      *
38355      * basic tag replacing syntax
38356      * WORD:WORD()
38357      *
38358      * // you can fake an object call by doing this
38359      *  x.t:(test,tesT) 
38360      * 
38361      */
38362     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38363
38364     /**
38365      * compile the template
38366      *
38367      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38368      *
38369      */
38370     compile: function()
38371     {
38372         var s = this.html;
38373      
38374         s = ['<tpl>', s, '</tpl>'].join('');
38375     
38376         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38377             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38378             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38379             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38380             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38381             m,
38382             id     = 0,
38383             tpls   = [];
38384     
38385         while(true == !!(m = s.match(re))){
38386             var forMatch   = m[0].match(nameRe),
38387                 ifMatch   = m[0].match(ifRe),
38388                 execMatch   = m[0].match(execRe),
38389                 namedMatch   = m[0].match(namedRe),
38390                 
38391                 exp  = null, 
38392                 fn   = null,
38393                 exec = null,
38394                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38395                 
38396             if (ifMatch) {
38397                 // if - puts fn into test..
38398                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38399                 if(exp){
38400                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38401                 }
38402             }
38403             
38404             if (execMatch) {
38405                 // exec - calls a function... returns empty if true is  returned.
38406                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38407                 if(exp){
38408                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38409                 }
38410             }
38411             
38412             
38413             if (name) {
38414                 // for = 
38415                 switch(name){
38416                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38417                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38418                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38419                 }
38420             }
38421             var uid = namedMatch ? namedMatch[1] : id;
38422             
38423             
38424             tpls.push({
38425                 id:     namedMatch ? namedMatch[1] : id,
38426                 target: name,
38427                 exec:   exec,
38428                 test:   fn,
38429                 body:   m[1] || ''
38430             });
38431             if (namedMatch) {
38432                 s = s.replace(m[0], '');
38433             } else { 
38434                 s = s.replace(m[0], '{xtpl'+ id + '}');
38435             }
38436             ++id;
38437         }
38438         this.tpls = [];
38439         for(var i = tpls.length-1; i >= 0; --i){
38440             this.compileTpl(tpls[i]);
38441             this.tpls[tpls[i].id] = tpls[i];
38442         }
38443         this.master = tpls[tpls.length-1];
38444         return this;
38445     },
38446     /**
38447      * same as applyTemplate, except it's done to one of the subTemplates
38448      * when using named templates, you can do:
38449      *
38450      * var str = pl.applySubTemplate('your-name', values);
38451      *
38452      * 
38453      * @param {Number} id of the template
38454      * @param {Object} values to apply to template
38455      * @param {Object} parent (normaly the instance of this object)
38456      */
38457     applySubTemplate : function(id, values, parent)
38458     {
38459         
38460         
38461         var t = this.tpls[id];
38462         
38463         
38464         try { 
38465             if(t.test && !t.test.call(this, values, parent)){
38466                 return '';
38467             }
38468         } catch(e) {
38469             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38470             Roo.log(e.toString());
38471             Roo.log(t.test);
38472             return ''
38473         }
38474         try { 
38475             
38476             if(t.exec && t.exec.call(this, values, parent)){
38477                 return '';
38478             }
38479         } catch(e) {
38480             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38481             Roo.log(e.toString());
38482             Roo.log(t.exec);
38483             return ''
38484         }
38485         try {
38486             var vs = t.target ? t.target.call(this, values, parent) : values;
38487             parent = t.target ? values : parent;
38488             if(t.target && vs instanceof Array){
38489                 var buf = [];
38490                 for(var i = 0, len = vs.length; i < len; i++){
38491                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38492                 }
38493                 return buf.join('');
38494             }
38495             return t.compiled.call(this, vs, parent);
38496         } catch (e) {
38497             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38498             Roo.log(e.toString());
38499             Roo.log(t.compiled);
38500             return '';
38501         }
38502     },
38503
38504     compileTpl : function(tpl)
38505     {
38506         var fm = Roo.util.Format;
38507         var useF = this.disableFormats !== true;
38508         var sep = Roo.isGecko ? "+" : ",";
38509         var undef = function(str) {
38510             Roo.log("Property not found :"  + str);
38511             return '';
38512         };
38513         
38514         var fn = function(m, name, format, args)
38515         {
38516             //Roo.log(arguments);
38517             args = args ? args.replace(/\\'/g,"'") : args;
38518             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38519             if (typeof(format) == 'undefined') {
38520                 format= 'htmlEncode';
38521             }
38522             if (format == 'raw' ) {
38523                 format = false;
38524             }
38525             
38526             if(name.substr(0, 4) == 'xtpl'){
38527                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38528             }
38529             
38530             // build an array of options to determine if value is undefined..
38531             
38532             // basically get 'xxxx.yyyy' then do
38533             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38534             //    (function () { Roo.log("Property not found"); return ''; })() :
38535             //    ......
38536             
38537             var udef_ar = [];
38538             var lookfor = '';
38539             Roo.each(name.split('.'), function(st) {
38540                 lookfor += (lookfor.length ? '.': '') + st;
38541                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38542             });
38543             
38544             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38545             
38546             
38547             if(format && useF){
38548                 
38549                 args = args ? ',' + args : "";
38550                  
38551                 if(format.substr(0, 5) != "this."){
38552                     format = "fm." + format + '(';
38553                 }else{
38554                     format = 'this.call("'+ format.substr(5) + '", ';
38555                     args = ", values";
38556                 }
38557                 
38558                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38559             }
38560              
38561             if (args.length) {
38562                 // called with xxyx.yuu:(test,test)
38563                 // change to ()
38564                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38565             }
38566             // raw.. - :raw modifier..
38567             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38568             
38569         };
38570         var body;
38571         // branched to use + in gecko and [].join() in others
38572         if(Roo.isGecko){
38573             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38574                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38575                     "';};};";
38576         }else{
38577             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38578             body.push(tpl.body.replace(/(\r\n|\n)/g,
38579                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38580             body.push("'].join('');};};");
38581             body = body.join('');
38582         }
38583         
38584         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38585        
38586         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38587         eval(body);
38588         
38589         return this;
38590     },
38591
38592     applyTemplate : function(values){
38593         return this.master.compiled.call(this, values, {});
38594         //var s = this.subs;
38595     },
38596
38597     apply : function(){
38598         return this.applyTemplate.apply(this, arguments);
38599     }
38600
38601  });
38602
38603 Roo.XTemplate.from = function(el){
38604     el = Roo.getDom(el);
38605     return new Roo.XTemplate(el.value || el.innerHTML);
38606 };/*
38607  * Original code for Roojs - LGPL
38608  * <script type="text/javascript">
38609  */
38610  
38611 /**
38612  * @class Roo.XComponent
38613  * A delayed Element creator...
38614  * Or a way to group chunks of interface together.
38615  * 
38616  * Mypart.xyx = new Roo.XComponent({
38617
38618     parent : 'Mypart.xyz', // empty == document.element.!!
38619     order : '001',
38620     name : 'xxxx'
38621     region : 'xxxx'
38622     disabled : function() {} 
38623      
38624     tree : function() { // return an tree of xtype declared components
38625         var MODULE = this;
38626         return 
38627         {
38628             xtype : 'NestedLayoutPanel',
38629             // technicall
38630         }
38631      ]
38632  *})
38633  *
38634  *
38635  * It can be used to build a big heiracy, with parent etc.
38636  * or you can just use this to render a single compoent to a dom element
38637  * MYPART.render(Roo.Element | String(id) | dom_element )
38638  * 
38639  * @extends Roo.util.Observable
38640  * @constructor
38641  * @param cfg {Object} configuration of component
38642  * 
38643  */
38644 Roo.XComponent = function(cfg) {
38645     Roo.apply(this, cfg);
38646     this.addEvents({ 
38647         /**
38648              * @event built
38649              * Fires when this the componnt is built
38650              * @param {Roo.XComponent} c the component
38651              */
38652         'built' : true
38653         
38654     });
38655     this.region = this.region || 'center'; // default..
38656     Roo.XComponent.register(this);
38657     this.modules = false;
38658     this.el = false; // where the layout goes..
38659     
38660     
38661 }
38662 Roo.extend(Roo.XComponent, Roo.util.Observable, {
38663     /**
38664      * @property el
38665      * The created element (with Roo.factory())
38666      * @type {Roo.Layout}
38667      */
38668     el  : false,
38669     
38670     /**
38671      * @property el
38672      * for BC  - use el in new code
38673      * @type {Roo.Layout}
38674      */
38675     panel : false,
38676     
38677     /**
38678      * @property layout
38679      * for BC  - use el in new code
38680      * @type {Roo.Layout}
38681      */
38682     layout : false,
38683     
38684      /**
38685      * @cfg {Function|boolean} disabled
38686      * If this module is disabled by some rule, return true from the funtion
38687      */
38688     disabled : false,
38689     
38690     /**
38691      * @cfg {String} parent 
38692      * Name of parent element which it get xtype added to..
38693      */
38694     parent: false,
38695     
38696     /**
38697      * @cfg {String} order
38698      * Used to set the order in which elements are created (usefull for multiple tabs)
38699      */
38700     
38701     order : false,
38702     /**
38703      * @cfg {String} name
38704      * String to display while loading.
38705      */
38706     name : false,
38707     /**
38708      * @cfg {String} region
38709      * Region to render component to (defaults to center)
38710      */
38711     region : 'center',
38712     
38713     /**
38714      * @cfg {Array} items
38715      * A single item array - the first element is the root of the tree..
38716      * It's done this way to stay compatible with the Xtype system...
38717      */
38718     items : false,
38719     
38720     /**
38721      * @property _tree
38722      * The method that retuns the tree of parts that make up this compoennt 
38723      * @type {function}
38724      */
38725     _tree  : false,
38726     
38727      /**
38728      * render
38729      * render element to dom or tree
38730      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
38731      */
38732     
38733     render : function(el)
38734     {
38735         
38736         el = el || false;
38737         var hp = this.parent ? 1 : 0;
38738         
38739         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
38740             // if parent is a '#.....' string, then let's use that..
38741             var ename = this.parent.substr(1)
38742             this.parent = false;
38743             el = Roo.get(ename);
38744             if (!el) {
38745                 Roo.log("Warning - element can not be found :#" + ename );
38746                 return;
38747             }
38748         }
38749         
38750         
38751         if (!this.parent) {
38752             
38753             el = el ? Roo.get(el) : false;      
38754             
38755             // it's a top level one..
38756             this.parent =  {
38757                 el : new Roo.BorderLayout(el || document.body, {
38758                 
38759                      center: {
38760                          titlebar: false,
38761                          autoScroll:false,
38762                          closeOnTab: true,
38763                          tabPosition: 'top',
38764                           //resizeTabs: true,
38765                          alwaysShowTabs: el && hp? false :  true,
38766                          hideTabs: el || !hp ? true :  false,
38767                          minTabWidth: 140
38768                      }
38769                  })
38770             }
38771         }
38772         
38773                 if (!this.parent.el) {
38774                         // probably an old style ctor, which has been disabled.
38775                         return;
38776                         
38777                 }
38778                 // The 'tree' method is  '_tree now' 
38779             
38780         var tree = this._tree ? this._tree() : this.tree();
38781         tree.region = tree.region || this.region;
38782         this.el = this.parent.el.addxtype(tree);
38783         this.fireEvent('built', this);
38784         
38785         this.panel = this.el;
38786         this.layout = this.panel.layout;
38787                 this.parentLayout = this.parent.layout  || false;  
38788          
38789     }
38790     
38791 });
38792
38793 Roo.apply(Roo.XComponent, {
38794     /**
38795      * @property  hideProgress
38796      * true to disable the building progress bar.. usefull on single page renders.
38797      * @type Boolean
38798      */
38799     hideProgress : false,
38800     /**
38801      * @property  buildCompleted
38802      * True when the builder has completed building the interface.
38803      * @type Boolean
38804      */
38805     buildCompleted : false,
38806      
38807     /**
38808      * @property  topModule
38809      * the upper most module - uses document.element as it's constructor.
38810      * @type Object
38811      */
38812      
38813     topModule  : false,
38814       
38815     /**
38816      * @property  modules
38817      * array of modules to be created by registration system.
38818      * @type {Array} of Roo.XComponent
38819      */
38820     
38821     modules : [],
38822     /**
38823      * @property  elmodules
38824      * array of modules to be created by which use #ID 
38825      * @type {Array} of Roo.XComponent
38826      */
38827      
38828     elmodules : [],
38829
38830     
38831     /**
38832      * Register components to be built later.
38833      *
38834      * This solves the following issues
38835      * - Building is not done on page load, but after an authentication process has occured.
38836      * - Interface elements are registered on page load
38837      * - Parent Interface elements may not be loaded before child, so this handles that..
38838      * 
38839      *
38840      * example:
38841      * 
38842      * MyApp.register({
38843           order : '000001',
38844           module : 'Pman.Tab.projectMgr',
38845           region : 'center',
38846           parent : 'Pman.layout',
38847           disabled : false,  // or use a function..
38848         })
38849      
38850      * * @param {Object} details about module
38851      */
38852     register : function(obj) {
38853                 
38854         Roo.XComponent.event.fireEvent('register', obj);
38855         switch(typeof(obj.disabled) ) {
38856                 
38857             case 'undefined':
38858                 break;
38859             
38860             case 'function':
38861                 if ( obj.disabled() ) {
38862                         return;
38863                 }
38864                 break;
38865             
38866             default:
38867                 if (obj.disabled) {
38868                         return;
38869                 }
38870                 break;
38871         }
38872                 
38873         this.modules.push(obj);
38874          
38875     },
38876     /**
38877      * convert a string to an object..
38878      * eg. 'AAA.BBB' -> finds AAA.BBB
38879
38880      */
38881     
38882     toObject : function(str)
38883     {
38884         if (!str || typeof(str) == 'object') {
38885             return str;
38886         }
38887         if (str.substring(0,1) == '#') {
38888             return str;
38889         }
38890
38891         var ar = str.split('.');
38892         var rt, o;
38893         rt = ar.shift();
38894             /** eval:var:o */
38895         try {
38896             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38897         } catch (e) {
38898             throw "Module not found : " + str;
38899         }
38900         
38901         if (o === false) {
38902             throw "Module not found : " + str;
38903         }
38904         Roo.each(ar, function(e) {
38905             if (typeof(o[e]) == 'undefined') {
38906                 throw "Module not found : " + str;
38907             }
38908             o = o[e];
38909         });
38910         
38911         return o;
38912         
38913     },
38914     
38915     
38916     /**
38917      * move modules into their correct place in the tree..
38918      * 
38919      */
38920     preBuild : function ()
38921     {
38922         var _t = this;
38923         Roo.each(this.modules , function (obj)
38924         {
38925             Roo.XComponent.event.fireEvent('beforebuild', obj);
38926             
38927             var opar = obj.parent;
38928             try { 
38929                 obj.parent = this.toObject(opar);
38930             } catch(e) {
38931                 Roo.log("parent:toObject failed: " + e.toString());
38932                 return;
38933             }
38934             
38935             if (!obj.parent) {
38936                 Roo.debug && Roo.log("GOT top level module");
38937                 Roo.debug && Roo.log(obj);
38938                 obj.modules = new Roo.util.MixedCollection(false, 
38939                     function(o) { return o.order + '' }
38940                 );
38941                 this.topModule = obj;
38942                 return;
38943             }
38944                         // parent is a string (usually a dom element name..)
38945             if (typeof(obj.parent) == 'string') {
38946                 this.elmodules.push(obj);
38947                 return;
38948             }
38949             if (obj.parent.constructor != Roo.XComponent) {
38950                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
38951             }
38952             if (!obj.parent.modules) {
38953                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38954                     function(o) { return o.order + '' }
38955                 );
38956             }
38957             if (obj.parent.disabled) {
38958                 obj.disabled = true;
38959             }
38960             obj.parent.modules.add(obj);
38961         }, this);
38962     },
38963     
38964      /**
38965      * make a list of modules to build.
38966      * @return {Array} list of modules. 
38967      */ 
38968     
38969     buildOrder : function()
38970     {
38971         var _this = this;
38972         var cmp = function(a,b) {   
38973             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38974         };
38975         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38976             throw "No top level modules to build";
38977         }
38978         
38979         // make a flat list in order of modules to build.
38980         var mods = this.topModule ? [ this.topModule ] : [];
38981                 
38982         // elmodules (is a list of DOM based modules )
38983         Roo.each(this.elmodules, function(e) {
38984             mods.push(e)
38985         });
38986
38987         
38988         // add modules to their parents..
38989         var addMod = function(m) {
38990             Roo.debug && Roo.log("build Order: add: " + m.name);
38991             
38992         mods.push(m);
38993         if (m.modules && !m.disabled) {
38994             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
38995             m.modules.keySort('ASC',  cmp );
38996             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
38997
38998             m.modules.each(addMod);
38999         } else {
39000             Roo.debug && Roo.log("build Order: no child modules");
39001             }
39002             // not sure if this is used any more..
39003             if (m.finalize) {
39004                 m.finalize.name = m.name + " (clean up) ";
39005                 mods.push(m.finalize);
39006             }
39007             
39008         }
39009         if (this.topModule) { 
39010             this.topModule.modules.keySort('ASC',  cmp );
39011             this.topModule.modules.each(addMod);
39012         }
39013         return mods;
39014     },
39015     
39016      /**
39017      * Build the registered modules.
39018      * @param {Object} parent element.
39019      * @param {Function} optional method to call after module has been added.
39020      * 
39021      */ 
39022    
39023     build : function() 
39024     {
39025         
39026         this.preBuild();
39027         var mods = this.buildOrder();
39028       
39029         //this.allmods = mods;
39030         //Roo.debug && Roo.log(mods);
39031         //return;
39032         if (!mods.length) { // should not happen
39033             throw "NO modules!!!";
39034         }
39035         
39036         
39037         var msg = "Building Interface...";
39038         // flash it up as modal - so we store the mask!?
39039         if (!this.hideProgress) {
39040             Roo.MessageBox.show({ title: 'loading' });
39041             Roo.MessageBox.show({
39042                title: "Please wait...",
39043                msg: msg,
39044                width:450,
39045                progress:true,
39046                closable:false,
39047                modal: false
39048               
39049             });
39050         }
39051         var total = mods.length;
39052         
39053         var _this = this;
39054         var progressRun = function() {
39055             if (!mods.length) {
39056                 Roo.debug && Roo.log('hide?');
39057                 if (!this.hideProgress) {
39058                     Roo.MessageBox.hide();
39059                 }
39060                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
39061                 
39062                 // THE END...
39063                 return false;   
39064             }
39065             
39066             var m = mods.shift();
39067             
39068             
39069             Roo.debug && Roo.log(m);
39070             // not sure if this is supported any more.. - modules that are are just function
39071             if (typeof(m) == 'function') { 
39072                 m.call(this);
39073                 return progressRun.defer(10, _this);
39074             } 
39075             
39076             
39077             msg = "Building Interface " + (total  - mods.length) + 
39078                     " of " + total + 
39079                     (m.name ? (' - ' + m.name) : '');
39080                         Roo.debug && Roo.log(msg);
39081             if (!this.hideProgress) { 
39082                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
39083             }
39084             
39085          
39086             // is the module disabled?
39087             var disabled = (typeof(m.disabled) == 'function') ?
39088                 m.disabled.call(m.module.disabled) : m.disabled;    
39089             
39090             
39091             if (disabled) {
39092                 return progressRun(); // we do not update the display!
39093             }
39094             
39095             // now build 
39096             
39097                         
39098                         
39099             m.render();
39100             // it's 10 on top level, and 1 on others??? why...
39101             return progressRun.defer(10, _this);
39102              
39103         }
39104         progressRun.defer(1, _this);
39105      
39106         
39107         
39108     },
39109         
39110         
39111         /**
39112          * Event Object.
39113          *
39114          *
39115          */
39116         event: false, 
39117     /**
39118          * wrapper for event.on - aliased later..  
39119          * Typically use to register a event handler for register:
39120          *
39121          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
39122          *
39123          */
39124     on : false
39125    
39126     
39127     
39128 });
39129
39130 Roo.XComponent.event = new Roo.util.Observable({
39131                 events : { 
39132                         /**
39133                          * @event register
39134                          * Fires when an Component is registered,
39135                          * set the disable property on the Component to stop registration.
39136                          * @param {Roo.XComponent} c the component being registerd.
39137                          * 
39138                          */
39139                         'register' : true,
39140             /**
39141                          * @event beforebuild
39142                          * Fires before each Component is built
39143                          * can be used to apply permissions.
39144                          * @param {Roo.XComponent} c the component being registerd.
39145                          * 
39146                          */
39147                         'beforebuild' : true,
39148                         /**
39149                          * @event buildcomplete
39150                          * Fires on the top level element when all elements have been built
39151                          * @param {Roo.XComponent} the top level component.
39152                          */
39153                         'buildcomplete' : true
39154                         
39155                 }
39156 });
39157
39158 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
39159  //<script type="text/javascript">
39160
39161
39162 /**
39163  * @class Roo.Login
39164  * @extends Roo.LayoutDialog
39165  * A generic Login Dialog..... - only one needed in theory!?!?
39166  *
39167  * Fires XComponent builder on success...
39168  * 
39169  * Sends 
39170  *    username,password, lang = for login actions.
39171  *    check = 1 for periodic checking that sesion is valid.
39172  *    passwordRequest = email request password
39173  *    logout = 1 = to logout
39174  * 
39175  * Affects: (this id="????" elements)
39176  *   loading  (removed) (used to indicate application is loading)
39177  *   loading-mask (hides) (used to hide application when it's building loading)
39178  *   
39179  * 
39180  * Usage: 
39181  *    
39182  * 
39183  * Myapp.login = Roo.Login({
39184      url: xxxx,
39185    
39186      realm : 'Myapp', 
39187      
39188      
39189      method : 'POST',
39190      
39191      
39192      * 
39193  })
39194  * 
39195  * 
39196  * 
39197  **/
39198  
39199 Roo.Login = function(cfg)
39200 {
39201     this.addEvents({
39202         'refreshed' : true
39203     });
39204     
39205     Roo.apply(this,cfg);
39206     
39207     Roo.onReady(function() {
39208         this.onLoad();
39209     }, this);
39210     // call parent..
39211     
39212    
39213     Roo.Login.superclass.constructor.call(this, this);
39214     //this.addxtype(this.items[0]);
39215     
39216     
39217 }
39218
39219
39220 Roo.extend(Roo.Login, Roo.LayoutDialog, {
39221     
39222     /**
39223      * @cfg {String} method
39224      * Method used to query for login details.
39225      */
39226     
39227     method : 'POST',
39228     /**
39229      * @cfg {String} url
39230      * URL to query login data. - eg. baseURL + '/Login.php'
39231      */
39232     url : '',
39233     
39234     /**
39235      * @property user
39236      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
39237      * @type {Object} 
39238      */
39239     user : false,
39240     /**
39241      * @property checkFails
39242      * Number of times we have attempted to get authentication check, and failed.
39243      * @type {Number} 
39244      */
39245     checkFails : 0,
39246       /**
39247      * @property intervalID
39248      * The window interval that does the constant login checking.
39249      * @type {Number} 
39250      */
39251     intervalID : 0,
39252     
39253     
39254     onLoad : function() // called on page load...
39255     {
39256         // load 
39257          
39258         if (Roo.get('loading')) { // clear any loading indicator..
39259             Roo.get('loading').remove();
39260         }
39261         
39262         //this.switchLang('en'); // set the language to english..
39263        
39264         this.check({
39265             success:  function(response, opts)  {  // check successfull...
39266             
39267                 var res = this.processResponse(response);
39268                 this.checkFails =0;
39269                 if (!res.success) { // error!
39270                     this.checkFails = 5;
39271                     //console.log('call failure');
39272                     return this.failure(response,opts);
39273                 }
39274                 
39275                 if (!res.data.id) { // id=0 == login failure.
39276                     return this.show();
39277                 }
39278                 
39279                               
39280                         //console.log(success);
39281                 this.fillAuth(res.data);   
39282                 this.checkFails =0;
39283                 Roo.XComponent.build();
39284             },
39285             failure : this.show
39286         });
39287         
39288     }, 
39289     
39290     
39291     check: function(cfg) // called every so often to refresh cookie etc..
39292     {
39293         if (cfg.again) { // could be undefined..
39294             this.checkFails++;
39295         } else {
39296             this.checkFails = 0;
39297         }
39298         var _this = this;
39299         if (this.sending) {
39300             if ( this.checkFails > 4) {
39301                 Roo.MessageBox.alert("Error",  
39302                     "Error getting authentication status. - try reloading, or wait a while", function() {
39303                         _this.sending = false;
39304                     }); 
39305                 return;
39306             }
39307             cfg.again = true;
39308             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
39309             return;
39310         }
39311         this.sending = true;
39312         
39313         Roo.Ajax.request({  
39314             url: this.url,
39315             params: {
39316                 getAuthUser: true
39317             },  
39318             method: this.method,
39319             success:  cfg.success || this.success,
39320             failure : cfg.failure || this.failure,
39321             scope : this,
39322             callCfg : cfg
39323               
39324         });  
39325     }, 
39326     
39327     
39328     logout: function()
39329     {
39330         window.onbeforeunload = function() { }; // false does not work for IE..
39331         this.user = false;
39332         var _this = this;
39333         
39334         Roo.Ajax.request({  
39335             url: this.url,
39336             params: {
39337                 logout: 1
39338             },  
39339             method: 'GET',
39340             failure : function() {
39341                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
39342                     document.location = document.location.toString() + '?ts=' + Math.random();
39343                 });
39344                 
39345             },
39346             success : function() {
39347                 _this.user = false;
39348                 this.checkFails =0;
39349                 // fixme..
39350                 document.location = document.location.toString() + '?ts=' + Math.random();
39351             }
39352               
39353               
39354         }); 
39355     },
39356     
39357     processResponse : function (response)
39358     {
39359         var res = '';
39360         try {
39361             res = Roo.decode(response.responseText);
39362             // oops...
39363             if (typeof(res) != 'object') {
39364                 res = { success : false, errorMsg : res, errors : true };
39365             }
39366             if (typeof(res.success) == 'undefined') {
39367                 res.success = false;
39368             }
39369             
39370         } catch(e) {
39371             res = { success : false,  errorMsg : response.responseText, errors : true };
39372         }
39373         return res;
39374     },
39375     
39376     success : function(response, opts)  // check successfull...
39377     {  
39378         this.sending = false;
39379         var res = this.processResponse(response);
39380         if (!res.success) {
39381             return this.failure(response, opts);
39382         }
39383         if (!res.data || !res.data.id) {
39384             return this.failure(response,opts);
39385         }
39386         //console.log(res);
39387         this.fillAuth(res.data);
39388         
39389         this.checkFails =0;
39390         
39391     },
39392     
39393     
39394     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
39395     {
39396         this.authUser = -1;
39397         this.sending = false;
39398         var res = this.processResponse(response);
39399         //console.log(res);
39400         if ( this.checkFails > 2) {
39401         
39402             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
39403                 "Error getting authentication status. - try reloading"); 
39404             return;
39405         }
39406         opts.callCfg.again = true;
39407         this.check.defer(1000, this, [ opts.callCfg ]);
39408         return;  
39409     },
39410     
39411     
39412     
39413     fillAuth: function(au) {
39414         this.startAuthCheck();
39415         this.authUserId = au.id;
39416         this.authUser = au;
39417         this.lastChecked = new Date();
39418         this.fireEvent('refreshed', au);
39419         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
39420         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
39421         au.lang = au.lang || 'en';
39422         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
39423         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
39424         this.switchLang(au.lang );
39425         
39426      
39427         // open system... - -on setyp..
39428         if (this.authUserId  < 0) {
39429             Roo.MessageBox.alert("Warning", 
39430                 "This is an open system - please set up a admin user with a password.");  
39431         }
39432          
39433         //Pman.onload(); // which should do nothing if it's a re-auth result...
39434         
39435              
39436     },
39437     
39438     startAuthCheck : function() // starter for timeout checking..
39439     {
39440         if (this.intervalID) { // timer already in place...
39441             return false;
39442         }
39443         var _this = this;
39444         this.intervalID =  window.setInterval(function() {
39445               _this.check(false);
39446             }, 120000); // every 120 secs = 2mins..
39447         
39448         
39449     },
39450          
39451     
39452     switchLang : function (lang) 
39453     {
39454         _T = typeof(_T) == 'undefined' ? false : _T;
39455           if (!_T || !lang.length) {
39456             return;
39457         }
39458         
39459         if (!_T && lang != 'en') {
39460             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39461             return;
39462         }
39463         
39464         if (typeof(_T.en) == 'undefined') {
39465             _T.en = {};
39466             Roo.apply(_T.en, _T);
39467         }
39468         
39469         if (typeof(_T[lang]) == 'undefined') {
39470             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39471             return;
39472         }
39473         
39474         
39475         Roo.apply(_T, _T[lang]);
39476         // just need to set the text values for everything...
39477         var _this = this;
39478         /* this will not work ...
39479         if (this.form) { 
39480             
39481                
39482             function formLabel(name, val) {
39483                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
39484             }
39485             
39486             formLabel('password', "Password"+':');
39487             formLabel('username', "Email Address"+':');
39488             formLabel('lang', "Language"+':');
39489             this.dialog.setTitle("Login");
39490             this.dialog.buttons[0].setText("Forgot Password");
39491             this.dialog.buttons[1].setText("Login");
39492         }
39493         */
39494         
39495         
39496     },
39497     
39498     
39499     title: "Login",
39500     modal: true,
39501     width:  350,
39502     //height: 230,
39503     height: 180,
39504     shadow: true,
39505     minWidth:200,
39506     minHeight:180,
39507     //proxyDrag: true,
39508     closable: false,
39509     draggable: false,
39510     collapsible: false,
39511     resizable: false,
39512     center: {  // needed??
39513         autoScroll:false,
39514         titlebar: false,
39515        // tabPosition: 'top',
39516         hideTabs: true,
39517         closeOnTab: true,
39518         alwaysShowTabs: false
39519     } ,
39520     listeners : {
39521         
39522         show  : function(dlg)
39523         {
39524             //console.log(this);
39525             this.form = this.layout.getRegion('center').activePanel.form;
39526             this.form.dialog = dlg;
39527             this.buttons[0].form = this.form;
39528             this.buttons[0].dialog = dlg;
39529             this.buttons[1].form = this.form;
39530             this.buttons[1].dialog = dlg;
39531            
39532            //this.resizeToLogo.defer(1000,this);
39533             // this is all related to resizing for logos..
39534             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
39535            //// if (!sz) {
39536              //   this.resizeToLogo.defer(1000,this);
39537              //   return;
39538            // }
39539             //var w = Ext.lib.Dom.getViewWidth() - 100;
39540             //var h = Ext.lib.Dom.getViewHeight() - 100;
39541             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
39542             //this.center();
39543             if (this.disabled) {
39544                 this.hide();
39545                 return;
39546             }
39547             
39548             if (this.user.id < 0) { // used for inital setup situations.
39549                 return;
39550             }
39551             
39552             if (this.intervalID) {
39553                 // remove the timer
39554                 window.clearInterval(this.intervalID);
39555                 this.intervalID = false;
39556             }
39557             
39558             
39559             if (Roo.get('loading')) {
39560                 Roo.get('loading').remove();
39561             }
39562             if (Roo.get('loading-mask')) {
39563                 Roo.get('loading-mask').hide();
39564             }
39565             
39566             //incomming._node = tnode;
39567             this.form.reset();
39568             //this.dialog.modal = !modal;
39569             //this.dialog.show();
39570             this.el.unmask(); 
39571             
39572             
39573             this.form.setValues({
39574                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
39575                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
39576             });
39577             
39578             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
39579             if (this.form.findField('username').getValue().length > 0 ){
39580                 this.form.findField('password').focus();
39581             } else {
39582                this.form.findField('username').focus();
39583             }
39584     
39585         }
39586     },
39587     items : [
39588          {
39589        
39590             xtype : 'ContentPanel',
39591             xns : Roo,
39592             region: 'center',
39593             fitToFrame : true,
39594             
39595             items : [
39596     
39597                 {
39598                
39599                     xtype : 'Form',
39600                     xns : Roo.form,
39601                     labelWidth: 100,
39602                     style : 'margin: 10px;',
39603                     
39604                     listeners : {
39605                         actionfailed : function(f, act) {
39606                             // form can return { errors: .... }
39607                                 
39608                             //act.result.errors // invalid form element list...
39609                             //act.result.errorMsg// invalid form element list...
39610                             
39611                             this.dialog.el.unmask();
39612                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
39613                                         "Login failed - communication error - try again.");
39614                                       
39615                         },
39616                         actioncomplete: function(re, act) {
39617                              
39618                             Roo.state.Manager.set(
39619                                 this.dialog.realm + '.username',  
39620                                     this.findField('username').getValue()
39621                             );
39622                             Roo.state.Manager.set(
39623                                 this.dialog.realm + '.lang',  
39624                                 this.findField('lang').getValue() 
39625                             );
39626                             
39627                             this.dialog.fillAuth(act.result.data);
39628                               
39629                             this.dialog.hide();
39630                             
39631                             if (Roo.get('loading-mask')) {
39632                                 Roo.get('loading-mask').show();
39633                             }
39634                             Roo.XComponent.build();
39635                             
39636                              
39637                             
39638                         }
39639                     },
39640                     items : [
39641                         {
39642                             xtype : 'TextField',
39643                             xns : Roo.form,
39644                             fieldLabel: "Email Address",
39645                             name: 'username',
39646                             width:200,
39647                             autoCreate : {tag: "input", type: "text", size: "20"}
39648                         },
39649                         {
39650                             xtype : 'TextField',
39651                             xns : Roo.form,
39652                             fieldLabel: "Password",
39653                             inputType: 'password',
39654                             name: 'password',
39655                             width:200,
39656                             autoCreate : {tag: "input", type: "text", size: "20"},
39657                             listeners : {
39658                                 specialkey : function(e,ev) {
39659                                     if (ev.keyCode == 13) {
39660                                         this.form.dialog.el.mask("Logging in");
39661                                         this.form.doAction('submit', {
39662                                             url: this.form.dialog.url,
39663                                             method: this.form.dialog.method
39664                                         });
39665                                     }
39666                                 }
39667                             }  
39668                         },
39669                         {
39670                             xtype : 'ComboBox',
39671                             xns : Roo.form,
39672                             fieldLabel: "Language",
39673                             name : 'langdisp',
39674                             store: {
39675                                 xtype : 'SimpleStore',
39676                                 fields: ['lang', 'ldisp'],
39677                                 data : [
39678                                     [ 'en', 'English' ],
39679                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
39680                                     [ 'zh_CN', '\u7C21\u4E2D' ]
39681                                 ]
39682                             },
39683                             
39684                             valueField : 'lang',
39685                             hiddenName:  'lang',
39686                             width: 200,
39687                             displayField:'ldisp',
39688                             typeAhead: false,
39689                             editable: false,
39690                             mode: 'local',
39691                             triggerAction: 'all',
39692                             emptyText:'Select a Language...',
39693                             selectOnFocus:true,
39694                             listeners : {
39695                                 select :  function(cb, rec, ix) {
39696                                     this.form.switchLang(rec.data.lang);
39697                                 }
39698                             }
39699                         
39700                         }
39701                     ]
39702                 }
39703                   
39704                 
39705             ]
39706         }
39707     ],
39708     buttons : [
39709         {
39710             xtype : 'Button',
39711             xns : 'Roo',
39712             text : "Forgot Password",
39713             listeners : {
39714                 click : function() {
39715                     //console.log(this);
39716                     var n = this.form.findField('username').getValue();
39717                     if (!n.length) {
39718                         Roo.MessageBox.alert("Error", "Fill in your email address");
39719                         return;
39720                     }
39721                     Roo.Ajax.request({
39722                         url: this.dialog.url,
39723                         params: {
39724                             passwordRequest: n
39725                         },
39726                         method: this.dialog.method,
39727                         success:  function(response, opts)  {  // check successfull...
39728                         
39729                             var res = this.dialog.processResponse(response);
39730                             if (!res.success) { // error!
39731                                Roo.MessageBox.alert("Error" ,
39732                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
39733                                return;
39734                             }
39735                             Roo.MessageBox.alert("Notice" ,
39736                                 "Please check you email for the Password Reset message");
39737                         },
39738                         failure : function() {
39739                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
39740                         }
39741                         
39742                     });
39743                 }
39744             }
39745         },
39746         {
39747             xtype : 'Button',
39748             xns : 'Roo',
39749             text : "Login",
39750             listeners : {
39751                 
39752                 click : function () {
39753                         
39754                     this.dialog.el.mask("Logging in");
39755                     this.form.doAction('submit', {
39756                             url: this.dialog.url,
39757                             method: this.dialog.method
39758                     });
39759                 }
39760             }
39761         }
39762     ]
39763   
39764   
39765 })
39766  
39767
39768
39769