roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing && this.store){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             // show a message if no listener is registered.
5064             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5065                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5066             }
5067             // loadmask wil be hooked into this..
5068             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5069             return;
5070         }
5071         var r = o.records, t = o.totalRecords || r.length;
5072         if(!options || options.add !== true){
5073             if(this.pruneModifiedRecords){
5074                 this.modified = [];
5075             }
5076             for(var i = 0, len = r.length; i < len; i++){
5077                 r[i].join(this);
5078             }
5079             if(this.snapshot){
5080                 this.data = this.snapshot;
5081                 delete this.snapshot;
5082             }
5083             this.data.clear();
5084             this.data.addAll(r);
5085             this.totalLength = t;
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.totalLength = Math.max(t, this.data.length+r.length);
5090             this.add(r);
5091         }
5092         this.fireEvent("load", this, r, options);
5093         if(options.callback){
5094             options.callback.call(options.scope || this, r, options, true);
5095         }
5096     },
5097
5098
5099     /**
5100      * Loads data from a passed data block. A Reader which understands the format of the data
5101      * must have been configured in the constructor.
5102      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5103      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5104      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5105      */
5106     loadData : function(o, append){
5107         var r = this.reader.readRecords(o);
5108         this.loadRecords(r, {add: append}, true);
5109     },
5110
5111     /**
5112      * Gets the number of cached records.
5113      * <p>
5114      * <em>If using paging, this may not be the total size of the dataset. If the data object
5115      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5116      * the data set size</em>
5117      */
5118     getCount : function(){
5119         return this.data.length || 0;
5120     },
5121
5122     /**
5123      * Gets the total number of records in the dataset as returned by the server.
5124      * <p>
5125      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5126      * the dataset size</em>
5127      */
5128     getTotalCount : function(){
5129         return this.totalLength || 0;
5130     },
5131
5132     /**
5133      * Returns the sort state of the Store as an object with two properties:
5134      * <pre><code>
5135  field {String} The name of the field by which the Records are sorted
5136  direction {String} The sort order, "ASC" or "DESC"
5137      * </code></pre>
5138      */
5139     getSortState : function(){
5140         return this.sortInfo;
5141     },
5142
5143     // private
5144     applySort : function(){
5145         if(this.sortInfo && !this.remoteSort){
5146             var s = this.sortInfo, f = s.field;
5147             var st = this.fields.get(f).sortType;
5148             var fn = function(r1, r2){
5149                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5150                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5151             };
5152             this.data.sort(s.direction, fn);
5153             if(this.snapshot && this.snapshot != this.data){
5154                 this.snapshot.sort(s.direction, fn);
5155             }
5156         }
5157     },
5158
5159     /**
5160      * Sets the default sort column and order to be used by the next load operation.
5161      * @param {String} fieldName The name of the field to sort by.
5162      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5163      */
5164     setDefaultSort : function(field, dir){
5165         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5166     },
5167
5168     /**
5169      * Sort the Records.
5170      * If remote sorting is used, the sort is performed on the server, and the cache is
5171      * reloaded. If local sorting is used, the cache is sorted internally.
5172      * @param {String} fieldName The name of the field to sort by.
5173      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5174      */
5175     sort : function(fieldName, dir){
5176         var f = this.fields.get(fieldName);
5177         if(!dir){
5178             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5179             
5180             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5181                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5182             }else{
5183                 dir = f.sortDir;
5184             }
5185         }
5186         this.sortToggle[f.name] = dir;
5187         this.sortInfo = {field: f.name, direction: dir};
5188         if(!this.remoteSort){
5189             this.applySort();
5190             this.fireEvent("datachanged", this);
5191         }else{
5192             this.load(this.lastOptions);
5193         }
5194     },
5195
5196     /**
5197      * Calls the specified function for each of the Records in the cache.
5198      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5199      * Returning <em>false</em> aborts and exits the iteration.
5200      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5201      */
5202     each : function(fn, scope){
5203         this.data.each(fn, scope);
5204     },
5205
5206     /**
5207      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5208      * (e.g., during paging).
5209      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5210      */
5211     getModifiedRecords : function(){
5212         return this.modified;
5213     },
5214
5215     // private
5216     createFilterFn : function(property, value, anyMatch){
5217         if(!value.exec){ // not a regex
5218             value = String(value);
5219             if(value.length == 0){
5220                 return false;
5221             }
5222             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5223         }
5224         return function(r){
5225             return value.test(r.data[property]);
5226         };
5227     },
5228
5229     /**
5230      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5231      * @param {String} property A field on your records
5232      * @param {Number} start The record index to start at (defaults to 0)
5233      * @param {Number} end The last record index to include (defaults to length - 1)
5234      * @return {Number} The sum
5235      */
5236     sum : function(property, start, end){
5237         var rs = this.data.items, v = 0;
5238         start = start || 0;
5239         end = (end || end === 0) ? end : rs.length-1;
5240
5241         for(var i = start; i <= end; i++){
5242             v += (rs[i].data[property] || 0);
5243         }
5244         return v;
5245     },
5246
5247     /**
5248      * Filter the records by a specified property.
5249      * @param {String} field A field on your records
5250      * @param {String/RegExp} value Either a string that the field
5251      * should start with or a RegExp to test against the field
5252      * @param {Boolean} anyMatch True to match any part not just the beginning
5253      */
5254     filter : function(property, value, anyMatch){
5255         var fn = this.createFilterFn(property, value, anyMatch);
5256         return fn ? this.filterBy(fn) : this.clearFilter();
5257     },
5258
5259     /**
5260      * Filter by a function. The specified function will be called with each
5261      * record in this data source. If the function returns true the record is included,
5262      * otherwise it is filtered.
5263      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5264      * @param {Object} scope (optional) The scope of the function (defaults to this)
5265      */
5266     filterBy : function(fn, scope){
5267         this.snapshot = this.snapshot || this.data;
5268         this.data = this.queryBy(fn, scope||this);
5269         this.fireEvent("datachanged", this);
5270     },
5271
5272     /**
5273      * Query the records by a specified property.
5274      * @param {String} field A field on your records
5275      * @param {String/RegExp} value Either a string that the field
5276      * should start with or a RegExp to test against the field
5277      * @param {Boolean} anyMatch True to match any part not just the beginning
5278      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5279      */
5280     query : function(property, value, anyMatch){
5281         var fn = this.createFilterFn(property, value, anyMatch);
5282         return fn ? this.queryBy(fn) : this.data.clone();
5283     },
5284
5285     /**
5286      * Query by a function. The specified function will be called with each
5287      * record in this data source. If the function returns true the record is included
5288      * in the results.
5289      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5290      * @param {Object} scope (optional) The scope of the function (defaults to this)
5291       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5292      **/
5293     queryBy : function(fn, scope){
5294         var data = this.snapshot || this.data;
5295         return data.filterBy(fn, scope||this);
5296     },
5297
5298     /**
5299      * Collects unique values for a particular dataIndex from this store.
5300      * @param {String} dataIndex The property to collect
5301      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5302      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5303      * @return {Array} An array of the unique values
5304      **/
5305     collect : function(dataIndex, allowNull, bypassFilter){
5306         var d = (bypassFilter === true && this.snapshot) ?
5307                 this.snapshot.items : this.data.items;
5308         var v, sv, r = [], l = {};
5309         for(var i = 0, len = d.length; i < len; i++){
5310             v = d[i].data[dataIndex];
5311             sv = String(v);
5312             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5313                 l[sv] = true;
5314                 r[r.length] = v;
5315             }
5316         }
5317         return r;
5318     },
5319
5320     /**
5321      * Revert to a view of the Record cache with no filtering applied.
5322      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5323      */
5324     clearFilter : function(suppressEvent){
5325         if(this.snapshot && this.snapshot != this.data){
5326             this.data = this.snapshot;
5327             delete this.snapshot;
5328             if(suppressEvent !== true){
5329                 this.fireEvent("datachanged", this);
5330             }
5331         }
5332     },
5333
5334     // private
5335     afterEdit : function(record){
5336         if(this.modified.indexOf(record) == -1){
5337             this.modified.push(record);
5338         }
5339         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5340     },
5341     
5342     // private
5343     afterReject : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5346     },
5347
5348     // private
5349     afterCommit : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5352     },
5353
5354     /**
5355      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5356      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5357      */
5358     commitChanges : function(){
5359         var m = this.modified.slice(0);
5360         this.modified = [];
5361         for(var i = 0, len = m.length; i < len; i++){
5362             m[i].commit();
5363         }
5364     },
5365
5366     /**
5367      * Cancel outstanding changes on all changed records.
5368      */
5369     rejectChanges : function(){
5370         var m = this.modified.slice(0);
5371         this.modified = [];
5372         for(var i = 0, len = m.length; i < len; i++){
5373             m[i].reject();
5374         }
5375     },
5376
5377     onMetaChange : function(meta, rtype, o){
5378         this.recordType = rtype;
5379         this.fields = rtype.prototype.fields;
5380         delete this.snapshot;
5381         this.sortInfo = meta.sortInfo || this.sortInfo;
5382         this.modified = [];
5383         this.fireEvent('metachange', this, this.reader.meta);
5384     }
5385 });/*
5386  * Based on:
5387  * Ext JS Library 1.1.1
5388  * Copyright(c) 2006-2007, Ext JS, LLC.
5389  *
5390  * Originally Released Under LGPL - original licence link has changed is not relivant.
5391  *
5392  * Fork - LGPL
5393  * <script type="text/javascript">
5394  */
5395
5396 /**
5397  * @class Roo.data.SimpleStore
5398  * @extends Roo.data.Store
5399  * Small helper class to make creating Stores from Array data easier.
5400  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5401  * @cfg {Array} fields An array of field definition objects, or field name strings.
5402  * @cfg {Array} data The multi-dimensional array of data
5403  * @constructor
5404  * @param {Object} config
5405  */
5406 Roo.data.SimpleStore = function(config){
5407     Roo.data.SimpleStore.superclass.constructor.call(this, {
5408         isLocal : true,
5409         reader: new Roo.data.ArrayReader({
5410                 id: config.id
5411             },
5412             Roo.data.Record.create(config.fields)
5413         ),
5414         proxy : new Roo.data.MemoryProxy(config.data)
5415     });
5416     this.load();
5417 };
5418 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429 /**
5430 /**
5431  * @extends Roo.data.Store
5432  * @class Roo.data.JsonStore
5433  * Small helper class to make creating Stores for JSON data easier. <br/>
5434 <pre><code>
5435 var store = new Roo.data.JsonStore({
5436     url: 'get-images.php',
5437     root: 'images',
5438     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5439 });
5440 </code></pre>
5441  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5442  * JsonReader and HttpProxy (unless inline data is provided).</b>
5443  * @cfg {Array} fields An array of field definition objects, or field name strings.
5444  * @constructor
5445  * @param {Object} config
5446  */
5447 Roo.data.JsonStore = function(c){
5448     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5449         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5450         reader: new Roo.data.JsonReader(c, c.fields)
5451     }));
5452 };
5453 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5454  * Based on:
5455  * Ext JS Library 1.1.1
5456  * Copyright(c) 2006-2007, Ext JS, LLC.
5457  *
5458  * Originally Released Under LGPL - original licence link has changed is not relivant.
5459  *
5460  * Fork - LGPL
5461  * <script type="text/javascript">
5462  */
5463
5464  
5465 Roo.data.Field = function(config){
5466     if(typeof config == "string"){
5467         config = {name: config};
5468     }
5469     Roo.apply(this, config);
5470     
5471     if(!this.type){
5472         this.type = "auto";
5473     }
5474     
5475     var st = Roo.data.SortTypes;
5476     // named sortTypes are supported, here we look them up
5477     if(typeof this.sortType == "string"){
5478         this.sortType = st[this.sortType];
5479     }
5480     
5481     // set default sortType for strings and dates
5482     if(!this.sortType){
5483         switch(this.type){
5484             case "string":
5485                 this.sortType = st.asUCString;
5486                 break;
5487             case "date":
5488                 this.sortType = st.asDate;
5489                 break;
5490             default:
5491                 this.sortType = st.none;
5492         }
5493     }
5494
5495     // define once
5496     var stripRe = /[\$,%]/g;
5497
5498     // prebuilt conversion function for this field, instead of
5499     // switching every time we're reading a value
5500     if(!this.convert){
5501         var cv, dateFormat = this.dateFormat;
5502         switch(this.type){
5503             case "":
5504             case "auto":
5505             case undefined:
5506                 cv = function(v){ return v; };
5507                 break;
5508             case "string":
5509                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5510                 break;
5511             case "int":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5515                     };
5516                 break;
5517             case "float":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5521                     };
5522                 break;
5523             case "bool":
5524             case "boolean":
5525                 cv = function(v){ return v === true || v === "true" || v == 1; };
5526                 break;
5527             case "date":
5528                 cv = function(v){
5529                     if(!v){
5530                         return '';
5531                     }
5532                     if(v instanceof Date){
5533                         return v;
5534                     }
5535                     if(dateFormat){
5536                         if(dateFormat == "timestamp"){
5537                             return new Date(v*1000);
5538                         }
5539                         return Date.parseDate(v, dateFormat);
5540                     }
5541                     var parsed = Date.parse(v);
5542                     return parsed ? new Date(parsed) : null;
5543                 };
5544              break;
5545             
5546         }
5547         this.convert = cv;
5548     }
5549 };
5550
5551 Roo.data.Field.prototype = {
5552     dateFormat: null,
5553     defaultValue: "",
5554     mapping: null,
5555     sortType : null,
5556     sortDir : "ASC"
5557 };/*
5558  * Based on:
5559  * Ext JS Library 1.1.1
5560  * Copyright(c) 2006-2007, Ext JS, LLC.
5561  *
5562  * Originally Released Under LGPL - original licence link has changed is not relivant.
5563  *
5564  * Fork - LGPL
5565  * <script type="text/javascript">
5566  */
5567  
5568 // Base class for reading structured data from a data source.  This class is intended to be
5569 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5570
5571 /**
5572  * @class Roo.data.DataReader
5573  * Base class for reading structured data from a data source.  This class is intended to be
5574  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5575  */
5576
5577 Roo.data.DataReader = function(meta, recordType){
5578     
5579     this.meta = meta;
5580     
5581     this.recordType = recordType instanceof Array ? 
5582         Roo.data.Record.create(recordType) : recordType;
5583 };
5584
5585 Roo.data.DataReader.prototype = {
5586      /**
5587      * Create an empty record
5588      * @param {Object} data (optional) - overlay some values
5589      * @return {Roo.data.Record} record created.
5590      */
5591     newRow :  function(d) {
5592         var da =  {};
5593         this.recordType.prototype.fields.each(function(c) {
5594             switch( c.type) {
5595                 case 'int' : da[c.name] = 0; break;
5596                 case 'date' : da[c.name] = new Date(); break;
5597                 case 'float' : da[c.name] = 0.0; break;
5598                 case 'boolean' : da[c.name] = false; break;
5599                 default : da[c.name] = ""; break;
5600             }
5601             
5602         });
5603         return new this.recordType(Roo.apply(da, d));
5604     }
5605     
5606 };/*
5607  * Based on:
5608  * Ext JS Library 1.1.1
5609  * Copyright(c) 2006-2007, Ext JS, LLC.
5610  *
5611  * Originally Released Under LGPL - original licence link has changed is not relivant.
5612  *
5613  * Fork - LGPL
5614  * <script type="text/javascript">
5615  */
5616
5617 /**
5618  * @class Roo.data.DataProxy
5619  * @extends Roo.data.Observable
5620  * This class is an abstract base class for implementations which provide retrieval of
5621  * unformatted data objects.<br>
5622  * <p>
5623  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5624  * (of the appropriate type which knows how to parse the data object) to provide a block of
5625  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5626  * <p>
5627  * Custom implementations must implement the load method as described in
5628  * {@link Roo.data.HttpProxy#load}.
5629  */
5630 Roo.data.DataProxy = function(){
5631     this.addEvents({
5632         /**
5633          * @event beforeload
5634          * Fires before a network request is made to retrieve a data object.
5635          * @param {Object} This DataProxy object.
5636          * @param {Object} params The params parameter to the load function.
5637          */
5638         beforeload : true,
5639         /**
5640          * @event load
5641          * Fires before the load method's callback is called.
5642          * @param {Object} This DataProxy object.
5643          * @param {Object} o The data object.
5644          * @param {Object} arg The callback argument object passed to the load function.
5645          */
5646         load : true,
5647         /**
5648          * @event loadexception
5649          * Fires if an Exception occurs during data retrieval.
5650          * @param {Object} This DataProxy object.
5651          * @param {Object} o The data object.
5652          * @param {Object} arg The callback argument object passed to the load function.
5653          * @param {Object} e The Exception.
5654          */
5655         loadexception : true
5656     });
5657     Roo.data.DataProxy.superclass.constructor.call(this);
5658 };
5659
5660 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5661
5662     /**
5663      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5664      */
5665 /*
5666  * Based on:
5667  * Ext JS Library 1.1.1
5668  * Copyright(c) 2006-2007, Ext JS, LLC.
5669  *
5670  * Originally Released Under LGPL - original licence link has changed is not relivant.
5671  *
5672  * Fork - LGPL
5673  * <script type="text/javascript">
5674  */
5675 /**
5676  * @class Roo.data.MemoryProxy
5677  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5678  * to the Reader when its load method is called.
5679  * @constructor
5680  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5681  */
5682 Roo.data.MemoryProxy = function(data){
5683     if (data.data) {
5684         data = data.data;
5685     }
5686     Roo.data.MemoryProxy.superclass.constructor.call(this);
5687     this.data = data;
5688 };
5689
5690 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5691     /**
5692      * Load data from the requested source (in this case an in-memory
5693      * data object passed to the constructor), read the data object into
5694      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5695      * process that block using the passed callback.
5696      * @param {Object} params This parameter is not used by the MemoryProxy class.
5697      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5698      * object into a block of Roo.data.Records.
5699      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5700      * The function must be passed <ul>
5701      * <li>The Record block object</li>
5702      * <li>The "arg" argument from the load function</li>
5703      * <li>A boolean success indicator</li>
5704      * </ul>
5705      * @param {Object} scope The scope in which to call the callback
5706      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5707      */
5708     load : function(params, reader, callback, scope, arg){
5709         params = params || {};
5710         var result;
5711         try {
5712             result = reader.readRecords(this.data);
5713         }catch(e){
5714             this.fireEvent("loadexception", this, arg, null, e);
5715             callback.call(scope, null, arg, false);
5716             return;
5717         }
5718         callback.call(scope, result, arg, true);
5719     },
5720     
5721     // private
5722     update : function(params, records){
5723         
5724     }
5725 });/*
5726  * Based on:
5727  * Ext JS Library 1.1.1
5728  * Copyright(c) 2006-2007, Ext JS, LLC.
5729  *
5730  * Originally Released Under LGPL - original licence link has changed is not relivant.
5731  *
5732  * Fork - LGPL
5733  * <script type="text/javascript">
5734  */
5735 /**
5736  * @class Roo.data.HttpProxy
5737  * @extends Roo.data.DataProxy
5738  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5739  * configured to reference a certain URL.<br><br>
5740  * <p>
5741  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5742  * from which the running page was served.<br><br>
5743  * <p>
5744  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5745  * <p>
5746  * Be aware that to enable the browser to parse an XML document, the server must set
5747  * the Content-Type header in the HTTP response to "text/xml".
5748  * @constructor
5749  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5750  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5751  * will be used to make the request.
5752  */
5753 Roo.data.HttpProxy = function(conn){
5754     Roo.data.HttpProxy.superclass.constructor.call(this);
5755     // is conn a conn config or a real conn?
5756     this.conn = conn;
5757     this.useAjax = !conn || !conn.events;
5758   
5759 };
5760
5761 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5762     // thse are take from connection...
5763     
5764     /**
5765      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5766      */
5767     /**
5768      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5769      * extra parameters to each request made by this object. (defaults to undefined)
5770      */
5771     /**
5772      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5773      *  to each request made by this object. (defaults to undefined)
5774      */
5775     /**
5776      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5777      */
5778     /**
5779      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5780      */
5781      /**
5782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5783      * @type Boolean
5784      */
5785   
5786
5787     /**
5788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5789      * @type Boolean
5790      */
5791     /**
5792      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5793      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5794      * a finer-grained basis than the DataProxy events.
5795      */
5796     getConnection : function(){
5797         return this.useAjax ? Roo.Ajax : this.conn;
5798     },
5799
5800     /**
5801      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5802      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5803      * process that block using the passed callback.
5804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5805      * for the request to the remote server.
5806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5807      * object into a block of Roo.data.Records.
5808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5809      * The function must be passed <ul>
5810      * <li>The Record block object</li>
5811      * <li>The "arg" argument from the load function</li>
5812      * <li>A boolean success indicator</li>
5813      * </ul>
5814      * @param {Object} scope The scope in which to call the callback
5815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5816      */
5817     load : function(params, reader, callback, scope, arg){
5818         if(this.fireEvent("beforeload", this, params) !== false){
5819             var  o = {
5820                 params : params || {},
5821                 request: {
5822                     callback : callback,
5823                     scope : scope,
5824                     arg : arg
5825                 },
5826                 reader: reader,
5827                 callback : this.loadResponse,
5828                 scope: this
5829             };
5830             if(this.useAjax){
5831                 Roo.applyIf(o, this.conn);
5832                 if(this.activeRequest){
5833                     Roo.Ajax.abort(this.activeRequest);
5834                 }
5835                 this.activeRequest = Roo.Ajax.request(o);
5836             }else{
5837                 this.conn.request(o);
5838             }
5839         }else{
5840             callback.call(scope||this, null, arg, false);
5841         }
5842     },
5843
5844     // private
5845     loadResponse : function(o, success, response){
5846         delete this.activeRequest;
5847         if(!success){
5848             this.fireEvent("loadexception", this, o, response);
5849             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5850             return;
5851         }
5852         var result;
5853         try {
5854             result = o.reader.read(response);
5855         }catch(e){
5856             this.fireEvent("loadexception", this, o, response, e);
5857             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5858             return;
5859         }
5860         
5861         this.fireEvent("load", this, o, o.request.arg);
5862         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5863     },
5864
5865     // private
5866     update : function(dataSet){
5867
5868     },
5869
5870     // private
5871     updateResponse : function(dataSet){
5872
5873     }
5874 });/*
5875  * Based on:
5876  * Ext JS Library 1.1.1
5877  * Copyright(c) 2006-2007, Ext JS, LLC.
5878  *
5879  * Originally Released Under LGPL - original licence link has changed is not relivant.
5880  *
5881  * Fork - LGPL
5882  * <script type="text/javascript">
5883  */
5884
5885 /**
5886  * @class Roo.data.ScriptTagProxy
5887  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5888  * other than the originating domain of the running page.<br><br>
5889  * <p>
5890  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5891  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5892  * <p>
5893  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5894  * source code that is used as the source inside a &lt;script> tag.<br><br>
5895  * <p>
5896  * In order for the browser to process the returned data, the server must wrap the data object
5897  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5898  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5899  * depending on whether the callback name was passed:
5900  * <p>
5901  * <pre><code>
5902 boolean scriptTag = false;
5903 String cb = request.getParameter("callback");
5904 if (cb != null) {
5905     scriptTag = true;
5906     response.setContentType("text/javascript");
5907 } else {
5908     response.setContentType("application/x-json");
5909 }
5910 Writer out = response.getWriter();
5911 if (scriptTag) {
5912     out.write(cb + "(");
5913 }
5914 out.print(dataBlock.toJsonString());
5915 if (scriptTag) {
5916     out.write(");");
5917 }
5918 </pre></code>
5919  *
5920  * @constructor
5921  * @param {Object} config A configuration object.
5922  */
5923 Roo.data.ScriptTagProxy = function(config){
5924     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5925     Roo.apply(this, config);
5926     this.head = document.getElementsByTagName("head")[0];
5927 };
5928
5929 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5930
5931 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5932     /**
5933      * @cfg {String} url The URL from which to request the data object.
5934      */
5935     /**
5936      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5937      */
5938     timeout : 30000,
5939     /**
5940      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5941      * the server the name of the callback function set up by the load call to process the returned data object.
5942      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5943      * javascript output which calls this named function passing the data object as its only parameter.
5944      */
5945     callbackParam : "callback",
5946     /**
5947      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5948      * name to the request.
5949      */
5950     nocache : true,
5951
5952     /**
5953      * Load data from the configured URL, read the data object into
5954      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5955      * process that block using the passed callback.
5956      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5957      * for the request to the remote server.
5958      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5959      * object into a block of Roo.data.Records.
5960      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5961      * The function must be passed <ul>
5962      * <li>The Record block object</li>
5963      * <li>The "arg" argument from the load function</li>
5964      * <li>A boolean success indicator</li>
5965      * </ul>
5966      * @param {Object} scope The scope in which to call the callback
5967      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5968      */
5969     load : function(params, reader, callback, scope, arg){
5970         if(this.fireEvent("beforeload", this, params) !== false){
5971
5972             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5973
5974             var url = this.url;
5975             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5976             if(this.nocache){
5977                 url += "&_dc=" + (new Date().getTime());
5978             }
5979             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5980             var trans = {
5981                 id : transId,
5982                 cb : "stcCallback"+transId,
5983                 scriptId : "stcScript"+transId,
5984                 params : params,
5985                 arg : arg,
5986                 url : url,
5987                 callback : callback,
5988                 scope : scope,
5989                 reader : reader
5990             };
5991             var conn = this;
5992
5993             window[trans.cb] = function(o){
5994                 conn.handleResponse(o, trans);
5995             };
5996
5997             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5998
5999             if(this.autoAbort !== false){
6000                 this.abort();
6001             }
6002
6003             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6004
6005             var script = document.createElement("script");
6006             script.setAttribute("src", url);
6007             script.setAttribute("type", "text/javascript");
6008             script.setAttribute("id", trans.scriptId);
6009             this.head.appendChild(script);
6010
6011             this.trans = trans;
6012         }else{
6013             callback.call(scope||this, null, arg, false);
6014         }
6015     },
6016
6017     // private
6018     isLoading : function(){
6019         return this.trans ? true : false;
6020     },
6021
6022     /**
6023      * Abort the current server request.
6024      */
6025     abort : function(){
6026         if(this.isLoading()){
6027             this.destroyTrans(this.trans);
6028         }
6029     },
6030
6031     // private
6032     destroyTrans : function(trans, isLoaded){
6033         this.head.removeChild(document.getElementById(trans.scriptId));
6034         clearTimeout(trans.timeoutId);
6035         if(isLoaded){
6036             window[trans.cb] = undefined;
6037             try{
6038                 delete window[trans.cb];
6039             }catch(e){}
6040         }else{
6041             // if hasn't been loaded, wait for load to remove it to prevent script error
6042             window[trans.cb] = function(){
6043                 window[trans.cb] = undefined;
6044                 try{
6045                     delete window[trans.cb];
6046                 }catch(e){}
6047             };
6048         }
6049     },
6050
6051     // private
6052     handleResponse : function(o, trans){
6053         this.trans = false;
6054         this.destroyTrans(trans, true);
6055         var result;
6056         try {
6057             result = trans.reader.readRecords(o);
6058         }catch(e){
6059             this.fireEvent("loadexception", this, o, trans.arg, e);
6060             trans.callback.call(trans.scope||window, null, trans.arg, false);
6061             return;
6062         }
6063         this.fireEvent("load", this, o, trans.arg);
6064         trans.callback.call(trans.scope||window, result, trans.arg, true);
6065     },
6066
6067     // private
6068     handleFailure : function(trans){
6069         this.trans = false;
6070         this.destroyTrans(trans, false);
6071         this.fireEvent("loadexception", this, null, trans.arg);
6072         trans.callback.call(trans.scope||window, null, trans.arg, false);
6073     }
6074 });/*
6075  * Based on:
6076  * Ext JS Library 1.1.1
6077  * Copyright(c) 2006-2007, Ext JS, LLC.
6078  *
6079  * Originally Released Under LGPL - original licence link has changed is not relivant.
6080  *
6081  * Fork - LGPL
6082  * <script type="text/javascript">
6083  */
6084
6085 /**
6086  * @class Roo.data.JsonReader
6087  * @extends Roo.data.DataReader
6088  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6089  * based on mappings in a provided Roo.data.Record constructor.
6090  * 
6091  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6092  * in the reply previously. 
6093  * 
6094  * <p>
6095  * Example code:
6096  * <pre><code>
6097 var RecordDef = Roo.data.Record.create([
6098     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6099     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6100 ]);
6101 var myReader = new Roo.data.JsonReader({
6102     totalProperty: "results",    // The property which contains the total dataset size (optional)
6103     root: "rows",                // The property which contains an Array of row objects
6104     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6105 }, RecordDef);
6106 </code></pre>
6107  * <p>
6108  * This would consume a JSON file like this:
6109  * <pre><code>
6110 { 'results': 2, 'rows': [
6111     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6112     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6113 }
6114 </code></pre>
6115  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6116  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6117  * paged from the remote server.
6118  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6119  * @cfg {String} root name of the property which contains the Array of row objects.
6120  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6121  * @constructor
6122  * Create a new JsonReader
6123  * @param {Object} meta Metadata configuration options
6124  * @param {Object} recordType Either an Array of field definition objects,
6125  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6126  */
6127 Roo.data.JsonReader = function(meta, recordType){
6128     
6129     meta = meta || {};
6130     // set some defaults:
6131     Roo.applyIf(meta, {
6132         totalProperty: 'total',
6133         successProperty : 'success',
6134         root : 'data',
6135         id : 'id'
6136     });
6137     
6138     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6139 };
6140 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6141     
6142     /**
6143      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6144      * Used by Store query builder to append _requestMeta to params.
6145      * 
6146      */
6147     metaFromRemote : false,
6148     /**
6149      * This method is only used by a DataProxy which has retrieved data from a remote server.
6150      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6151      * @return {Object} data A data block which is used by an Roo.data.Store object as
6152      * a cache of Roo.data.Records.
6153      */
6154     read : function(response){
6155         var json = response.responseText;
6156        
6157         var o = /* eval:var:o */ eval("("+json+")");
6158         if(!o) {
6159             throw {message: "JsonReader.read: Json object not found"};
6160         }
6161         
6162         if(o.metaData){
6163             
6164             delete this.ef;
6165             this.metaFromRemote = true;
6166             this.meta = o.metaData;
6167             this.recordType = Roo.data.Record.create(o.metaData.fields);
6168             this.onMetaChange(this.meta, this.recordType, o);
6169         }
6170         return this.readRecords(o);
6171     },
6172
6173     // private function a store will implement
6174     onMetaChange : function(meta, recordType, o){
6175
6176     },
6177
6178     /**
6179          * @ignore
6180          */
6181     simpleAccess: function(obj, subsc) {
6182         return obj[subsc];
6183     },
6184
6185         /**
6186          * @ignore
6187          */
6188     getJsonAccessor: function(){
6189         var re = /[\[\.]/;
6190         return function(expr) {
6191             try {
6192                 return(re.test(expr))
6193                     ? new Function("obj", "return obj." + expr)
6194                     : function(obj){
6195                         return obj[expr];
6196                     };
6197             } catch(e){}
6198             return Roo.emptyFn;
6199         };
6200     }(),
6201
6202     /**
6203      * Create a data block containing Roo.data.Records from an XML document.
6204      * @param {Object} o An object which contains an Array of row objects in the property specified
6205      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6206      * which contains the total size of the dataset.
6207      * @return {Object} data A data block which is used by an Roo.data.Store object as
6208      * a cache of Roo.data.Records.
6209      */
6210     readRecords : function(o){
6211         /**
6212          * After any data loads, the raw JSON data is available for further custom processing.
6213          * @type Object
6214          */
6215         this.jsonData = o;
6216         var s = this.meta, Record = this.recordType,
6217             f = Record.prototype.fields, fi = f.items, fl = f.length;
6218
6219 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6220         if (!this.ef) {
6221             if(s.totalProperty) {
6222                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6223                 }
6224                 if(s.successProperty) {
6225                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6226                 }
6227                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6228                 if (s.id) {
6229                         var g = this.getJsonAccessor(s.id);
6230                         this.getId = function(rec) {
6231                                 var r = g(rec);
6232                                 return (r === undefined || r === "") ? null : r;
6233                         };
6234                 } else {
6235                         this.getId = function(){return null;};
6236                 }
6237             this.ef = [];
6238             for(var jj = 0; jj < fl; jj++){
6239                 f = fi[jj];
6240                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6241                 this.ef[jj] = this.getJsonAccessor(map);
6242             }
6243         }
6244
6245         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6246         if(s.totalProperty){
6247             var vt = parseInt(this.getTotal(o), 10);
6248             if(!isNaN(vt)){
6249                 totalRecords = vt;
6250             }
6251         }
6252         if(s.successProperty){
6253             var vs = this.getSuccess(o);
6254             if(vs === false || vs === 'false'){
6255                 success = false;
6256             }
6257         }
6258         var records = [];
6259             for(var i = 0; i < c; i++){
6260                     var n = root[i];
6261                 var values = {};
6262                 var id = this.getId(n);
6263                 for(var j = 0; j < fl; j++){
6264                     f = fi[j];
6265                 var v = this.ef[j](n);
6266                 if (!f.convert) {
6267                     Roo.log('missing convert for ' + f.name);
6268                     Roo.log(f);
6269                     continue;
6270                 }
6271                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6272                 }
6273                 var record = new Record(values, id);
6274                 record.json = n;
6275                 records[i] = record;
6276             }
6277             return {
6278                 success : success,
6279                 records : records,
6280                 totalRecords : totalRecords
6281             };
6282     }
6283 });/*
6284  * Based on:
6285  * Ext JS Library 1.1.1
6286  * Copyright(c) 2006-2007, Ext JS, LLC.
6287  *
6288  * Originally Released Under LGPL - original licence link has changed is not relivant.
6289  *
6290  * Fork - LGPL
6291  * <script type="text/javascript">
6292  */
6293
6294 /**
6295  * @class Roo.data.XmlReader
6296  * @extends Roo.data.DataReader
6297  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6298  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6299  * <p>
6300  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6301  * header in the HTTP response must be set to "text/xml".</em>
6302  * <p>
6303  * Example code:
6304  * <pre><code>
6305 var RecordDef = Roo.data.Record.create([
6306    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6307    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6308 ]);
6309 var myReader = new Roo.data.XmlReader({
6310    totalRecords: "results", // The element which contains the total dataset size (optional)
6311    record: "row",           // The repeated element which contains row information
6312    id: "id"                 // The element within the row that provides an ID for the record (optional)
6313 }, RecordDef);
6314 </code></pre>
6315  * <p>
6316  * This would consume an XML file like this:
6317  * <pre><code>
6318 &lt;?xml?>
6319 &lt;dataset>
6320  &lt;results>2&lt;/results>
6321  &lt;row>
6322    &lt;id>1&lt;/id>
6323    &lt;name>Bill&lt;/name>
6324    &lt;occupation>Gardener&lt;/occupation>
6325  &lt;/row>
6326  &lt;row>
6327    &lt;id>2&lt;/id>
6328    &lt;name>Ben&lt;/name>
6329    &lt;occupation>Horticulturalist&lt;/occupation>
6330  &lt;/row>
6331 &lt;/dataset>
6332 </code></pre>
6333  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6334  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6335  * paged from the remote server.
6336  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6337  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6338  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6339  * a record identifier value.
6340  * @constructor
6341  * Create a new XmlReader
6342  * @param {Object} meta Metadata configuration options
6343  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6344  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6345  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6346  */
6347 Roo.data.XmlReader = function(meta, recordType){
6348     meta = meta || {};
6349     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6350 };
6351 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6352     /**
6353      * This method is only used by a DataProxy which has retrieved data from a remote server.
6354          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6355          * to contain a method called 'responseXML' that returns an XML document object.
6356      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6357      * a cache of Roo.data.Records.
6358      */
6359     read : function(response){
6360         var doc = response.responseXML;
6361         if(!doc) {
6362             throw {message: "XmlReader.read: XML Document not available"};
6363         }
6364         return this.readRecords(doc);
6365     },
6366
6367     /**
6368      * Create a data block containing Roo.data.Records from an XML document.
6369          * @param {Object} doc A parsed XML document.
6370      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6371      * a cache of Roo.data.Records.
6372      */
6373     readRecords : function(doc){
6374         /**
6375          * After any data loads/reads, the raw XML Document is available for further custom processing.
6376          * @type XMLDocument
6377          */
6378         this.xmlData = doc;
6379         var root = doc.documentElement || doc;
6380         var q = Roo.DomQuery;
6381         var recordType = this.recordType, fields = recordType.prototype.fields;
6382         var sid = this.meta.id;
6383         var totalRecords = 0, success = true;
6384         if(this.meta.totalRecords){
6385             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6386         }
6387         
6388         if(this.meta.success){
6389             var sv = q.selectValue(this.meta.success, root, true);
6390             success = sv !== false && sv !== 'false';
6391         }
6392         var records = [];
6393         var ns = q.select(this.meta.record, root);
6394         for(var i = 0, len = ns.length; i < len; i++) {
6395                 var n = ns[i];
6396                 var values = {};
6397                 var id = sid ? q.selectValue(sid, n) : undefined;
6398                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6399                     var f = fields.items[j];
6400                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6401                     v = f.convert(v);
6402                     values[f.name] = v;
6403                 }
6404                 var record = new recordType(values, id);
6405                 record.node = n;
6406                 records[records.length] = record;
6407             }
6408
6409             return {
6410                 success : success,
6411                 records : records,
6412                 totalRecords : totalRecords || records.length
6413             };
6414     }
6415 });/*
6416  * Based on:
6417  * Ext JS Library 1.1.1
6418  * Copyright(c) 2006-2007, Ext JS, LLC.
6419  *
6420  * Originally Released Under LGPL - original licence link has changed is not relivant.
6421  *
6422  * Fork - LGPL
6423  * <script type="text/javascript">
6424  */
6425
6426 /**
6427  * @class Roo.data.ArrayReader
6428  * @extends Roo.data.DataReader
6429  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6430  * Each element of that Array represents a row of data fields. The
6431  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6432  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6433  * <p>
6434  * Example code:.
6435  * <pre><code>
6436 var RecordDef = Roo.data.Record.create([
6437     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6438     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6439 ]);
6440 var myReader = new Roo.data.ArrayReader({
6441     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6442 }, RecordDef);
6443 </code></pre>
6444  * <p>
6445  * This would consume an Array like this:
6446  * <pre><code>
6447 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6448   </code></pre>
6449  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6450  * @constructor
6451  * Create a new JsonReader
6452  * @param {Object} meta Metadata configuration options.
6453  * @param {Object} recordType Either an Array of field definition objects
6454  * as specified to {@link Roo.data.Record#create},
6455  * or an {@link Roo.data.Record} object
6456  * created using {@link Roo.data.Record#create}.
6457  */
6458 Roo.data.ArrayReader = function(meta, recordType){
6459     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6460 };
6461
6462 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6463     /**
6464      * Create a data block containing Roo.data.Records from an XML document.
6465      * @param {Object} o An Array of row objects which represents the dataset.
6466      * @return {Object} data A data block which is used by an Roo.data.Store object as
6467      * a cache of Roo.data.Records.
6468      */
6469     readRecords : function(o){
6470         var sid = this.meta ? this.meta.id : null;
6471         var recordType = this.recordType, fields = recordType.prototype.fields;
6472         var records = [];
6473         var root = o;
6474             for(var i = 0; i < root.length; i++){
6475                     var n = root[i];
6476                 var values = {};
6477                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6478                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6479                 var f = fields.items[j];
6480                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6481                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6482                 v = f.convert(v);
6483                 values[f.name] = v;
6484             }
6485                 var record = new recordType(values, id);
6486                 record.json = n;
6487                 records[records.length] = record;
6488             }
6489             return {
6490                 records : records,
6491                 totalRecords : records.length
6492             };
6493     }
6494 });/*
6495  * Based on:
6496  * Ext JS Library 1.1.1
6497  * Copyright(c) 2006-2007, Ext JS, LLC.
6498  *
6499  * Originally Released Under LGPL - original licence link has changed is not relivant.
6500  *
6501  * Fork - LGPL
6502  * <script type="text/javascript">
6503  */
6504
6505
6506 /**
6507  * @class Roo.data.Tree
6508  * @extends Roo.util.Observable
6509  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6510  * in the tree have most standard DOM functionality.
6511  * @constructor
6512  * @param {Node} root (optional) The root node
6513  */
6514 Roo.data.Tree = function(root){
6515    this.nodeHash = {};
6516    /**
6517     * The root node for this tree
6518     * @type Node
6519     */
6520    this.root = null;
6521    if(root){
6522        this.setRootNode(root);
6523    }
6524    this.addEvents({
6525        /**
6526         * @event append
6527         * Fires when a new child node is appended to a node in this tree.
6528         * @param {Tree} tree The owner tree
6529         * @param {Node} parent The parent node
6530         * @param {Node} node The newly appended node
6531         * @param {Number} index The index of the newly appended node
6532         */
6533        "append" : true,
6534        /**
6535         * @event remove
6536         * Fires when a child node is removed from a node in this tree.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node removed
6540         */
6541        "remove" : true,
6542        /**
6543         * @event move
6544         * Fires when a node is moved to a new location in the tree
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node moved
6547         * @param {Node} oldParent The old parent of this node
6548         * @param {Node} newParent The new parent of this node
6549         * @param {Number} index The index it was moved to
6550         */
6551        "move" : true,
6552        /**
6553         * @event insert
6554         * Fires when a new child node is inserted in a node in this tree.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node inserted
6558         * @param {Node} refNode The child node the node was inserted before
6559         */
6560        "insert" : true,
6561        /**
6562         * @event beforeappend
6563         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6564         * @param {Tree} tree The owner tree
6565         * @param {Node} parent The parent node
6566         * @param {Node} node The child node to be appended
6567         */
6568        "beforeappend" : true,
6569        /**
6570         * @event beforeremove
6571         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6572         * @param {Tree} tree The owner tree
6573         * @param {Node} parent The parent node
6574         * @param {Node} node The child node to be removed
6575         */
6576        "beforeremove" : true,
6577        /**
6578         * @event beforemove
6579         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} node The node being moved
6582         * @param {Node} oldParent The parent of the node
6583         * @param {Node} newParent The new parent the node is moving to
6584         * @param {Number} index The index it is being moved to
6585         */
6586        "beforemove" : true,
6587        /**
6588         * @event beforeinsert
6589         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6590         * @param {Tree} tree The owner tree
6591         * @param {Node} parent The parent node
6592         * @param {Node} node The child node to be inserted
6593         * @param {Node} refNode The child node the node is being inserted before
6594         */
6595        "beforeinsert" : true
6596    });
6597
6598     Roo.data.Tree.superclass.constructor.call(this);
6599 };
6600
6601 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6602     pathSeparator: "/",
6603
6604     proxyNodeEvent : function(){
6605         return this.fireEvent.apply(this, arguments);
6606     },
6607
6608     /**
6609      * Returns the root node for this tree.
6610      * @return {Node}
6611      */
6612     getRootNode : function(){
6613         return this.root;
6614     },
6615
6616     /**
6617      * Sets the root node for this tree.
6618      * @param {Node} node
6619      * @return {Node}
6620      */
6621     setRootNode : function(node){
6622         this.root = node;
6623         node.ownerTree = this;
6624         node.isRoot = true;
6625         this.registerNode(node);
6626         return node;
6627     },
6628
6629     /**
6630      * Gets a node in this tree by its id.
6631      * @param {String} id
6632      * @return {Node}
6633      */
6634     getNodeById : function(id){
6635         return this.nodeHash[id];
6636     },
6637
6638     registerNode : function(node){
6639         this.nodeHash[node.id] = node;
6640     },
6641
6642     unregisterNode : function(node){
6643         delete this.nodeHash[node.id];
6644     },
6645
6646     toString : function(){
6647         return "[Tree"+(this.id?" "+this.id:"")+"]";
6648     }
6649 });
6650
6651 /**
6652  * @class Roo.data.Node
6653  * @extends Roo.util.Observable
6654  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6655  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6656  * @constructor
6657  * @param {Object} attributes The attributes/config for the node
6658  */
6659 Roo.data.Node = function(attributes){
6660     /**
6661      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6662      * @type {Object}
6663      */
6664     this.attributes = attributes || {};
6665     this.leaf = this.attributes.leaf;
6666     /**
6667      * The node id. @type String
6668      */
6669     this.id = this.attributes.id;
6670     if(!this.id){
6671         this.id = Roo.id(null, "ynode-");
6672         this.attributes.id = this.id;
6673     }
6674     /**
6675      * 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     /**
9263      * @cfg {String} selectedClass The css class to add to selected nodes
9264      */
9265     selectedClass : "x-view-selected",
9266      /**
9267      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9268      */
9269     emptyText : "",
9270     /**
9271      * @cfg {Boolean} multiSelect Allow multiple selection
9272      */
9273     multiSelect : false,
9274     /**
9275      * @cfg {Boolean} singleSelect Allow single selection
9276      */
9277     singleSelect:  false,
9278     
9279     /**
9280      * @cfg {Boolean} toggleSelect - selecting 
9281      */
9282     toggleSelect : false,
9283     
9284     /**
9285      * Returns the element this view is bound to.
9286      * @return {Roo.Element}
9287      */
9288     getEl : function(){
9289         return this.el;
9290     },
9291
9292     /**
9293      * Refreshes the view.
9294      */
9295     refresh : function(){
9296         var t = this.tpl;
9297         this.clearSelections();
9298         this.el.update("");
9299         var html = [];
9300         var records = this.store.getRange();
9301         if(records.length < 1){
9302             this.el.update(this.emptyText);
9303             return;
9304         }
9305         for(var i = 0, len = records.length; i < len; i++){
9306             var data = this.prepareData(records[i].data, i, records[i]);
9307             this.fireEvent("preparedata", this, data, i, records[i]);
9308             html[html.length] = t.apply(data);
9309         }
9310         this.el.update(html.join(""));
9311         this.nodes = this.el.dom.childNodes;
9312         this.updateIndexes(0);
9313     },
9314
9315     /**
9316      * Function to override to reformat the data that is sent to
9317      * the template for each node.
9318      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9319      * a JSON object for an UpdateManager bound view).
9320      */
9321     prepareData : function(data){
9322         return data;
9323     },
9324
9325     onUpdate : function(ds, record){
9326         this.clearSelections();
9327         var index = this.store.indexOf(record);
9328         var n = this.nodes[index];
9329         this.tpl.insertBefore(n, this.prepareData(record.data));
9330         n.parentNode.removeChild(n);
9331         this.updateIndexes(index, index);
9332     },
9333
9334     onAdd : function(ds, records, index){
9335         this.clearSelections();
9336         if(this.nodes.length == 0){
9337             this.refresh();
9338             return;
9339         }
9340         var n = this.nodes[index];
9341         for(var i = 0, len = records.length; i < len; i++){
9342             var d = this.prepareData(records[i].data);
9343             if(n){
9344                 this.tpl.insertBefore(n, d);
9345             }else{
9346                 this.tpl.append(this.el, d);
9347             }
9348         }
9349         this.updateIndexes(index);
9350     },
9351
9352     onRemove : function(ds, record, index){
9353         this.clearSelections();
9354         this.el.dom.removeChild(this.nodes[index]);
9355         this.updateIndexes(index);
9356     },
9357
9358     /**
9359      * Refresh an individual node.
9360      * @param {Number} index
9361      */
9362     refreshNode : function(index){
9363         this.onUpdate(this.store, this.store.getAt(index));
9364     },
9365
9366     updateIndexes : function(startIndex, endIndex){
9367         var ns = this.nodes;
9368         startIndex = startIndex || 0;
9369         endIndex = endIndex || ns.length - 1;
9370         for(var i = startIndex; i <= endIndex; i++){
9371             ns[i].nodeIndex = i;
9372         }
9373     },
9374
9375     /**
9376      * Changes the data store this view uses and refresh the view.
9377      * @param {Store} store
9378      */
9379     setStore : function(store, initial){
9380         if(!initial && this.store){
9381             this.store.un("datachanged", this.refresh);
9382             this.store.un("add", this.onAdd);
9383             this.store.un("remove", this.onRemove);
9384             this.store.un("update", this.onUpdate);
9385             this.store.un("clear", this.refresh);
9386         }
9387         if(store){
9388           
9389             store.on("datachanged", this.refresh, this);
9390             store.on("add", this.onAdd, this);
9391             store.on("remove", this.onRemove, this);
9392             store.on("update", this.onUpdate, this);
9393             store.on("clear", this.refresh, this);
9394         }
9395         
9396         if(store){
9397             this.refresh();
9398         }
9399     },
9400
9401     /**
9402      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9403      * @param {HTMLElement} node
9404      * @return {HTMLElement} The template node
9405      */
9406     findItemFromChild : function(node){
9407         var el = this.el.dom;
9408         if(!node || node.parentNode == el){
9409                     return node;
9410             }
9411             var p = node.parentNode;
9412             while(p && p != el){
9413             if(p.parentNode == el){
9414                 return p;
9415             }
9416             p = p.parentNode;
9417         }
9418             return null;
9419     },
9420
9421     /** @ignore */
9422     onClick : function(e){
9423         var item = this.findItemFromChild(e.getTarget());
9424         if(item){
9425             var index = this.indexOf(item);
9426             if(this.onItemClick(item, index, e) !== false){
9427                 this.fireEvent("click", this, index, item, e);
9428             }
9429         }else{
9430             this.clearSelections();
9431         }
9432     },
9433
9434     /** @ignore */
9435     onContextMenu : function(e){
9436         var item = this.findItemFromChild(e.getTarget());
9437         if(item){
9438             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9439         }
9440     },
9441
9442     /** @ignore */
9443     onDblClick : function(e){
9444         var item = this.findItemFromChild(e.getTarget());
9445         if(item){
9446             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9447         }
9448     },
9449
9450     onItemClick : function(item, index, e)
9451     {
9452         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9453             return false;
9454         }
9455         if (this.toggleSelect) {
9456             var m = this.isSelected(item) ? 'unselect' : 'select';
9457             Roo.log(m);
9458             var _t = this;
9459             _t[m](item, true, false);
9460             return true;
9461         }
9462         if(this.multiSelect || this.singleSelect){
9463             if(this.multiSelect && e.shiftKey && this.lastSelection){
9464                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9465             }else{
9466                 this.select(item, this.multiSelect && e.ctrlKey);
9467                 this.lastSelection = item;
9468             }
9469             e.preventDefault();
9470         }
9471         return true;
9472     },
9473
9474     /**
9475      * Get the number of selected nodes.
9476      * @return {Number}
9477      */
9478     getSelectionCount : function(){
9479         return this.selections.length;
9480     },
9481
9482     /**
9483      * Get the currently selected nodes.
9484      * @return {Array} An array of HTMLElements
9485      */
9486     getSelectedNodes : function(){
9487         return this.selections;
9488     },
9489
9490     /**
9491      * Get the indexes of the selected nodes.
9492      * @return {Array}
9493      */
9494     getSelectedIndexes : function(){
9495         var indexes = [], s = this.selections;
9496         for(var i = 0, len = s.length; i < len; i++){
9497             indexes.push(s[i].nodeIndex);
9498         }
9499         return indexes;
9500     },
9501
9502     /**
9503      * Clear all selections
9504      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9505      */
9506     clearSelections : function(suppressEvent){
9507         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9508             this.cmp.elements = this.selections;
9509             this.cmp.removeClass(this.selectedClass);
9510             this.selections = [];
9511             if(!suppressEvent){
9512                 this.fireEvent("selectionchange", this, this.selections);
9513             }
9514         }
9515     },
9516
9517     /**
9518      * Returns true if the passed node is selected
9519      * @param {HTMLElement/Number} node The node or node index
9520      * @return {Boolean}
9521      */
9522     isSelected : function(node){
9523         var s = this.selections;
9524         if(s.length < 1){
9525             return false;
9526         }
9527         node = this.getNode(node);
9528         return s.indexOf(node) !== -1;
9529     },
9530
9531     /**
9532      * Selects nodes.
9533      * @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
9534      * @param {Boolean} keepExisting (optional) true to keep existing selections
9535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9536      */
9537     select : function(nodeInfo, keepExisting, suppressEvent){
9538         if(nodeInfo instanceof Array){
9539             if(!keepExisting){
9540                 this.clearSelections(true);
9541             }
9542             for(var i = 0, len = nodeInfo.length; i < len; i++){
9543                 this.select(nodeInfo[i], true, true);
9544             }
9545             return;
9546         } 
9547         var node = this.getNode(nodeInfo);
9548         if(!node || this.isSelected(node)){
9549             return; // already selected.
9550         }
9551         if(!keepExisting){
9552             this.clearSelections(true);
9553         }
9554         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9555             Roo.fly(node).addClass(this.selectedClass);
9556             this.selections.push(node);
9557             if(!suppressEvent){
9558                 this.fireEvent("selectionchange", this, this.selections);
9559             }
9560         }
9561         
9562         
9563     },
9564       /**
9565      * Unselects nodes.
9566      * @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
9567      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9569      */
9570     unselect : function(nodeInfo, keepExisting, suppressEvent)
9571     {
9572         if(nodeInfo instanceof Array){
9573             Roo.each(this.selections, function(s) {
9574                 this.unselect(s, nodeInfo);
9575             }, this);
9576             return;
9577         }
9578         var node = this.getNode(nodeInfo);
9579         if(!node || !this.isSelected(node)){
9580             Roo.log("not selected");
9581             return; // not selected.
9582         }
9583         // fireevent???
9584         var ns = [];
9585         Roo.each(this.selections, function(s) {
9586             if (s == node ) {
9587                 Roo.fly(node).removeClass(this.selectedClass);
9588
9589                 return;
9590             }
9591             ns.push(s);
9592         },this);
9593         
9594         this.selections= ns;
9595         this.fireEvent("selectionchange", this, this.selections);
9596     },
9597
9598     /**
9599      * Gets a template node.
9600      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9601      * @return {HTMLElement} The node or null if it wasn't found
9602      */
9603     getNode : function(nodeInfo){
9604         if(typeof nodeInfo == "string"){
9605             return document.getElementById(nodeInfo);
9606         }else if(typeof nodeInfo == "number"){
9607             return this.nodes[nodeInfo];
9608         }
9609         return nodeInfo;
9610     },
9611
9612     /**
9613      * Gets a range template nodes.
9614      * @param {Number} startIndex
9615      * @param {Number} endIndex
9616      * @return {Array} An array of nodes
9617      */
9618     getNodes : function(start, end){
9619         var ns = this.nodes;
9620         start = start || 0;
9621         end = typeof end == "undefined" ? ns.length - 1 : end;
9622         var nodes = [];
9623         if(start <= end){
9624             for(var i = start; i <= end; i++){
9625                 nodes.push(ns[i]);
9626             }
9627         } else{
9628             for(var i = start; i >= end; i--){
9629                 nodes.push(ns[i]);
9630             }
9631         }
9632         return nodes;
9633     },
9634
9635     /**
9636      * Finds the index of the passed node
9637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9638      * @return {Number} The index of the node or -1
9639      */
9640     indexOf : function(node){
9641         node = this.getNode(node);
9642         if(typeof node.nodeIndex == "number"){
9643             return node.nodeIndex;
9644         }
9645         var ns = this.nodes;
9646         for(var i = 0, len = ns.length; i < len; i++){
9647             if(ns[i] == node){
9648                 return i;
9649             }
9650         }
9651         return -1;
9652     }
9653 });
9654 /*
9655  * Based on:
9656  * Ext JS Library 1.1.1
9657  * Copyright(c) 2006-2007, Ext JS, LLC.
9658  *
9659  * Originally Released Under LGPL - original licence link has changed is not relivant.
9660  *
9661  * Fork - LGPL
9662  * <script type="text/javascript">
9663  */
9664
9665 /**
9666  * @class Roo.JsonView
9667  * @extends Roo.View
9668  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9669 <pre><code>
9670 var view = new Roo.JsonView({
9671     container: "my-element",
9672     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9673     multiSelect: true, 
9674     jsonRoot: "data" 
9675 });
9676
9677 // listen for node click?
9678 view.on("click", function(vw, index, node, e){
9679     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9680 });
9681
9682 // direct load of JSON data
9683 view.load("foobar.php");
9684
9685 // Example from my blog list
9686 var tpl = new Roo.Template(
9687     '&lt;div class="entry"&gt;' +
9688     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9689     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9690     "&lt;/div&gt;&lt;hr /&gt;"
9691 );
9692
9693 var moreView = new Roo.JsonView({
9694     container :  "entry-list", 
9695     template : tpl,
9696     jsonRoot: "posts"
9697 });
9698 moreView.on("beforerender", this.sortEntries, this);
9699 moreView.load({
9700     url: "/blog/get-posts.php",
9701     params: "allposts=true",
9702     text: "Loading Blog Entries..."
9703 });
9704 </code></pre>
9705
9706 * Note: old code is supported with arguments : (container, template, config)
9707
9708
9709  * @constructor
9710  * Create a new JsonView
9711  * 
9712  * @param {Object} config The config object
9713  * 
9714  */
9715 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9716     
9717     
9718     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9719
9720     var um = this.el.getUpdateManager();
9721     um.setRenderer(this);
9722     um.on("update", this.onLoad, this);
9723     um.on("failure", this.onLoadException, this);
9724
9725     /**
9726      * @event beforerender
9727      * Fires before rendering of the downloaded JSON data.
9728      * @param {Roo.JsonView} this
9729      * @param {Object} data The JSON data loaded
9730      */
9731     /**
9732      * @event load
9733      * Fires when data is loaded.
9734      * @param {Roo.JsonView} this
9735      * @param {Object} data The JSON data loaded
9736      * @param {Object} response The raw Connect response object
9737      */
9738     /**
9739      * @event loadexception
9740      * Fires when loading fails.
9741      * @param {Roo.JsonView} this
9742      * @param {Object} response The raw Connect response object
9743      */
9744     this.addEvents({
9745         'beforerender' : true,
9746         'load' : true,
9747         'loadexception' : true
9748     });
9749 };
9750 Roo.extend(Roo.JsonView, Roo.View, {
9751     /**
9752      * @type {String} The root property in the loaded JSON object that contains the data
9753      */
9754     jsonRoot : "",
9755
9756     /**
9757      * Refreshes the view.
9758      */
9759     refresh : function(){
9760         this.clearSelections();
9761         this.el.update("");
9762         var html = [];
9763         var o = this.jsonData;
9764         if(o && o.length > 0){
9765             for(var i = 0, len = o.length; i < len; i++){
9766                 var data = this.prepareData(o[i], i, o);
9767                 html[html.length] = this.tpl.apply(data);
9768             }
9769         }else{
9770             html.push(this.emptyText);
9771         }
9772         this.el.update(html.join(""));
9773         this.nodes = this.el.dom.childNodes;
9774         this.updateIndexes(0);
9775     },
9776
9777     /**
9778      * 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.
9779      * @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:
9780      <pre><code>
9781      view.load({
9782          url: "your-url.php",
9783          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9784          callback: yourFunction,
9785          scope: yourObject, //(optional scope)
9786          discardUrl: false,
9787          nocache: false,
9788          text: "Loading...",
9789          timeout: 30,
9790          scripts: false
9791      });
9792      </code></pre>
9793      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9794      * 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.
9795      * @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}
9796      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9797      * @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.
9798      */
9799     load : function(){
9800         var um = this.el.getUpdateManager();
9801         um.update.apply(um, arguments);
9802     },
9803
9804     render : function(el, response){
9805         this.clearSelections();
9806         this.el.update("");
9807         var o;
9808         try{
9809             o = Roo.util.JSON.decode(response.responseText);
9810             if(this.jsonRoot){
9811                 
9812                 o = o[this.jsonRoot];
9813             }
9814         } catch(e){
9815         }
9816         /**
9817          * The current JSON data or null
9818          */
9819         this.jsonData = o;
9820         this.beforeRender();
9821         this.refresh();
9822     },
9823
9824 /**
9825  * Get the number of records in the current JSON dataset
9826  * @return {Number}
9827  */
9828     getCount : function(){
9829         return this.jsonData ? this.jsonData.length : 0;
9830     },
9831
9832 /**
9833  * Returns the JSON object for the specified node(s)
9834  * @param {HTMLElement/Array} node The node or an array of nodes
9835  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9836  * you get the JSON object for the node
9837  */
9838     getNodeData : function(node){
9839         if(node instanceof Array){
9840             var data = [];
9841             for(var i = 0, len = node.length; i < len; i++){
9842                 data.push(this.getNodeData(node[i]));
9843             }
9844             return data;
9845         }
9846         return this.jsonData[this.indexOf(node)] || null;
9847     },
9848
9849     beforeRender : function(){
9850         this.snapshot = this.jsonData;
9851         if(this.sortInfo){
9852             this.sort.apply(this, this.sortInfo);
9853         }
9854         this.fireEvent("beforerender", this, this.jsonData);
9855     },
9856
9857     onLoad : function(el, o){
9858         this.fireEvent("load", this, this.jsonData, o);
9859     },
9860
9861     onLoadException : function(el, o){
9862         this.fireEvent("loadexception", this, o);
9863     },
9864
9865 /**
9866  * Filter the data by a specific property.
9867  * @param {String} property A property on your JSON objects
9868  * @param {String/RegExp} value Either string that the property values
9869  * should start with, or a RegExp to test against the property
9870  */
9871     filter : function(property, value){
9872         if(this.jsonData){
9873             var data = [];
9874             var ss = this.snapshot;
9875             if(typeof value == "string"){
9876                 var vlen = value.length;
9877                 if(vlen == 0){
9878                     this.clearFilter();
9879                     return;
9880                 }
9881                 value = value.toLowerCase();
9882                 for(var i = 0, len = ss.length; i < len; i++){
9883                     var o = ss[i];
9884                     if(o[property].substr(0, vlen).toLowerCase() == value){
9885                         data.push(o);
9886                     }
9887                 }
9888             } else if(value.exec){ // regex?
9889                 for(var i = 0, len = ss.length; i < len; i++){
9890                     var o = ss[i];
9891                     if(value.test(o[property])){
9892                         data.push(o);
9893                     }
9894                 }
9895             } else{
9896                 return;
9897             }
9898             this.jsonData = data;
9899             this.refresh();
9900         }
9901     },
9902
9903 /**
9904  * Filter by a function. The passed function will be called with each
9905  * object in the current dataset. If the function returns true the value is kept,
9906  * otherwise it is filtered.
9907  * @param {Function} fn
9908  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9909  */
9910     filterBy : function(fn, scope){
9911         if(this.jsonData){
9912             var data = [];
9913             var ss = this.snapshot;
9914             for(var i = 0, len = ss.length; i < len; i++){
9915                 var o = ss[i];
9916                 if(fn.call(scope || this, o)){
9917                     data.push(o);
9918                 }
9919             }
9920             this.jsonData = data;
9921             this.refresh();
9922         }
9923     },
9924
9925 /**
9926  * Clears the current filter.
9927  */
9928     clearFilter : function(){
9929         if(this.snapshot && this.jsonData != this.snapshot){
9930             this.jsonData = this.snapshot;
9931             this.refresh();
9932         }
9933     },
9934
9935
9936 /**
9937  * Sorts the data for this view and refreshes it.
9938  * @param {String} property A property on your JSON objects to sort on
9939  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9940  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9941  */
9942     sort : function(property, dir, sortType){
9943         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9944         if(this.jsonData){
9945             var p = property;
9946             var dsc = dir && dir.toLowerCase() == "desc";
9947             var f = function(o1, o2){
9948                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9949                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9950                 ;
9951                 if(v1 < v2){
9952                     return dsc ? +1 : -1;
9953                 } else if(v1 > v2){
9954                     return dsc ? -1 : +1;
9955                 } else{
9956                     return 0;
9957                 }
9958             };
9959             this.jsonData.sort(f);
9960             this.refresh();
9961             if(this.jsonData != this.snapshot){
9962                 this.snapshot.sort(f);
9963             }
9964         }
9965     }
9966 });/*
9967  * Based on:
9968  * Ext JS Library 1.1.1
9969  * Copyright(c) 2006-2007, Ext JS, LLC.
9970  *
9971  * Originally Released Under LGPL - original licence link has changed is not relivant.
9972  *
9973  * Fork - LGPL
9974  * <script type="text/javascript">
9975  */
9976  
9977
9978 /**
9979  * @class Roo.ColorPalette
9980  * @extends Roo.Component
9981  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9982  * Here's an example of typical usage:
9983  * <pre><code>
9984 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9985 cp.render('my-div');
9986
9987 cp.on('select', function(palette, selColor){
9988     // do something with selColor
9989 });
9990 </code></pre>
9991  * @constructor
9992  * Create a new ColorPalette
9993  * @param {Object} config The config object
9994  */
9995 Roo.ColorPalette = function(config){
9996     Roo.ColorPalette.superclass.constructor.call(this, config);
9997     this.addEvents({
9998         /**
9999              * @event select
10000              * Fires when a color is selected
10001              * @param {ColorPalette} this
10002              * @param {String} color The 6-digit color hex code (without the # symbol)
10003              */
10004         select: true
10005     });
10006
10007     if(this.handler){
10008         this.on("select", this.handler, this.scope, true);
10009     }
10010 };
10011 Roo.extend(Roo.ColorPalette, Roo.Component, {
10012     /**
10013      * @cfg {String} itemCls
10014      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10015      */
10016     itemCls : "x-color-palette",
10017     /**
10018      * @cfg {String} value
10019      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10020      * the hex codes are case-sensitive.
10021      */
10022     value : null,
10023     clickEvent:'click',
10024     // private
10025     ctype: "Roo.ColorPalette",
10026
10027     /**
10028      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10029      */
10030     allowReselect : false,
10031
10032     /**
10033      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10034      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10035      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10036      * of colors with the width setting until the box is symmetrical.</p>
10037      * <p>You can override individual colors if needed:</p>
10038      * <pre><code>
10039 var cp = new Roo.ColorPalette();
10040 cp.colors[0] = "FF0000";  // change the first box to red
10041 </code></pre>
10042
10043 Or you can provide a custom array of your own for complete control:
10044 <pre><code>
10045 var cp = new Roo.ColorPalette();
10046 cp.colors = ["000000", "993300", "333300"];
10047 </code></pre>
10048      * @type Array
10049      */
10050     colors : [
10051         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10052         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10053         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10054         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10055         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10056     ],
10057
10058     // private
10059     onRender : function(container, position){
10060         var t = new Roo.MasterTemplate(
10061             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10062         );
10063         var c = this.colors;
10064         for(var i = 0, len = c.length; i < len; i++){
10065             t.add([c[i]]);
10066         }
10067         var el = document.createElement("div");
10068         el.className = this.itemCls;
10069         t.overwrite(el);
10070         container.dom.insertBefore(el, position);
10071         this.el = Roo.get(el);
10072         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10073         if(this.clickEvent != 'click'){
10074             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10075         }
10076     },
10077
10078     // private
10079     afterRender : function(){
10080         Roo.ColorPalette.superclass.afterRender.call(this);
10081         if(this.value){
10082             var s = this.value;
10083             this.value = null;
10084             this.select(s);
10085         }
10086     },
10087
10088     // private
10089     handleClick : function(e, t){
10090         e.preventDefault();
10091         if(!this.disabled){
10092             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10093             this.select(c.toUpperCase());
10094         }
10095     },
10096
10097     /**
10098      * Selects the specified color in the palette (fires the select event)
10099      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10100      */
10101     select : function(color){
10102         color = color.replace("#", "");
10103         if(color != this.value || this.allowReselect){
10104             var el = this.el;
10105             if(this.value){
10106                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10107             }
10108             el.child("a.color-"+color).addClass("x-color-palette-sel");
10109             this.value = color;
10110             this.fireEvent("select", this, color);
10111         }
10112     }
10113 });/*
10114  * Based on:
10115  * Ext JS Library 1.1.1
10116  * Copyright(c) 2006-2007, Ext JS, LLC.
10117  *
10118  * Originally Released Under LGPL - original licence link has changed is not relivant.
10119  *
10120  * Fork - LGPL
10121  * <script type="text/javascript">
10122  */
10123  
10124 /**
10125  * @class Roo.DatePicker
10126  * @extends Roo.Component
10127  * Simple date picker class.
10128  * @constructor
10129  * Create a new DatePicker
10130  * @param {Object} config The config object
10131  */
10132 Roo.DatePicker = function(config){
10133     Roo.DatePicker.superclass.constructor.call(this, config);
10134
10135     this.value = config && config.value ?
10136                  config.value.clearTime() : new Date().clearTime();
10137
10138     this.addEvents({
10139         /**
10140              * @event select
10141              * Fires when a date is selected
10142              * @param {DatePicker} this
10143              * @param {Date} date The selected date
10144              */
10145         'select': true,
10146         /**
10147              * @event monthchange
10148              * Fires when the displayed month changes 
10149              * @param {DatePicker} this
10150              * @param {Date} date The selected month
10151              */
10152         'monthchange': true
10153     });
10154
10155     if(this.handler){
10156         this.on("select", this.handler,  this.scope || this);
10157     }
10158     // build the disabledDatesRE
10159     if(!this.disabledDatesRE && this.disabledDates){
10160         var dd = this.disabledDates;
10161         var re = "(?:";
10162         for(var i = 0; i < dd.length; i++){
10163             re += dd[i];
10164             if(i != dd.length-1) re += "|";
10165         }
10166         this.disabledDatesRE = new RegExp(re + ")");
10167     }
10168 };
10169
10170 Roo.extend(Roo.DatePicker, Roo.Component, {
10171     /**
10172      * @cfg {String} todayText
10173      * The text to display on the button that selects the current date (defaults to "Today")
10174      */
10175     todayText : "Today",
10176     /**
10177      * @cfg {String} okText
10178      * The text to display on the ok button
10179      */
10180     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10181     /**
10182      * @cfg {String} cancelText
10183      * The text to display on the cancel button
10184      */
10185     cancelText : "Cancel",
10186     /**
10187      * @cfg {String} todayTip
10188      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10189      */
10190     todayTip : "{0} (Spacebar)",
10191     /**
10192      * @cfg {Date} minDate
10193      * Minimum allowable date (JavaScript date object, defaults to null)
10194      */
10195     minDate : null,
10196     /**
10197      * @cfg {Date} maxDate
10198      * Maximum allowable date (JavaScript date object, defaults to null)
10199      */
10200     maxDate : null,
10201     /**
10202      * @cfg {String} minText
10203      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10204      */
10205     minText : "This date is before the minimum date",
10206     /**
10207      * @cfg {String} maxText
10208      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10209      */
10210     maxText : "This date is after the maximum date",
10211     /**
10212      * @cfg {String} format
10213      * The default date format string which can be overriden for localization support.  The format must be
10214      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10215      */
10216     format : "m/d/y",
10217     /**
10218      * @cfg {Array} disabledDays
10219      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10220      */
10221     disabledDays : null,
10222     /**
10223      * @cfg {String} disabledDaysText
10224      * The tooltip to display when the date falls on a disabled day (defaults to "")
10225      */
10226     disabledDaysText : "",
10227     /**
10228      * @cfg {RegExp} disabledDatesRE
10229      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10230      */
10231     disabledDatesRE : null,
10232     /**
10233      * @cfg {String} disabledDatesText
10234      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10235      */
10236     disabledDatesText : "",
10237     /**
10238      * @cfg {Boolean} constrainToViewport
10239      * True to constrain the date picker to the viewport (defaults to true)
10240      */
10241     constrainToViewport : true,
10242     /**
10243      * @cfg {Array} monthNames
10244      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10245      */
10246     monthNames : Date.monthNames,
10247     /**
10248      * @cfg {Array} dayNames
10249      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10250      */
10251     dayNames : Date.dayNames,
10252     /**
10253      * @cfg {String} nextText
10254      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10255      */
10256     nextText: 'Next Month (Control+Right)',
10257     /**
10258      * @cfg {String} prevText
10259      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10260      */
10261     prevText: 'Previous Month (Control+Left)',
10262     /**
10263      * @cfg {String} monthYearText
10264      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10265      */
10266     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10267     /**
10268      * @cfg {Number} startDay
10269      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10270      */
10271     startDay : 0,
10272     /**
10273      * @cfg {Bool} showClear
10274      * Show a clear button (usefull for date form elements that can be blank.)
10275      */
10276     
10277     showClear: false,
10278     
10279     /**
10280      * Sets the value of the date field
10281      * @param {Date} value The date to set
10282      */
10283     setValue : function(value){
10284         var old = this.value;
10285         this.value = value.clearTime(true);
10286         if(this.el){
10287             this.update(this.value);
10288         }
10289     },
10290
10291     /**
10292      * Gets the current selected value of the date field
10293      * @return {Date} The selected date
10294      */
10295     getValue : function(){
10296         return this.value;
10297     },
10298
10299     // private
10300     focus : function(){
10301         if(this.el){
10302             this.update(this.activeDate);
10303         }
10304     },
10305
10306     // private
10307     onRender : function(container, position){
10308         var m = [
10309              '<table cellspacing="0">',
10310                 '<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>',
10311                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10312         var dn = this.dayNames;
10313         for(var i = 0; i < 7; i++){
10314             var d = this.startDay+i;
10315             if(d > 6){
10316                 d = d-7;
10317             }
10318             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10319         }
10320         m[m.length] = "</tr></thead><tbody><tr>";
10321         for(var i = 0; i < 42; i++) {
10322             if(i % 7 == 0 && i != 0){
10323                 m[m.length] = "</tr><tr>";
10324             }
10325             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10326         }
10327         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10328             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10329
10330         var el = document.createElement("div");
10331         el.className = "x-date-picker";
10332         el.innerHTML = m.join("");
10333
10334         container.dom.insertBefore(el, position);
10335
10336         this.el = Roo.get(el);
10337         this.eventEl = Roo.get(el.firstChild);
10338
10339         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10340             handler: this.showPrevMonth,
10341             scope: this,
10342             preventDefault:true,
10343             stopDefault:true
10344         });
10345
10346         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10347             handler: this.showNextMonth,
10348             scope: this,
10349             preventDefault:true,
10350             stopDefault:true
10351         });
10352
10353         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10354
10355         this.monthPicker = this.el.down('div.x-date-mp');
10356         this.monthPicker.enableDisplayMode('block');
10357         
10358         var kn = new Roo.KeyNav(this.eventEl, {
10359             "left" : function(e){
10360                 e.ctrlKey ?
10361                     this.showPrevMonth() :
10362                     this.update(this.activeDate.add("d", -1));
10363             },
10364
10365             "right" : function(e){
10366                 e.ctrlKey ?
10367                     this.showNextMonth() :
10368                     this.update(this.activeDate.add("d", 1));
10369             },
10370
10371             "up" : function(e){
10372                 e.ctrlKey ?
10373                     this.showNextYear() :
10374                     this.update(this.activeDate.add("d", -7));
10375             },
10376
10377             "down" : function(e){
10378                 e.ctrlKey ?
10379                     this.showPrevYear() :
10380                     this.update(this.activeDate.add("d", 7));
10381             },
10382
10383             "pageUp" : function(e){
10384                 this.showNextMonth();
10385             },
10386
10387             "pageDown" : function(e){
10388                 this.showPrevMonth();
10389             },
10390
10391             "enter" : function(e){
10392                 e.stopPropagation();
10393                 return true;
10394             },
10395
10396             scope : this
10397         });
10398
10399         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10400
10401         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10402
10403         this.el.unselectable();
10404         
10405         this.cells = this.el.select("table.x-date-inner tbody td");
10406         this.textNodes = this.el.query("table.x-date-inner tbody span");
10407
10408         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10409             text: "&#160;",
10410             tooltip: this.monthYearText
10411         });
10412
10413         this.mbtn.on('click', this.showMonthPicker, this);
10414         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10415
10416
10417         var today = (new Date()).dateFormat(this.format);
10418         
10419         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10420         if (this.showClear) {
10421             baseTb.add( new Roo.Toolbar.Fill());
10422         }
10423         baseTb.add({
10424             text: String.format(this.todayText, today),
10425             tooltip: String.format(this.todayTip, today),
10426             handler: this.selectToday,
10427             scope: this
10428         });
10429         
10430         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10431             
10432         //});
10433         if (this.showClear) {
10434             
10435             baseTb.add( new Roo.Toolbar.Fill());
10436             baseTb.add({
10437                 text: '&#160;',
10438                 cls: 'x-btn-icon x-btn-clear',
10439                 handler: function() {
10440                     //this.value = '';
10441                     this.fireEvent("select", this, '');
10442                 },
10443                 scope: this
10444             });
10445         }
10446         
10447         
10448         if(Roo.isIE){
10449             this.el.repaint();
10450         }
10451         this.update(this.value);
10452     },
10453
10454     createMonthPicker : function(){
10455         if(!this.monthPicker.dom.firstChild){
10456             var buf = ['<table border="0" cellspacing="0">'];
10457             for(var i = 0; i < 6; i++){
10458                 buf.push(
10459                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10460                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10461                     i == 0 ?
10462                     '<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>' :
10463                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10464                 );
10465             }
10466             buf.push(
10467                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10468                     this.okText,
10469                     '</button><button type="button" class="x-date-mp-cancel">',
10470                     this.cancelText,
10471                     '</button></td></tr>',
10472                 '</table>'
10473             );
10474             this.monthPicker.update(buf.join(''));
10475             this.monthPicker.on('click', this.onMonthClick, this);
10476             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10477
10478             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10479             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10480
10481             this.mpMonths.each(function(m, a, i){
10482                 i += 1;
10483                 if((i%2) == 0){
10484                     m.dom.xmonth = 5 + Math.round(i * .5);
10485                 }else{
10486                     m.dom.xmonth = Math.round((i-1) * .5);
10487                 }
10488             });
10489         }
10490     },
10491
10492     showMonthPicker : function(){
10493         this.createMonthPicker();
10494         var size = this.el.getSize();
10495         this.monthPicker.setSize(size);
10496         this.monthPicker.child('table').setSize(size);
10497
10498         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10499         this.updateMPMonth(this.mpSelMonth);
10500         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10501         this.updateMPYear(this.mpSelYear);
10502
10503         this.monthPicker.slideIn('t', {duration:.2});
10504     },
10505
10506     updateMPYear : function(y){
10507         this.mpyear = y;
10508         var ys = this.mpYears.elements;
10509         for(var i = 1; i <= 10; i++){
10510             var td = ys[i-1], y2;
10511             if((i%2) == 0){
10512                 y2 = y + Math.round(i * .5);
10513                 td.firstChild.innerHTML = y2;
10514                 td.xyear = y2;
10515             }else{
10516                 y2 = y - (5-Math.round(i * .5));
10517                 td.firstChild.innerHTML = y2;
10518                 td.xyear = y2;
10519             }
10520             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10521         }
10522     },
10523
10524     updateMPMonth : function(sm){
10525         this.mpMonths.each(function(m, a, i){
10526             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10527         });
10528     },
10529
10530     selectMPMonth: function(m){
10531         
10532     },
10533
10534     onMonthClick : function(e, t){
10535         e.stopEvent();
10536         var el = new Roo.Element(t), pn;
10537         if(el.is('button.x-date-mp-cancel')){
10538             this.hideMonthPicker();
10539         }
10540         else if(el.is('button.x-date-mp-ok')){
10541             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10542             this.hideMonthPicker();
10543         }
10544         else if(pn = el.up('td.x-date-mp-month', 2)){
10545             this.mpMonths.removeClass('x-date-mp-sel');
10546             pn.addClass('x-date-mp-sel');
10547             this.mpSelMonth = pn.dom.xmonth;
10548         }
10549         else if(pn = el.up('td.x-date-mp-year', 2)){
10550             this.mpYears.removeClass('x-date-mp-sel');
10551             pn.addClass('x-date-mp-sel');
10552             this.mpSelYear = pn.dom.xyear;
10553         }
10554         else if(el.is('a.x-date-mp-prev')){
10555             this.updateMPYear(this.mpyear-10);
10556         }
10557         else if(el.is('a.x-date-mp-next')){
10558             this.updateMPYear(this.mpyear+10);
10559         }
10560     },
10561
10562     onMonthDblClick : function(e, t){
10563         e.stopEvent();
10564         var el = new Roo.Element(t), pn;
10565         if(pn = el.up('td.x-date-mp-month', 2)){
10566             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10567             this.hideMonthPicker();
10568         }
10569         else if(pn = el.up('td.x-date-mp-year', 2)){
10570             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10571             this.hideMonthPicker();
10572         }
10573     },
10574
10575     hideMonthPicker : function(disableAnim){
10576         if(this.monthPicker){
10577             if(disableAnim === true){
10578                 this.monthPicker.hide();
10579             }else{
10580                 this.monthPicker.slideOut('t', {duration:.2});
10581             }
10582         }
10583     },
10584
10585     // private
10586     showPrevMonth : function(e){
10587         this.update(this.activeDate.add("mo", -1));
10588     },
10589
10590     // private
10591     showNextMonth : function(e){
10592         this.update(this.activeDate.add("mo", 1));
10593     },
10594
10595     // private
10596     showPrevYear : function(){
10597         this.update(this.activeDate.add("y", -1));
10598     },
10599
10600     // private
10601     showNextYear : function(){
10602         this.update(this.activeDate.add("y", 1));
10603     },
10604
10605     // private
10606     handleMouseWheel : function(e){
10607         var delta = e.getWheelDelta();
10608         if(delta > 0){
10609             this.showPrevMonth();
10610             e.stopEvent();
10611         } else if(delta < 0){
10612             this.showNextMonth();
10613             e.stopEvent();
10614         }
10615     },
10616
10617     // private
10618     handleDateClick : function(e, t){
10619         e.stopEvent();
10620         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10621             this.setValue(new Date(t.dateValue));
10622             this.fireEvent("select", this, this.value);
10623         }
10624     },
10625
10626     // private
10627     selectToday : function(){
10628         this.setValue(new Date().clearTime());
10629         this.fireEvent("select", this, this.value);
10630     },
10631
10632     // private
10633     update : function(date)
10634     {
10635         var vd = this.activeDate;
10636         this.activeDate = date;
10637         if(vd && this.el){
10638             var t = date.getTime();
10639             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10640                 this.cells.removeClass("x-date-selected");
10641                 this.cells.each(function(c){
10642                    if(c.dom.firstChild.dateValue == t){
10643                        c.addClass("x-date-selected");
10644                        setTimeout(function(){
10645                             try{c.dom.firstChild.focus();}catch(e){}
10646                        }, 50);
10647                        return false;
10648                    }
10649                 });
10650                 return;
10651             }
10652         }
10653         
10654         var days = date.getDaysInMonth();
10655         var firstOfMonth = date.getFirstDateOfMonth();
10656         var startingPos = firstOfMonth.getDay()-this.startDay;
10657
10658         if(startingPos <= this.startDay){
10659             startingPos += 7;
10660         }
10661
10662         var pm = date.add("mo", -1);
10663         var prevStart = pm.getDaysInMonth()-startingPos;
10664
10665         var cells = this.cells.elements;
10666         var textEls = this.textNodes;
10667         days += startingPos;
10668
10669         // convert everything to numbers so it's fast
10670         var day = 86400000;
10671         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10672         var today = new Date().clearTime().getTime();
10673         var sel = date.clearTime().getTime();
10674         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10675         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10676         var ddMatch = this.disabledDatesRE;
10677         var ddText = this.disabledDatesText;
10678         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10679         var ddaysText = this.disabledDaysText;
10680         var format = this.format;
10681
10682         var setCellClass = function(cal, cell){
10683             cell.title = "";
10684             var t = d.getTime();
10685             cell.firstChild.dateValue = t;
10686             if(t == today){
10687                 cell.className += " x-date-today";
10688                 cell.title = cal.todayText;
10689             }
10690             if(t == sel){
10691                 cell.className += " x-date-selected";
10692                 setTimeout(function(){
10693                     try{cell.firstChild.focus();}catch(e){}
10694                 }, 50);
10695             }
10696             // disabling
10697             if(t < min) {
10698                 cell.className = " x-date-disabled";
10699                 cell.title = cal.minText;
10700                 return;
10701             }
10702             if(t > max) {
10703                 cell.className = " x-date-disabled";
10704                 cell.title = cal.maxText;
10705                 return;
10706             }
10707             if(ddays){
10708                 if(ddays.indexOf(d.getDay()) != -1){
10709                     cell.title = ddaysText;
10710                     cell.className = " x-date-disabled";
10711                 }
10712             }
10713             if(ddMatch && format){
10714                 var fvalue = d.dateFormat(format);
10715                 if(ddMatch.test(fvalue)){
10716                     cell.title = ddText.replace("%0", fvalue);
10717                     cell.className = " x-date-disabled";
10718                 }
10719             }
10720         };
10721
10722         var i = 0;
10723         for(; i < startingPos; i++) {
10724             textEls[i].innerHTML = (++prevStart);
10725             d.setDate(d.getDate()+1);
10726             cells[i].className = "x-date-prevday";
10727             setCellClass(this, cells[i]);
10728         }
10729         for(; i < days; i++){
10730             intDay = i - startingPos + 1;
10731             textEls[i].innerHTML = (intDay);
10732             d.setDate(d.getDate()+1);
10733             cells[i].className = "x-date-active";
10734             setCellClass(this, cells[i]);
10735         }
10736         var extraDays = 0;
10737         for(; i < 42; i++) {
10738              textEls[i].innerHTML = (++extraDays);
10739              d.setDate(d.getDate()+1);
10740              cells[i].className = "x-date-nextday";
10741              setCellClass(this, cells[i]);
10742         }
10743
10744         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10745         this.fireEvent('monthchange', this, date);
10746         
10747         if(!this.internalRender){
10748             var main = this.el.dom.firstChild;
10749             var w = main.offsetWidth;
10750             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10751             Roo.fly(main).setWidth(w);
10752             this.internalRender = true;
10753             // opera does not respect the auto grow header center column
10754             // then, after it gets a width opera refuses to recalculate
10755             // without a second pass
10756             if(Roo.isOpera && !this.secondPass){
10757                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10758                 this.secondPass = true;
10759                 this.update.defer(10, this, [date]);
10760             }
10761         }
10762         
10763         
10764     }
10765 });        /*
10766  * Based on:
10767  * Ext JS Library 1.1.1
10768  * Copyright(c) 2006-2007, Ext JS, LLC.
10769  *
10770  * Originally Released Under LGPL - original licence link has changed is not relivant.
10771  *
10772  * Fork - LGPL
10773  * <script type="text/javascript">
10774  */
10775 /**
10776  * @class Roo.TabPanel
10777  * @extends Roo.util.Observable
10778  * A lightweight tab container.
10779  * <br><br>
10780  * Usage:
10781  * <pre><code>
10782 // basic tabs 1, built from existing content
10783 var tabs = new Roo.TabPanel("tabs1");
10784 tabs.addTab("script", "View Script");
10785 tabs.addTab("markup", "View Markup");
10786 tabs.activate("script");
10787
10788 // more advanced tabs, built from javascript
10789 var jtabs = new Roo.TabPanel("jtabs");
10790 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10791
10792 // set up the UpdateManager
10793 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10794 var updater = tab2.getUpdateManager();
10795 updater.setDefaultUrl("ajax1.htm");
10796 tab2.on('activate', updater.refresh, updater, true);
10797
10798 // Use setUrl for Ajax loading
10799 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10800 tab3.setUrl("ajax2.htm", null, true);
10801
10802 // Disabled tab
10803 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10804 tab4.disable();
10805
10806 jtabs.activate("jtabs-1");
10807  * </code></pre>
10808  * @constructor
10809  * Create a new TabPanel.
10810  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10811  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10812  */
10813 Roo.TabPanel = function(container, config){
10814     /**
10815     * The container element for this TabPanel.
10816     * @type Roo.Element
10817     */
10818     this.el = Roo.get(container, true);
10819     if(config){
10820         if(typeof config == "boolean"){
10821             this.tabPosition = config ? "bottom" : "top";
10822         }else{
10823             Roo.apply(this, config);
10824         }
10825     }
10826     if(this.tabPosition == "bottom"){
10827         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10828         this.el.addClass("x-tabs-bottom");
10829     }
10830     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10831     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10832     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10833     if(Roo.isIE){
10834         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10835     }
10836     if(this.tabPosition != "bottom"){
10837         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10838          * @type Roo.Element
10839          */
10840         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10841         this.el.addClass("x-tabs-top");
10842     }
10843     this.items = [];
10844
10845     this.bodyEl.setStyle("position", "relative");
10846
10847     this.active = null;
10848     this.activateDelegate = this.activate.createDelegate(this);
10849
10850     this.addEvents({
10851         /**
10852          * @event tabchange
10853          * Fires when the active tab changes
10854          * @param {Roo.TabPanel} this
10855          * @param {Roo.TabPanelItem} activePanel The new active tab
10856          */
10857         "tabchange": true,
10858         /**
10859          * @event beforetabchange
10860          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10861          * @param {Roo.TabPanel} this
10862          * @param {Object} e Set cancel to true on this object to cancel the tab change
10863          * @param {Roo.TabPanelItem} tab The tab being changed to
10864          */
10865         "beforetabchange" : true
10866     });
10867
10868     Roo.EventManager.onWindowResize(this.onResize, this);
10869     this.cpad = this.el.getPadding("lr");
10870     this.hiddenCount = 0;
10871
10872
10873     // toolbar on the tabbar support...
10874     if (this.toolbar) {
10875         var tcfg = this.toolbar;
10876         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10877         this.toolbar = new Roo.Toolbar(tcfg);
10878         if (Roo.isSafari) {
10879             var tbl = tcfg.container.child('table', true);
10880             tbl.setAttribute('width', '100%');
10881         }
10882         
10883     }
10884    
10885
10886
10887     Roo.TabPanel.superclass.constructor.call(this);
10888 };
10889
10890 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10891     /*
10892      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10893      */
10894     tabPosition : "top",
10895     /*
10896      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10897      */
10898     currentTabWidth : 0,
10899     /*
10900      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10901      */
10902     minTabWidth : 40,
10903     /*
10904      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10905      */
10906     maxTabWidth : 250,
10907     /*
10908      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10909      */
10910     preferredTabWidth : 175,
10911     /*
10912      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10913      */
10914     resizeTabs : false,
10915     /*
10916      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10917      */
10918     monitorResize : true,
10919     /*
10920      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10921      */
10922     toolbar : false,
10923
10924     /**
10925      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10926      * @param {String} id The id of the div to use <b>or create</b>
10927      * @param {String} text The text for the tab
10928      * @param {String} content (optional) Content to put in the TabPanelItem body
10929      * @param {Boolean} closable (optional) True to create a close icon on the tab
10930      * @return {Roo.TabPanelItem} The created TabPanelItem
10931      */
10932     addTab : function(id, text, content, closable){
10933         var item = new Roo.TabPanelItem(this, id, text, closable);
10934         this.addTabItem(item);
10935         if(content){
10936             item.setContent(content);
10937         }
10938         return item;
10939     },
10940
10941     /**
10942      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10943      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10944      * @return {Roo.TabPanelItem}
10945      */
10946     getTab : function(id){
10947         return this.items[id];
10948     },
10949
10950     /**
10951      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10952      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10953      */
10954     hideTab : function(id){
10955         var t = this.items[id];
10956         if(!t.isHidden()){
10957            t.setHidden(true);
10958            this.hiddenCount++;
10959            this.autoSizeTabs();
10960         }
10961     },
10962
10963     /**
10964      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10965      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10966      */
10967     unhideTab : function(id){
10968         var t = this.items[id];
10969         if(t.isHidden()){
10970            t.setHidden(false);
10971            this.hiddenCount--;
10972            this.autoSizeTabs();
10973         }
10974     },
10975
10976     /**
10977      * Adds an existing {@link Roo.TabPanelItem}.
10978      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10979      */
10980     addTabItem : function(item){
10981         this.items[item.id] = item;
10982         this.items.push(item);
10983         if(this.resizeTabs){
10984            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10985            this.autoSizeTabs();
10986         }else{
10987             item.autoSize();
10988         }
10989     },
10990
10991     /**
10992      * Removes a {@link Roo.TabPanelItem}.
10993      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10994      */
10995     removeTab : function(id){
10996         var items = this.items;
10997         var tab = items[id];
10998         if(!tab) { return; }
10999         var index = items.indexOf(tab);
11000         if(this.active == tab && items.length > 1){
11001             var newTab = this.getNextAvailable(index);
11002             if(newTab) {
11003                 newTab.activate();
11004             }
11005         }
11006         this.stripEl.dom.removeChild(tab.pnode.dom);
11007         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11008             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11009         }
11010         items.splice(index, 1);
11011         delete this.items[tab.id];
11012         tab.fireEvent("close", tab);
11013         tab.purgeListeners();
11014         this.autoSizeTabs();
11015     },
11016
11017     getNextAvailable : function(start){
11018         var items = this.items;
11019         var index = start;
11020         // look for a next tab that will slide over to
11021         // replace the one being removed
11022         while(index < items.length){
11023             var item = items[++index];
11024             if(item && !item.isHidden()){
11025                 return item;
11026             }
11027         }
11028         // if one isn't found select the previous tab (on the left)
11029         index = start;
11030         while(index >= 0){
11031             var item = items[--index];
11032             if(item && !item.isHidden()){
11033                 return item;
11034             }
11035         }
11036         return null;
11037     },
11038
11039     /**
11040      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11041      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11042      */
11043     disableTab : function(id){
11044         var tab = this.items[id];
11045         if(tab && this.active != tab){
11046             tab.disable();
11047         }
11048     },
11049
11050     /**
11051      * Enables a {@link Roo.TabPanelItem} that is disabled.
11052      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11053      */
11054     enableTab : function(id){
11055         var tab = this.items[id];
11056         tab.enable();
11057     },
11058
11059     /**
11060      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11061      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11062      * @return {Roo.TabPanelItem} The TabPanelItem.
11063      */
11064     activate : function(id){
11065         var tab = this.items[id];
11066         if(!tab){
11067             return null;
11068         }
11069         if(tab == this.active || tab.disabled){
11070             return tab;
11071         }
11072         var e = {};
11073         this.fireEvent("beforetabchange", this, e, tab);
11074         if(e.cancel !== true && !tab.disabled){
11075             if(this.active){
11076                 this.active.hide();
11077             }
11078             this.active = this.items[id];
11079             this.active.show();
11080             this.fireEvent("tabchange", this, this.active);
11081         }
11082         return tab;
11083     },
11084
11085     /**
11086      * Gets the active {@link Roo.TabPanelItem}.
11087      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11088      */
11089     getActiveTab : function(){
11090         return this.active;
11091     },
11092
11093     /**
11094      * Updates the tab body element to fit the height of the container element
11095      * for overflow scrolling
11096      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11097      */
11098     syncHeight : function(targetHeight){
11099         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11100         var bm = this.bodyEl.getMargins();
11101         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11102         this.bodyEl.setHeight(newHeight);
11103         return newHeight;
11104     },
11105
11106     onResize : function(){
11107         if(this.monitorResize){
11108             this.autoSizeTabs();
11109         }
11110     },
11111
11112     /**
11113      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11114      */
11115     beginUpdate : function(){
11116         this.updating = true;
11117     },
11118
11119     /**
11120      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11121      */
11122     endUpdate : function(){
11123         this.updating = false;
11124         this.autoSizeTabs();
11125     },
11126
11127     /**
11128      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11129      */
11130     autoSizeTabs : function(){
11131         var count = this.items.length;
11132         var vcount = count - this.hiddenCount;
11133         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11134         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11135         var availWidth = Math.floor(w / vcount);
11136         var b = this.stripBody;
11137         if(b.getWidth() > w){
11138             var tabs = this.items;
11139             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11140             if(availWidth < this.minTabWidth){
11141                 /*if(!this.sleft){    // incomplete scrolling code
11142                     this.createScrollButtons();
11143                 }
11144                 this.showScroll();
11145                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11146             }
11147         }else{
11148             if(this.currentTabWidth < this.preferredTabWidth){
11149                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11150             }
11151         }
11152     },
11153
11154     /**
11155      * Returns the number of tabs in this TabPanel.
11156      * @return {Number}
11157      */
11158      getCount : function(){
11159          return this.items.length;
11160      },
11161
11162     /**
11163      * Resizes all the tabs to the passed width
11164      * @param {Number} The new width
11165      */
11166     setTabWidth : function(width){
11167         this.currentTabWidth = width;
11168         for(var i = 0, len = this.items.length; i < len; i++) {
11169                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11170         }
11171     },
11172
11173     /**
11174      * Destroys this TabPanel
11175      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11176      */
11177     destroy : function(removeEl){
11178         Roo.EventManager.removeResizeListener(this.onResize, this);
11179         for(var i = 0, len = this.items.length; i < len; i++){
11180             this.items[i].purgeListeners();
11181         }
11182         if(removeEl === true){
11183             this.el.update("");
11184             this.el.remove();
11185         }
11186     }
11187 });
11188
11189 /**
11190  * @class Roo.TabPanelItem
11191  * @extends Roo.util.Observable
11192  * Represents an individual item (tab plus body) in a TabPanel.
11193  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11194  * @param {String} id The id of this TabPanelItem
11195  * @param {String} text The text for the tab of this TabPanelItem
11196  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11197  */
11198 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11199     /**
11200      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11201      * @type Roo.TabPanel
11202      */
11203     this.tabPanel = tabPanel;
11204     /**
11205      * The id for this TabPanelItem
11206      * @type String
11207      */
11208     this.id = id;
11209     /** @private */
11210     this.disabled = false;
11211     /** @private */
11212     this.text = text;
11213     /** @private */
11214     this.loaded = false;
11215     this.closable = closable;
11216
11217     /**
11218      * The body element for this TabPanelItem.
11219      * @type Roo.Element
11220      */
11221     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11222     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11223     this.bodyEl.setStyle("display", "block");
11224     this.bodyEl.setStyle("zoom", "1");
11225     this.hideAction();
11226
11227     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11228     /** @private */
11229     this.el = Roo.get(els.el, true);
11230     this.inner = Roo.get(els.inner, true);
11231     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11232     this.pnode = Roo.get(els.el.parentNode, true);
11233     this.el.on("mousedown", this.onTabMouseDown, this);
11234     this.el.on("click", this.onTabClick, this);
11235     /** @private */
11236     if(closable){
11237         var c = Roo.get(els.close, true);
11238         c.dom.title = this.closeText;
11239         c.addClassOnOver("close-over");
11240         c.on("click", this.closeClick, this);
11241      }
11242
11243     this.addEvents({
11244          /**
11245          * @event activate
11246          * Fires when this tab becomes the active tab.
11247          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11248          * @param {Roo.TabPanelItem} this
11249          */
11250         "activate": true,
11251         /**
11252          * @event beforeclose
11253          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11254          * @param {Roo.TabPanelItem} this
11255          * @param {Object} e Set cancel to true on this object to cancel the close.
11256          */
11257         "beforeclose": true,
11258         /**
11259          * @event close
11260          * Fires when this tab is closed.
11261          * @param {Roo.TabPanelItem} this
11262          */
11263          "close": true,
11264         /**
11265          * @event deactivate
11266          * Fires when this tab is no longer the active tab.
11267          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11268          * @param {Roo.TabPanelItem} this
11269          */
11270          "deactivate" : true
11271     });
11272     this.hidden = false;
11273
11274     Roo.TabPanelItem.superclass.constructor.call(this);
11275 };
11276
11277 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11278     purgeListeners : function(){
11279        Roo.util.Observable.prototype.purgeListeners.call(this);
11280        this.el.removeAllListeners();
11281     },
11282     /**
11283      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11284      */
11285     show : function(){
11286         this.pnode.addClass("on");
11287         this.showAction();
11288         if(Roo.isOpera){
11289             this.tabPanel.stripWrap.repaint();
11290         }
11291         this.fireEvent("activate", this.tabPanel, this);
11292     },
11293
11294     /**
11295      * Returns true if this tab is the active tab.
11296      * @return {Boolean}
11297      */
11298     isActive : function(){
11299         return this.tabPanel.getActiveTab() == this;
11300     },
11301
11302     /**
11303      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11304      */
11305     hide : function(){
11306         this.pnode.removeClass("on");
11307         this.hideAction();
11308         this.fireEvent("deactivate", this.tabPanel, this);
11309     },
11310
11311     hideAction : function(){
11312         this.bodyEl.hide();
11313         this.bodyEl.setStyle("position", "absolute");
11314         this.bodyEl.setLeft("-20000px");
11315         this.bodyEl.setTop("-20000px");
11316     },
11317
11318     showAction : function(){
11319         this.bodyEl.setStyle("position", "relative");
11320         this.bodyEl.setTop("");
11321         this.bodyEl.setLeft("");
11322         this.bodyEl.show();
11323     },
11324
11325     /**
11326      * Set the tooltip for the tab.
11327      * @param {String} tooltip The tab's tooltip
11328      */
11329     setTooltip : function(text){
11330         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11331             this.textEl.dom.qtip = text;
11332             this.textEl.dom.removeAttribute('title');
11333         }else{
11334             this.textEl.dom.title = text;
11335         }
11336     },
11337
11338     onTabClick : function(e){
11339         e.preventDefault();
11340         this.tabPanel.activate(this.id);
11341     },
11342
11343     onTabMouseDown : function(e){
11344         e.preventDefault();
11345         this.tabPanel.activate(this.id);
11346     },
11347
11348     getWidth : function(){
11349         return this.inner.getWidth();
11350     },
11351
11352     setWidth : function(width){
11353         var iwidth = width - this.pnode.getPadding("lr");
11354         this.inner.setWidth(iwidth);
11355         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11356         this.pnode.setWidth(width);
11357     },
11358
11359     /**
11360      * Show or hide the tab
11361      * @param {Boolean} hidden True to hide or false to show.
11362      */
11363     setHidden : function(hidden){
11364         this.hidden = hidden;
11365         this.pnode.setStyle("display", hidden ? "none" : "");
11366     },
11367
11368     /**
11369      * Returns true if this tab is "hidden"
11370      * @return {Boolean}
11371      */
11372     isHidden : function(){
11373         return this.hidden;
11374     },
11375
11376     /**
11377      * Returns the text for this tab
11378      * @return {String}
11379      */
11380     getText : function(){
11381         return this.text;
11382     },
11383
11384     autoSize : function(){
11385         //this.el.beginMeasure();
11386         this.textEl.setWidth(1);
11387         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11388         //this.el.endMeasure();
11389     },
11390
11391     /**
11392      * Sets the text for the tab (Note: this also sets the tooltip text)
11393      * @param {String} text The tab's text and tooltip
11394      */
11395     setText : function(text){
11396         this.text = text;
11397         this.textEl.update(text);
11398         this.setTooltip(text);
11399         if(!this.tabPanel.resizeTabs){
11400             this.autoSize();
11401         }
11402     },
11403     /**
11404      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11405      */
11406     activate : function(){
11407         this.tabPanel.activate(this.id);
11408     },
11409
11410     /**
11411      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11412      */
11413     disable : function(){
11414         if(this.tabPanel.active != this){
11415             this.disabled = true;
11416             this.pnode.addClass("disabled");
11417         }
11418     },
11419
11420     /**
11421      * Enables this TabPanelItem if it was previously disabled.
11422      */
11423     enable : function(){
11424         this.disabled = false;
11425         this.pnode.removeClass("disabled");
11426     },
11427
11428     /**
11429      * Sets the content for this TabPanelItem.
11430      * @param {String} content The content
11431      * @param {Boolean} loadScripts true to look for and load scripts
11432      */
11433     setContent : function(content, loadScripts){
11434         this.bodyEl.update(content, loadScripts);
11435     },
11436
11437     /**
11438      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11439      * @return {Roo.UpdateManager} The UpdateManager
11440      */
11441     getUpdateManager : function(){
11442         return this.bodyEl.getUpdateManager();
11443     },
11444
11445     /**
11446      * Set a URL to be used to load the content for this TabPanelItem.
11447      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11448      * @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)
11449      * @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)
11450      * @return {Roo.UpdateManager} The UpdateManager
11451      */
11452     setUrl : function(url, params, loadOnce){
11453         if(this.refreshDelegate){
11454             this.un('activate', this.refreshDelegate);
11455         }
11456         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11457         this.on("activate", this.refreshDelegate);
11458         return this.bodyEl.getUpdateManager();
11459     },
11460
11461     /** @private */
11462     _handleRefresh : function(url, params, loadOnce){
11463         if(!loadOnce || !this.loaded){
11464             var updater = this.bodyEl.getUpdateManager();
11465             updater.update(url, params, this._setLoaded.createDelegate(this));
11466         }
11467     },
11468
11469     /**
11470      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11471      *   Will fail silently if the setUrl method has not been called.
11472      *   This does not activate the panel, just updates its content.
11473      */
11474     refresh : function(){
11475         if(this.refreshDelegate){
11476            this.loaded = false;
11477            this.refreshDelegate();
11478         }
11479     },
11480
11481     /** @private */
11482     _setLoaded : function(){
11483         this.loaded = true;
11484     },
11485
11486     /** @private */
11487     closeClick : function(e){
11488         var o = {};
11489         e.stopEvent();
11490         this.fireEvent("beforeclose", this, o);
11491         if(o.cancel !== true){
11492             this.tabPanel.removeTab(this.id);
11493         }
11494     },
11495     /**
11496      * The text displayed in the tooltip for the close icon.
11497      * @type String
11498      */
11499     closeText : "Close this tab"
11500 });
11501
11502 /** @private */
11503 Roo.TabPanel.prototype.createStrip = function(container){
11504     var strip = document.createElement("div");
11505     strip.className = "x-tabs-wrap";
11506     container.appendChild(strip);
11507     return strip;
11508 };
11509 /** @private */
11510 Roo.TabPanel.prototype.createStripList = function(strip){
11511     // div wrapper for retard IE
11512     // returns the "tr" element.
11513     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11514         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11515         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11516     return strip.firstChild.firstChild.firstChild.firstChild;
11517 };
11518 /** @private */
11519 Roo.TabPanel.prototype.createBody = function(container){
11520     var body = document.createElement("div");
11521     Roo.id(body, "tab-body");
11522     Roo.fly(body).addClass("x-tabs-body");
11523     container.appendChild(body);
11524     return body;
11525 };
11526 /** @private */
11527 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11528     var body = Roo.getDom(id);
11529     if(!body){
11530         body = document.createElement("div");
11531         body.id = id;
11532     }
11533     Roo.fly(body).addClass("x-tabs-item-body");
11534     bodyEl.insertBefore(body, bodyEl.firstChild);
11535     return body;
11536 };
11537 /** @private */
11538 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11539     var td = document.createElement("td");
11540     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11541     //stripEl.appendChild(td);
11542     if(closable){
11543         td.className = "x-tabs-closable";
11544         if(!this.closeTpl){
11545             this.closeTpl = new Roo.Template(
11546                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11547                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11548                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11549             );
11550         }
11551         var el = this.closeTpl.overwrite(td, {"text": text});
11552         var close = el.getElementsByTagName("div")[0];
11553         var inner = el.getElementsByTagName("em")[0];
11554         return {"el": el, "close": close, "inner": inner};
11555     } else {
11556         if(!this.tabTpl){
11557             this.tabTpl = new Roo.Template(
11558                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11559                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11560             );
11561         }
11562         var el = this.tabTpl.overwrite(td, {"text": text});
11563         var inner = el.getElementsByTagName("em")[0];
11564         return {"el": el, "inner": inner};
11565     }
11566 };/*
11567  * Based on:
11568  * Ext JS Library 1.1.1
11569  * Copyright(c) 2006-2007, Ext JS, LLC.
11570  *
11571  * Originally Released Under LGPL - original licence link has changed is not relivant.
11572  *
11573  * Fork - LGPL
11574  * <script type="text/javascript">
11575  */
11576
11577 /**
11578  * @class Roo.Button
11579  * @extends Roo.util.Observable
11580  * Simple Button class
11581  * @cfg {String} text The button text
11582  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11583  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11584  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11585  * @cfg {Object} scope The scope of the handler
11586  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11587  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11588  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11589  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11590  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11591  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11592    applies if enableToggle = true)
11593  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11594  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11595   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11596  * @constructor
11597  * Create a new button
11598  * @param {Object} config The config object
11599  */
11600 Roo.Button = function(renderTo, config)
11601 {
11602     if (!config) {
11603         config = renderTo;
11604         renderTo = config.renderTo || false;
11605     }
11606     
11607     Roo.apply(this, config);
11608     this.addEvents({
11609         /**
11610              * @event click
11611              * Fires when this button is clicked
11612              * @param {Button} this
11613              * @param {EventObject} e The click event
11614              */
11615             "click" : true,
11616         /**
11617              * @event toggle
11618              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11619              * @param {Button} this
11620              * @param {Boolean} pressed
11621              */
11622             "toggle" : true,
11623         /**
11624              * @event mouseover
11625              * Fires when the mouse hovers over the button
11626              * @param {Button} this
11627              * @param {Event} e The event object
11628              */
11629         'mouseover' : true,
11630         /**
11631              * @event mouseout
11632              * Fires when the mouse exits the button
11633              * @param {Button} this
11634              * @param {Event} e The event object
11635              */
11636         'mouseout': true,
11637          /**
11638              * @event render
11639              * Fires when the button is rendered
11640              * @param {Button} this
11641              */
11642         'render': true
11643     });
11644     if(this.menu){
11645         this.menu = Roo.menu.MenuMgr.get(this.menu);
11646     }
11647     // register listeners first!!  - so render can be captured..
11648     Roo.util.Observable.call(this);
11649     if(renderTo){
11650         this.render(renderTo);
11651     }
11652     
11653   
11654 };
11655
11656 Roo.extend(Roo.Button, Roo.util.Observable, {
11657     /**
11658      * 
11659      */
11660     
11661     /**
11662      * Read-only. True if this button is hidden
11663      * @type Boolean
11664      */
11665     hidden : false,
11666     /**
11667      * Read-only. True if this button is disabled
11668      * @type Boolean
11669      */
11670     disabled : false,
11671     /**
11672      * Read-only. True if this button is pressed (only if enableToggle = true)
11673      * @type Boolean
11674      */
11675     pressed : false,
11676
11677     /**
11678      * @cfg {Number} tabIndex 
11679      * The DOM tabIndex for this button (defaults to undefined)
11680      */
11681     tabIndex : undefined,
11682
11683     /**
11684      * @cfg {Boolean} enableToggle
11685      * True to enable pressed/not pressed toggling (defaults to false)
11686      */
11687     enableToggle: false,
11688     /**
11689      * @cfg {Mixed} menu
11690      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11691      */
11692     menu : undefined,
11693     /**
11694      * @cfg {String} menuAlign
11695      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11696      */
11697     menuAlign : "tl-bl?",
11698
11699     /**
11700      * @cfg {String} iconCls
11701      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11702      */
11703     iconCls : undefined,
11704     /**
11705      * @cfg {String} type
11706      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11707      */
11708     type : 'button',
11709
11710     // private
11711     menuClassTarget: 'tr',
11712
11713     /**
11714      * @cfg {String} clickEvent
11715      * The type of event to map to the button's event handler (defaults to 'click')
11716      */
11717     clickEvent : 'click',
11718
11719     /**
11720      * @cfg {Boolean} handleMouseEvents
11721      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11722      */
11723     handleMouseEvents : true,
11724
11725     /**
11726      * @cfg {String} tooltipType
11727      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11728      */
11729     tooltipType : 'qtip',
11730
11731     /**
11732      * @cfg {String} cls
11733      * A CSS class to apply to the button's main element.
11734      */
11735     
11736     /**
11737      * @cfg {Roo.Template} template (Optional)
11738      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11739      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11740      * require code modifications if required elements (e.g. a button) aren't present.
11741      */
11742
11743     // private
11744     render : function(renderTo){
11745         var btn;
11746         if(this.hideParent){
11747             this.parentEl = Roo.get(renderTo);
11748         }
11749         if(!this.dhconfig){
11750             if(!this.template){
11751                 if(!Roo.Button.buttonTemplate){
11752                     // hideous table template
11753                     Roo.Button.buttonTemplate = new Roo.Template(
11754                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11755                         '<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>',
11756                         "</tr></tbody></table>");
11757                 }
11758                 this.template = Roo.Button.buttonTemplate;
11759             }
11760             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11761             var btnEl = btn.child("button:first");
11762             btnEl.on('focus', this.onFocus, this);
11763             btnEl.on('blur', this.onBlur, this);
11764             if(this.cls){
11765                 btn.addClass(this.cls);
11766             }
11767             if(this.icon){
11768                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11769             }
11770             if(this.iconCls){
11771                 btnEl.addClass(this.iconCls);
11772                 if(!this.cls){
11773                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11774                 }
11775             }
11776             if(this.tabIndex !== undefined){
11777                 btnEl.dom.tabIndex = this.tabIndex;
11778             }
11779             if(this.tooltip){
11780                 if(typeof this.tooltip == 'object'){
11781                     Roo.QuickTips.tips(Roo.apply({
11782                           target: btnEl.id
11783                     }, this.tooltip));
11784                 } else {
11785                     btnEl.dom[this.tooltipType] = this.tooltip;
11786                 }
11787             }
11788         }else{
11789             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11790         }
11791         this.el = btn;
11792         if(this.id){
11793             this.el.dom.id = this.el.id = this.id;
11794         }
11795         if(this.menu){
11796             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11797             this.menu.on("show", this.onMenuShow, this);
11798             this.menu.on("hide", this.onMenuHide, this);
11799         }
11800         btn.addClass("x-btn");
11801         if(Roo.isIE && !Roo.isIE7){
11802             this.autoWidth.defer(1, this);
11803         }else{
11804             this.autoWidth();
11805         }
11806         if(this.handleMouseEvents){
11807             btn.on("mouseover", this.onMouseOver, this);
11808             btn.on("mouseout", this.onMouseOut, this);
11809             btn.on("mousedown", this.onMouseDown, this);
11810         }
11811         btn.on(this.clickEvent, this.onClick, this);
11812         //btn.on("mouseup", this.onMouseUp, this);
11813         if(this.hidden){
11814             this.hide();
11815         }
11816         if(this.disabled){
11817             this.disable();
11818         }
11819         Roo.ButtonToggleMgr.register(this);
11820         if(this.pressed){
11821             this.el.addClass("x-btn-pressed");
11822         }
11823         if(this.repeat){
11824             var repeater = new Roo.util.ClickRepeater(btn,
11825                 typeof this.repeat == "object" ? this.repeat : {}
11826             );
11827             repeater.on("click", this.onClick,  this);
11828         }
11829         
11830         this.fireEvent('render', this);
11831         
11832     },
11833     /**
11834      * Returns the button's underlying element
11835      * @return {Roo.Element} The element
11836      */
11837     getEl : function(){
11838         return this.el;  
11839     },
11840     
11841     /**
11842      * Destroys this Button and removes any listeners.
11843      */
11844     destroy : function(){
11845         Roo.ButtonToggleMgr.unregister(this);
11846         this.el.removeAllListeners();
11847         this.purgeListeners();
11848         this.el.remove();
11849     },
11850
11851     // private
11852     autoWidth : function(){
11853         if(this.el){
11854             this.el.setWidth("auto");
11855             if(Roo.isIE7 && Roo.isStrict){
11856                 var ib = this.el.child('button');
11857                 if(ib && ib.getWidth() > 20){
11858                     ib.clip();
11859                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11860                 }
11861             }
11862             if(this.minWidth){
11863                 if(this.hidden){
11864                     this.el.beginMeasure();
11865                 }
11866                 if(this.el.getWidth() < this.minWidth){
11867                     this.el.setWidth(this.minWidth);
11868                 }
11869                 if(this.hidden){
11870                     this.el.endMeasure();
11871                 }
11872             }
11873         }
11874     },
11875
11876     /**
11877      * Assigns this button's click handler
11878      * @param {Function} handler The function to call when the button is clicked
11879      * @param {Object} scope (optional) Scope for the function passed in
11880      */
11881     setHandler : function(handler, scope){
11882         this.handler = handler;
11883         this.scope = scope;  
11884     },
11885     
11886     /**
11887      * Sets this button's text
11888      * @param {String} text The button text
11889      */
11890     setText : function(text){
11891         this.text = text;
11892         if(this.el){
11893             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11894         }
11895         this.autoWidth();
11896     },
11897     
11898     /**
11899      * Gets the text for this button
11900      * @return {String} The button text
11901      */
11902     getText : function(){
11903         return this.text;  
11904     },
11905     
11906     /**
11907      * Show this button
11908      */
11909     show: function(){
11910         this.hidden = false;
11911         if(this.el){
11912             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11913         }
11914     },
11915     
11916     /**
11917      * Hide this button
11918      */
11919     hide: function(){
11920         this.hidden = true;
11921         if(this.el){
11922             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11923         }
11924     },
11925     
11926     /**
11927      * Convenience function for boolean show/hide
11928      * @param {Boolean} visible True to show, false to hide
11929      */
11930     setVisible: function(visible){
11931         if(visible) {
11932             this.show();
11933         }else{
11934             this.hide();
11935         }
11936     },
11937     
11938     /**
11939      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11940      * @param {Boolean} state (optional) Force a particular state
11941      */
11942     toggle : function(state){
11943         state = state === undefined ? !this.pressed : state;
11944         if(state != this.pressed){
11945             if(state){
11946                 this.el.addClass("x-btn-pressed");
11947                 this.pressed = true;
11948                 this.fireEvent("toggle", this, true);
11949             }else{
11950                 this.el.removeClass("x-btn-pressed");
11951                 this.pressed = false;
11952                 this.fireEvent("toggle", this, false);
11953             }
11954             if(this.toggleHandler){
11955                 this.toggleHandler.call(this.scope || this, this, state);
11956             }
11957         }
11958     },
11959     
11960     /**
11961      * Focus the button
11962      */
11963     focus : function(){
11964         this.el.child('button:first').focus();
11965     },
11966     
11967     /**
11968      * Disable this button
11969      */
11970     disable : function(){
11971         if(this.el){
11972             this.el.addClass("x-btn-disabled");
11973         }
11974         this.disabled = true;
11975     },
11976     
11977     /**
11978      * Enable this button
11979      */
11980     enable : function(){
11981         if(this.el){
11982             this.el.removeClass("x-btn-disabled");
11983         }
11984         this.disabled = false;
11985     },
11986
11987     /**
11988      * Convenience function for boolean enable/disable
11989      * @param {Boolean} enabled True to enable, false to disable
11990      */
11991     setDisabled : function(v){
11992         this[v !== true ? "enable" : "disable"]();
11993     },
11994
11995     // private
11996     onClick : function(e){
11997         if(e){
11998             e.preventDefault();
11999         }
12000         if(e.button != 0){
12001             return;
12002         }
12003         if(!this.disabled){
12004             if(this.enableToggle){
12005                 this.toggle();
12006             }
12007             if(this.menu && !this.menu.isVisible()){
12008                 this.menu.show(this.el, this.menuAlign);
12009             }
12010             this.fireEvent("click", this, e);
12011             if(this.handler){
12012                 this.el.removeClass("x-btn-over");
12013                 this.handler.call(this.scope || this, this, e);
12014             }
12015         }
12016     },
12017     // private
12018     onMouseOver : function(e){
12019         if(!this.disabled){
12020             this.el.addClass("x-btn-over");
12021             this.fireEvent('mouseover', this, e);
12022         }
12023     },
12024     // private
12025     onMouseOut : function(e){
12026         if(!e.within(this.el,  true)){
12027             this.el.removeClass("x-btn-over");
12028             this.fireEvent('mouseout', this, e);
12029         }
12030     },
12031     // private
12032     onFocus : function(e){
12033         if(!this.disabled){
12034             this.el.addClass("x-btn-focus");
12035         }
12036     },
12037     // private
12038     onBlur : function(e){
12039         this.el.removeClass("x-btn-focus");
12040     },
12041     // private
12042     onMouseDown : function(e){
12043         if(!this.disabled && e.button == 0){
12044             this.el.addClass("x-btn-click");
12045             Roo.get(document).on('mouseup', this.onMouseUp, this);
12046         }
12047     },
12048     // private
12049     onMouseUp : function(e){
12050         if(e.button == 0){
12051             this.el.removeClass("x-btn-click");
12052             Roo.get(document).un('mouseup', this.onMouseUp, this);
12053         }
12054     },
12055     // private
12056     onMenuShow : function(e){
12057         this.el.addClass("x-btn-menu-active");
12058     },
12059     // private
12060     onMenuHide : function(e){
12061         this.el.removeClass("x-btn-menu-active");
12062     }   
12063 });
12064
12065 // Private utility class used by Button
12066 Roo.ButtonToggleMgr = function(){
12067    var groups = {};
12068    
12069    function toggleGroup(btn, state){
12070        if(state){
12071            var g = groups[btn.toggleGroup];
12072            for(var i = 0, l = g.length; i < l; i++){
12073                if(g[i] != btn){
12074                    g[i].toggle(false);
12075                }
12076            }
12077        }
12078    }
12079    
12080    return {
12081        register : function(btn){
12082            if(!btn.toggleGroup){
12083                return;
12084            }
12085            var g = groups[btn.toggleGroup];
12086            if(!g){
12087                g = groups[btn.toggleGroup] = [];
12088            }
12089            g.push(btn);
12090            btn.on("toggle", toggleGroup);
12091        },
12092        
12093        unregister : function(btn){
12094            if(!btn.toggleGroup){
12095                return;
12096            }
12097            var g = groups[btn.toggleGroup];
12098            if(g){
12099                g.remove(btn);
12100                btn.un("toggle", toggleGroup);
12101            }
12102        }
12103    };
12104 }();/*
12105  * Based on:
12106  * Ext JS Library 1.1.1
12107  * Copyright(c) 2006-2007, Ext JS, LLC.
12108  *
12109  * Originally Released Under LGPL - original licence link has changed is not relivant.
12110  *
12111  * Fork - LGPL
12112  * <script type="text/javascript">
12113  */
12114  
12115 /**
12116  * @class Roo.SplitButton
12117  * @extends Roo.Button
12118  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12119  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12120  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12121  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12122  * @cfg {String} arrowTooltip The title attribute of the arrow
12123  * @constructor
12124  * Create a new menu button
12125  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12126  * @param {Object} config The config object
12127  */
12128 Roo.SplitButton = function(renderTo, config){
12129     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12130     /**
12131      * @event arrowclick
12132      * Fires when this button's arrow is clicked
12133      * @param {SplitButton} this
12134      * @param {EventObject} e The click event
12135      */
12136     this.addEvents({"arrowclick":true});
12137 };
12138
12139 Roo.extend(Roo.SplitButton, Roo.Button, {
12140     render : function(renderTo){
12141         // this is one sweet looking template!
12142         var tpl = new Roo.Template(
12143             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12144             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12145             '<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>',
12146             "</tbody></table></td><td>",
12147             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12148             '<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>',
12149             "</tbody></table></td></tr></table>"
12150         );
12151         var btn = tpl.append(renderTo, [this.text, this.type], true);
12152         var btnEl = btn.child("button");
12153         if(this.cls){
12154             btn.addClass(this.cls);
12155         }
12156         if(this.icon){
12157             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12158         }
12159         if(this.iconCls){
12160             btnEl.addClass(this.iconCls);
12161             if(!this.cls){
12162                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12163             }
12164         }
12165         this.el = btn;
12166         if(this.handleMouseEvents){
12167             btn.on("mouseover", this.onMouseOver, this);
12168             btn.on("mouseout", this.onMouseOut, this);
12169             btn.on("mousedown", this.onMouseDown, this);
12170             btn.on("mouseup", this.onMouseUp, this);
12171         }
12172         btn.on(this.clickEvent, this.onClick, this);
12173         if(this.tooltip){
12174             if(typeof this.tooltip == 'object'){
12175                 Roo.QuickTips.tips(Roo.apply({
12176                       target: btnEl.id
12177                 }, this.tooltip));
12178             } else {
12179                 btnEl.dom[this.tooltipType] = this.tooltip;
12180             }
12181         }
12182         if(this.arrowTooltip){
12183             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12184         }
12185         if(this.hidden){
12186             this.hide();
12187         }
12188         if(this.disabled){
12189             this.disable();
12190         }
12191         if(this.pressed){
12192             this.el.addClass("x-btn-pressed");
12193         }
12194         if(Roo.isIE && !Roo.isIE7){
12195             this.autoWidth.defer(1, this);
12196         }else{
12197             this.autoWidth();
12198         }
12199         if(this.menu){
12200             this.menu.on("show", this.onMenuShow, this);
12201             this.menu.on("hide", this.onMenuHide, this);
12202         }
12203         this.fireEvent('render', this);
12204     },
12205
12206     // private
12207     autoWidth : function(){
12208         if(this.el){
12209             var tbl = this.el.child("table:first");
12210             var tbl2 = this.el.child("table:last");
12211             this.el.setWidth("auto");
12212             tbl.setWidth("auto");
12213             if(Roo.isIE7 && Roo.isStrict){
12214                 var ib = this.el.child('button:first');
12215                 if(ib && ib.getWidth() > 20){
12216                     ib.clip();
12217                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12218                 }
12219             }
12220             if(this.minWidth){
12221                 if(this.hidden){
12222                     this.el.beginMeasure();
12223                 }
12224                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12225                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12226                 }
12227                 if(this.hidden){
12228                     this.el.endMeasure();
12229                 }
12230             }
12231             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12232         } 
12233     },
12234     /**
12235      * Sets this button's click handler
12236      * @param {Function} handler The function to call when the button is clicked
12237      * @param {Object} scope (optional) Scope for the function passed above
12238      */
12239     setHandler : function(handler, scope){
12240         this.handler = handler;
12241         this.scope = scope;  
12242     },
12243     
12244     /**
12245      * Sets this button's arrow click handler
12246      * @param {Function} handler The function to call when the arrow is clicked
12247      * @param {Object} scope (optional) Scope for the function passed above
12248      */
12249     setArrowHandler : function(handler, scope){
12250         this.arrowHandler = handler;
12251         this.scope = scope;  
12252     },
12253     
12254     /**
12255      * Focus the button
12256      */
12257     focus : function(){
12258         if(this.el){
12259             this.el.child("button:first").focus();
12260         }
12261     },
12262
12263     // private
12264     onClick : function(e){
12265         e.preventDefault();
12266         if(!this.disabled){
12267             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12268                 if(this.menu && !this.menu.isVisible()){
12269                     this.menu.show(this.el, this.menuAlign);
12270                 }
12271                 this.fireEvent("arrowclick", this, e);
12272                 if(this.arrowHandler){
12273                     this.arrowHandler.call(this.scope || this, this, e);
12274                 }
12275             }else{
12276                 this.fireEvent("click", this, e);
12277                 if(this.handler){
12278                     this.handler.call(this.scope || this, this, e);
12279                 }
12280             }
12281         }
12282     },
12283     // private
12284     onMouseDown : function(e){
12285         if(!this.disabled){
12286             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12287         }
12288     },
12289     // private
12290     onMouseUp : function(e){
12291         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12292     }   
12293 });
12294
12295
12296 // backwards compat
12297 Roo.MenuButton = Roo.SplitButton;/*
12298  * Based on:
12299  * Ext JS Library 1.1.1
12300  * Copyright(c) 2006-2007, Ext JS, LLC.
12301  *
12302  * Originally Released Under LGPL - original licence link has changed is not relivant.
12303  *
12304  * Fork - LGPL
12305  * <script type="text/javascript">
12306  */
12307
12308 /**
12309  * @class Roo.Toolbar
12310  * Basic Toolbar class.
12311  * @constructor
12312  * Creates a new Toolbar
12313  * @param {Object} container The config object
12314  */ 
12315 Roo.Toolbar = function(container, buttons, config)
12316 {
12317     /// old consturctor format still supported..
12318     if(container instanceof Array){ // omit the container for later rendering
12319         buttons = container;
12320         config = buttons;
12321         container = null;
12322     }
12323     if (typeof(container) == 'object' && container.xtype) {
12324         config = container;
12325         container = config.container;
12326         buttons = config.buttons || []; // not really - use items!!
12327     }
12328     var xitems = [];
12329     if (config && config.items) {
12330         xitems = config.items;
12331         delete config.items;
12332     }
12333     Roo.apply(this, config);
12334     this.buttons = buttons;
12335     
12336     if(container){
12337         this.render(container);
12338     }
12339     this.xitems = xitems;
12340     Roo.each(xitems, function(b) {
12341         this.add(b);
12342     }, this);
12343     
12344 };
12345
12346 Roo.Toolbar.prototype = {
12347     /**
12348      * @cfg {Array} items
12349      * array of button configs or elements to add (will be converted to a MixedCollection)
12350      */
12351     
12352     /**
12353      * @cfg {String/HTMLElement/Element} container
12354      * The id or element that will contain the toolbar
12355      */
12356     // private
12357     render : function(ct){
12358         this.el = Roo.get(ct);
12359         if(this.cls){
12360             this.el.addClass(this.cls);
12361         }
12362         // using a table allows for vertical alignment
12363         // 100% width is needed by Safari...
12364         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12365         this.tr = this.el.child("tr", true);
12366         var autoId = 0;
12367         this.items = new Roo.util.MixedCollection(false, function(o){
12368             return o.id || ("item" + (++autoId));
12369         });
12370         if(this.buttons){
12371             this.add.apply(this, this.buttons);
12372             delete this.buttons;
12373         }
12374     },
12375
12376     /**
12377      * Adds element(s) to the toolbar -- this function takes a variable number of 
12378      * arguments of mixed type and adds them to the toolbar.
12379      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12380      * <ul>
12381      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12382      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12383      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12384      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12385      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12386      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12387      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12388      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12389      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12390      * </ul>
12391      * @param {Mixed} arg2
12392      * @param {Mixed} etc.
12393      */
12394     add : function(){
12395         var a = arguments, l = a.length;
12396         for(var i = 0; i < l; i++){
12397             this._add(a[i]);
12398         }
12399     },
12400     // private..
12401     _add : function(el) {
12402         
12403         if (el.xtype) {
12404             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12405         }
12406         
12407         if (el.applyTo){ // some kind of form field
12408             return this.addField(el);
12409         } 
12410         if (el.render){ // some kind of Toolbar.Item
12411             return this.addItem(el);
12412         }
12413         if (typeof el == "string"){ // string
12414             if(el == "separator" || el == "-"){
12415                 return this.addSeparator();
12416             }
12417             if (el == " "){
12418                 return this.addSpacer();
12419             }
12420             if(el == "->"){
12421                 return this.addFill();
12422             }
12423             return this.addText(el);
12424             
12425         }
12426         if(el.tagName){ // element
12427             return this.addElement(el);
12428         }
12429         if(typeof el == "object"){ // must be button config?
12430             return this.addButton(el);
12431         }
12432         // and now what?!?!
12433         return false;
12434         
12435     },
12436     
12437     /**
12438      * Add an Xtype element
12439      * @param {Object} xtype Xtype Object
12440      * @return {Object} created Object
12441      */
12442     addxtype : function(e){
12443         return this.add(e);  
12444     },
12445     
12446     /**
12447      * Returns the Element for this toolbar.
12448      * @return {Roo.Element}
12449      */
12450     getEl : function(){
12451         return this.el;  
12452     },
12453     
12454     /**
12455      * Adds a separator
12456      * @return {Roo.Toolbar.Item} The separator item
12457      */
12458     addSeparator : function(){
12459         return this.addItem(new Roo.Toolbar.Separator());
12460     },
12461
12462     /**
12463      * Adds a spacer element
12464      * @return {Roo.Toolbar.Spacer} The spacer item
12465      */
12466     addSpacer : function(){
12467         return this.addItem(new Roo.Toolbar.Spacer());
12468     },
12469
12470     /**
12471      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12472      * @return {Roo.Toolbar.Fill} The fill item
12473      */
12474     addFill : function(){
12475         return this.addItem(new Roo.Toolbar.Fill());
12476     },
12477
12478     /**
12479      * Adds any standard HTML element to the toolbar
12480      * @param {String/HTMLElement/Element} el The element or id of the element to add
12481      * @return {Roo.Toolbar.Item} The element's item
12482      */
12483     addElement : function(el){
12484         return this.addItem(new Roo.Toolbar.Item(el));
12485     },
12486     /**
12487      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12488      * @type Roo.util.MixedCollection  
12489      */
12490     items : false,
12491      
12492     /**
12493      * Adds any Toolbar.Item or subclass
12494      * @param {Roo.Toolbar.Item} item
12495      * @return {Roo.Toolbar.Item} The item
12496      */
12497     addItem : function(item){
12498         var td = this.nextBlock();
12499         item.render(td);
12500         this.items.add(item);
12501         return item;
12502     },
12503     
12504     /**
12505      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12506      * @param {Object/Array} config A button config or array of configs
12507      * @return {Roo.Toolbar.Button/Array}
12508      */
12509     addButton : function(config){
12510         if(config instanceof Array){
12511             var buttons = [];
12512             for(var i = 0, len = config.length; i < len; i++) {
12513                 buttons.push(this.addButton(config[i]));
12514             }
12515             return buttons;
12516         }
12517         var b = config;
12518         if(!(config instanceof Roo.Toolbar.Button)){
12519             b = config.split ?
12520                 new Roo.Toolbar.SplitButton(config) :
12521                 new Roo.Toolbar.Button(config);
12522         }
12523         var td = this.nextBlock();
12524         b.render(td);
12525         this.items.add(b);
12526         return b;
12527     },
12528     
12529     /**
12530      * Adds text to the toolbar
12531      * @param {String} text The text to add
12532      * @return {Roo.Toolbar.Item} The element's item
12533      */
12534     addText : function(text){
12535         return this.addItem(new Roo.Toolbar.TextItem(text));
12536     },
12537     
12538     /**
12539      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12540      * @param {Number} index The index where the item is to be inserted
12541      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12542      * @return {Roo.Toolbar.Button/Item}
12543      */
12544     insertButton : function(index, item){
12545         if(item instanceof Array){
12546             var buttons = [];
12547             for(var i = 0, len = item.length; i < len; i++) {
12548                buttons.push(this.insertButton(index + i, item[i]));
12549             }
12550             return buttons;
12551         }
12552         if (!(item instanceof Roo.Toolbar.Button)){
12553            item = new Roo.Toolbar.Button(item);
12554         }
12555         var td = document.createElement("td");
12556         this.tr.insertBefore(td, this.tr.childNodes[index]);
12557         item.render(td);
12558         this.items.insert(index, item);
12559         return item;
12560     },
12561     
12562     /**
12563      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12564      * @param {Object} config
12565      * @return {Roo.Toolbar.Item} The element's item
12566      */
12567     addDom : function(config, returnEl){
12568         var td = this.nextBlock();
12569         Roo.DomHelper.overwrite(td, config);
12570         var ti = new Roo.Toolbar.Item(td.firstChild);
12571         ti.render(td);
12572         this.items.add(ti);
12573         return ti;
12574     },
12575
12576     /**
12577      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12578      * @type Roo.util.MixedCollection  
12579      */
12580     fields : false,
12581     
12582     /**
12583      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12584      * Note: the field should not have been rendered yet. For a field that has already been
12585      * rendered, use {@link #addElement}.
12586      * @param {Roo.form.Field} field
12587      * @return {Roo.ToolbarItem}
12588      */
12589      
12590       
12591     addField : function(field) {
12592         if (!this.fields) {
12593             var autoId = 0;
12594             this.fields = new Roo.util.MixedCollection(false, function(o){
12595                 return o.id || ("item" + (++autoId));
12596             });
12597
12598         }
12599         
12600         var td = this.nextBlock();
12601         field.render(td);
12602         var ti = new Roo.Toolbar.Item(td.firstChild);
12603         ti.render(td);
12604         this.items.add(ti);
12605         this.fields.add(field);
12606         return ti;
12607     },
12608     /**
12609      * Hide the toolbar
12610      * @method hide
12611      */
12612      
12613       
12614     hide : function()
12615     {
12616         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12617         this.el.child('div').hide();
12618     },
12619     /**
12620      * Show the toolbar
12621      * @method show
12622      */
12623     show : function()
12624     {
12625         this.el.child('div').show();
12626     },
12627       
12628     // private
12629     nextBlock : function(){
12630         var td = document.createElement("td");
12631         this.tr.appendChild(td);
12632         return td;
12633     },
12634
12635     // private
12636     destroy : function(){
12637         if(this.items){ // rendered?
12638             Roo.destroy.apply(Roo, this.items.items);
12639         }
12640         if(this.fields){ // rendered?
12641             Roo.destroy.apply(Roo, this.fields.items);
12642         }
12643         Roo.Element.uncache(this.el, this.tr);
12644     }
12645 };
12646
12647 /**
12648  * @class Roo.Toolbar.Item
12649  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12650  * @constructor
12651  * Creates a new Item
12652  * @param {HTMLElement} el 
12653  */
12654 Roo.Toolbar.Item = function(el){
12655     this.el = Roo.getDom(el);
12656     this.id = Roo.id(this.el);
12657     this.hidden = false;
12658 };
12659
12660 Roo.Toolbar.Item.prototype = {
12661     
12662     /**
12663      * Get this item's HTML Element
12664      * @return {HTMLElement}
12665      */
12666     getEl : function(){
12667        return this.el;  
12668     },
12669
12670     // private
12671     render : function(td){
12672         this.td = td;
12673         td.appendChild(this.el);
12674     },
12675     
12676     /**
12677      * Removes and destroys this item.
12678      */
12679     destroy : function(){
12680         this.td.parentNode.removeChild(this.td);
12681     },
12682     
12683     /**
12684      * Shows this item.
12685      */
12686     show: function(){
12687         this.hidden = false;
12688         this.td.style.display = "";
12689     },
12690     
12691     /**
12692      * Hides this item.
12693      */
12694     hide: function(){
12695         this.hidden = true;
12696         this.td.style.display = "none";
12697     },
12698     
12699     /**
12700      * Convenience function for boolean show/hide.
12701      * @param {Boolean} visible true to show/false to hide
12702      */
12703     setVisible: function(visible){
12704         if(visible) {
12705             this.show();
12706         }else{
12707             this.hide();
12708         }
12709     },
12710     
12711     /**
12712      * Try to focus this item.
12713      */
12714     focus : function(){
12715         Roo.fly(this.el).focus();
12716     },
12717     
12718     /**
12719      * Disables this item.
12720      */
12721     disable : function(){
12722         Roo.fly(this.td).addClass("x-item-disabled");
12723         this.disabled = true;
12724         this.el.disabled = true;
12725     },
12726     
12727     /**
12728      * Enables this item.
12729      */
12730     enable : function(){
12731         Roo.fly(this.td).removeClass("x-item-disabled");
12732         this.disabled = false;
12733         this.el.disabled = false;
12734     }
12735 };
12736
12737
12738 /**
12739  * @class Roo.Toolbar.Separator
12740  * @extends Roo.Toolbar.Item
12741  * A simple toolbar separator class
12742  * @constructor
12743  * Creates a new Separator
12744  */
12745 Roo.Toolbar.Separator = function(){
12746     var s = document.createElement("span");
12747     s.className = "ytb-sep";
12748     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12749 };
12750 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12751     enable:Roo.emptyFn,
12752     disable:Roo.emptyFn,
12753     focus:Roo.emptyFn
12754 });
12755
12756 /**
12757  * @class Roo.Toolbar.Spacer
12758  * @extends Roo.Toolbar.Item
12759  * A simple element that adds extra horizontal space to a toolbar.
12760  * @constructor
12761  * Creates a new Spacer
12762  */
12763 Roo.Toolbar.Spacer = function(){
12764     var s = document.createElement("div");
12765     s.className = "ytb-spacer";
12766     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12767 };
12768 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12769     enable:Roo.emptyFn,
12770     disable:Roo.emptyFn,
12771     focus:Roo.emptyFn
12772 });
12773
12774 /**
12775  * @class Roo.Toolbar.Fill
12776  * @extends Roo.Toolbar.Spacer
12777  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12778  * @constructor
12779  * Creates a new Spacer
12780  */
12781 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12782     // private
12783     render : function(td){
12784         td.style.width = '100%';
12785         Roo.Toolbar.Fill.superclass.render.call(this, td);
12786     }
12787 });
12788
12789 /**
12790  * @class Roo.Toolbar.TextItem
12791  * @extends Roo.Toolbar.Item
12792  * A simple class that renders text directly into a toolbar.
12793  * @constructor
12794  * Creates a new TextItem
12795  * @param {String} text
12796  */
12797 Roo.Toolbar.TextItem = function(text){
12798     if (typeof(text) == 'object') {
12799         text = text.text;
12800     }
12801     var s = document.createElement("span");
12802     s.className = "ytb-text";
12803     s.innerHTML = text;
12804     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12805 };
12806 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12807     enable:Roo.emptyFn,
12808     disable:Roo.emptyFn,
12809     focus:Roo.emptyFn
12810 });
12811
12812 /**
12813  * @class Roo.Toolbar.Button
12814  * @extends Roo.Button
12815  * A button that renders into a toolbar.
12816  * @constructor
12817  * Creates a new Button
12818  * @param {Object} config A standard {@link Roo.Button} config object
12819  */
12820 Roo.Toolbar.Button = function(config){
12821     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12822 };
12823 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12824     render : function(td){
12825         this.td = td;
12826         Roo.Toolbar.Button.superclass.render.call(this, td);
12827     },
12828     
12829     /**
12830      * Removes and destroys this button
12831      */
12832     destroy : function(){
12833         Roo.Toolbar.Button.superclass.destroy.call(this);
12834         this.td.parentNode.removeChild(this.td);
12835     },
12836     
12837     /**
12838      * Shows this button
12839      */
12840     show: function(){
12841         this.hidden = false;
12842         this.td.style.display = "";
12843     },
12844     
12845     /**
12846      * Hides this button
12847      */
12848     hide: function(){
12849         this.hidden = true;
12850         this.td.style.display = "none";
12851     },
12852
12853     /**
12854      * Disables this item
12855      */
12856     disable : function(){
12857         Roo.fly(this.td).addClass("x-item-disabled");
12858         this.disabled = true;
12859     },
12860
12861     /**
12862      * Enables this item
12863      */
12864     enable : function(){
12865         Roo.fly(this.td).removeClass("x-item-disabled");
12866         this.disabled = false;
12867     }
12868 });
12869 // backwards compat
12870 Roo.ToolbarButton = Roo.Toolbar.Button;
12871
12872 /**
12873  * @class Roo.Toolbar.SplitButton
12874  * @extends Roo.SplitButton
12875  * A menu button that renders into a toolbar.
12876  * @constructor
12877  * Creates a new SplitButton
12878  * @param {Object} config A standard {@link Roo.SplitButton} config object
12879  */
12880 Roo.Toolbar.SplitButton = function(config){
12881     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12882 };
12883 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12884     render : function(td){
12885         this.td = td;
12886         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12887     },
12888     
12889     /**
12890      * Removes and destroys this button
12891      */
12892     destroy : function(){
12893         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12894         this.td.parentNode.removeChild(this.td);
12895     },
12896     
12897     /**
12898      * Shows this button
12899      */
12900     show: function(){
12901         this.hidden = false;
12902         this.td.style.display = "";
12903     },
12904     
12905     /**
12906      * Hides this button
12907      */
12908     hide: function(){
12909         this.hidden = true;
12910         this.td.style.display = "none";
12911     }
12912 });
12913
12914 // backwards compat
12915 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12916  * Based on:
12917  * Ext JS Library 1.1.1
12918  * Copyright(c) 2006-2007, Ext JS, LLC.
12919  *
12920  * Originally Released Under LGPL - original licence link has changed is not relivant.
12921  *
12922  * Fork - LGPL
12923  * <script type="text/javascript">
12924  */
12925  
12926 /**
12927  * @class Roo.PagingToolbar
12928  * @extends Roo.Toolbar
12929  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12930  * @constructor
12931  * Create a new PagingToolbar
12932  * @param {Object} config The config object
12933  */
12934 Roo.PagingToolbar = function(el, ds, config)
12935 {
12936     // old args format still supported... - xtype is prefered..
12937     if (typeof(el) == 'object' && el.xtype) {
12938         // created from xtype...
12939         config = el;
12940         ds = el.dataSource;
12941         el = config.container;
12942     }
12943     var items = [];
12944     if (config.items) {
12945         items = config.items;
12946         config.items = [];
12947     }
12948     
12949     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12950     this.ds = ds;
12951     this.cursor = 0;
12952     this.renderButtons(this.el);
12953     this.bind(ds);
12954     
12955     // supprot items array.
12956    
12957     Roo.each(items, function(e) {
12958         this.add(Roo.factory(e));
12959     },this);
12960     
12961 };
12962
12963 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12964     /**
12965      * @cfg {Roo.data.Store} dataSource
12966      * The underlying data store providing the paged data
12967      */
12968     /**
12969      * @cfg {String/HTMLElement/Element} container
12970      * container The id or element that will contain the toolbar
12971      */
12972     /**
12973      * @cfg {Boolean} displayInfo
12974      * True to display the displayMsg (defaults to false)
12975      */
12976     /**
12977      * @cfg {Number} pageSize
12978      * The number of records to display per page (defaults to 20)
12979      */
12980     pageSize: 20,
12981     /**
12982      * @cfg {String} displayMsg
12983      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12984      */
12985     displayMsg : 'Displaying {0} - {1} of {2}',
12986     /**
12987      * @cfg {String} emptyMsg
12988      * The message to display when no records are found (defaults to "No data to display")
12989      */
12990     emptyMsg : 'No data to display',
12991     /**
12992      * Customizable piece of the default paging text (defaults to "Page")
12993      * @type String
12994      */
12995     beforePageText : "Page",
12996     /**
12997      * Customizable piece of the default paging text (defaults to "of %0")
12998      * @type String
12999      */
13000     afterPageText : "of {0}",
13001     /**
13002      * Customizable piece of the default paging text (defaults to "First Page")
13003      * @type String
13004      */
13005     firstText : "First Page",
13006     /**
13007      * Customizable piece of the default paging text (defaults to "Previous Page")
13008      * @type String
13009      */
13010     prevText : "Previous Page",
13011     /**
13012      * Customizable piece of the default paging text (defaults to "Next Page")
13013      * @type String
13014      */
13015     nextText : "Next Page",
13016     /**
13017      * Customizable piece of the default paging text (defaults to "Last Page")
13018      * @type String
13019      */
13020     lastText : "Last Page",
13021     /**
13022      * Customizable piece of the default paging text (defaults to "Refresh")
13023      * @type String
13024      */
13025     refreshText : "Refresh",
13026
13027     // private
13028     renderButtons : function(el){
13029         Roo.PagingToolbar.superclass.render.call(this, el);
13030         this.first = this.addButton({
13031             tooltip: this.firstText,
13032             cls: "x-btn-icon x-grid-page-first",
13033             disabled: true,
13034             handler: this.onClick.createDelegate(this, ["first"])
13035         });
13036         this.prev = this.addButton({
13037             tooltip: this.prevText,
13038             cls: "x-btn-icon x-grid-page-prev",
13039             disabled: true,
13040             handler: this.onClick.createDelegate(this, ["prev"])
13041         });
13042         //this.addSeparator();
13043         this.add(this.beforePageText);
13044         this.field = Roo.get(this.addDom({
13045            tag: "input",
13046            type: "text",
13047            size: "3",
13048            value: "1",
13049            cls: "x-grid-page-number"
13050         }).el);
13051         this.field.on("keydown", this.onPagingKeydown, this);
13052         this.field.on("focus", function(){this.dom.select();});
13053         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13054         this.field.setHeight(18);
13055         //this.addSeparator();
13056         this.next = this.addButton({
13057             tooltip: this.nextText,
13058             cls: "x-btn-icon x-grid-page-next",
13059             disabled: true,
13060             handler: this.onClick.createDelegate(this, ["next"])
13061         });
13062         this.last = this.addButton({
13063             tooltip: this.lastText,
13064             cls: "x-btn-icon x-grid-page-last",
13065             disabled: true,
13066             handler: this.onClick.createDelegate(this, ["last"])
13067         });
13068         //this.addSeparator();
13069         this.loading = this.addButton({
13070             tooltip: this.refreshText,
13071             cls: "x-btn-icon x-grid-loading",
13072             handler: this.onClick.createDelegate(this, ["refresh"])
13073         });
13074
13075         if(this.displayInfo){
13076             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13077         }
13078     },
13079
13080     // private
13081     updateInfo : function(){
13082         if(this.displayEl){
13083             var count = this.ds.getCount();
13084             var msg = count == 0 ?
13085                 this.emptyMsg :
13086                 String.format(
13087                     this.displayMsg,
13088                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13089                 );
13090             this.displayEl.update(msg);
13091         }
13092     },
13093
13094     // private
13095     onLoad : function(ds, r, o){
13096        this.cursor = o.params ? o.params.start : 0;
13097        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13098
13099        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13100        this.field.dom.value = ap;
13101        this.first.setDisabled(ap == 1);
13102        this.prev.setDisabled(ap == 1);
13103        this.next.setDisabled(ap == ps);
13104        this.last.setDisabled(ap == ps);
13105        this.loading.enable();
13106        this.updateInfo();
13107     },
13108
13109     // private
13110     getPageData : function(){
13111         var total = this.ds.getTotalCount();
13112         return {
13113             total : total,
13114             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13115             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13116         };
13117     },
13118
13119     // private
13120     onLoadError : function(){
13121         this.loading.enable();
13122     },
13123
13124     // private
13125     onPagingKeydown : function(e){
13126         var k = e.getKey();
13127         var d = this.getPageData();
13128         if(k == e.RETURN){
13129             var v = this.field.dom.value, pageNum;
13130             if(!v || isNaN(pageNum = parseInt(v, 10))){
13131                 this.field.dom.value = d.activePage;
13132                 return;
13133             }
13134             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13135             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13136             e.stopEvent();
13137         }
13138         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))
13139         {
13140           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13141           this.field.dom.value = pageNum;
13142           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13143           e.stopEvent();
13144         }
13145         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13146         {
13147           var v = this.field.dom.value, pageNum; 
13148           var increment = (e.shiftKey) ? 10 : 1;
13149           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13150             increment *= -1;
13151           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13152             this.field.dom.value = d.activePage;
13153             return;
13154           }
13155           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13156           {
13157             this.field.dom.value = parseInt(v, 10) + increment;
13158             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13159             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13160           }
13161           e.stopEvent();
13162         }
13163     },
13164
13165     // private
13166     beforeLoad : function(){
13167         if(this.loading){
13168             this.loading.disable();
13169         }
13170     },
13171
13172     // private
13173     onClick : function(which){
13174         var ds = this.ds;
13175         switch(which){
13176             case "first":
13177                 ds.load({params:{start: 0, limit: this.pageSize}});
13178             break;
13179             case "prev":
13180                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13181             break;
13182             case "next":
13183                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13184             break;
13185             case "last":
13186                 var total = ds.getTotalCount();
13187                 var extra = total % this.pageSize;
13188                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13189                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13190             break;
13191             case "refresh":
13192                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13193             break;
13194         }
13195     },
13196
13197     /**
13198      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13199      * @param {Roo.data.Store} store The data store to unbind
13200      */
13201     unbind : function(ds){
13202         ds.un("beforeload", this.beforeLoad, this);
13203         ds.un("load", this.onLoad, this);
13204         ds.un("loadexception", this.onLoadError, this);
13205         ds.un("remove", this.updateInfo, this);
13206         ds.un("add", this.updateInfo, this);
13207         this.ds = undefined;
13208     },
13209
13210     /**
13211      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13212      * @param {Roo.data.Store} store The data store to bind
13213      */
13214     bind : function(ds){
13215         ds.on("beforeload", this.beforeLoad, this);
13216         ds.on("load", this.onLoad, this);
13217         ds.on("loadexception", this.onLoadError, this);
13218         ds.on("remove", this.updateInfo, this);
13219         ds.on("add", this.updateInfo, this);
13220         this.ds = ds;
13221     }
13222 });/*
13223  * Based on:
13224  * Ext JS Library 1.1.1
13225  * Copyright(c) 2006-2007, Ext JS, LLC.
13226  *
13227  * Originally Released Under LGPL - original licence link has changed is not relivant.
13228  *
13229  * Fork - LGPL
13230  * <script type="text/javascript">
13231  */
13232
13233 /**
13234  * @class Roo.Resizable
13235  * @extends Roo.util.Observable
13236  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13237  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13238  * 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
13239  * the element will be wrapped for you automatically.</p>
13240  * <p>Here is the list of valid resize handles:</p>
13241  * <pre>
13242 Value   Description
13243 ------  -------------------
13244  'n'     north
13245  's'     south
13246  'e'     east
13247  'w'     west
13248  'nw'    northwest
13249  'sw'    southwest
13250  'se'    southeast
13251  'ne'    northeast
13252  'hd'    horizontal drag
13253  'all'   all
13254 </pre>
13255  * <p>Here's an example showing the creation of a typical Resizable:</p>
13256  * <pre><code>
13257 var resizer = new Roo.Resizable("element-id", {
13258     handles: 'all',
13259     minWidth: 200,
13260     minHeight: 100,
13261     maxWidth: 500,
13262     maxHeight: 400,
13263     pinned: true
13264 });
13265 resizer.on("resize", myHandler);
13266 </code></pre>
13267  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13268  * resizer.east.setDisplayed(false);</p>
13269  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13270  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13271  * resize operation's new size (defaults to [0, 0])
13272  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13273  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13274  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13275  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13276  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13277  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13278  * @cfg {Number} width The width of the element in pixels (defaults to null)
13279  * @cfg {Number} height The height of the element in pixels (defaults to null)
13280  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13281  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13282  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13283  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13284  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13285  * in favor of the handles config option (defaults to false)
13286  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13287  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13288  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13289  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13290  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13291  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13292  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13293  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13294  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13295  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13296  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13297  * @constructor
13298  * Create a new resizable component
13299  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13300  * @param {Object} config configuration options
13301   */
13302 Roo.Resizable = function(el, config)
13303 {
13304     this.el = Roo.get(el);
13305
13306     if(config && config.wrap){
13307         config.resizeChild = this.el;
13308         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13309         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13310         this.el.setStyle("overflow", "hidden");
13311         this.el.setPositioning(config.resizeChild.getPositioning());
13312         config.resizeChild.clearPositioning();
13313         if(!config.width || !config.height){
13314             var csize = config.resizeChild.getSize();
13315             this.el.setSize(csize.width, csize.height);
13316         }
13317         if(config.pinned && !config.adjustments){
13318             config.adjustments = "auto";
13319         }
13320     }
13321
13322     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13323     this.proxy.unselectable();
13324     this.proxy.enableDisplayMode('block');
13325
13326     Roo.apply(this, config);
13327
13328     if(this.pinned){
13329         this.disableTrackOver = true;
13330         this.el.addClass("x-resizable-pinned");
13331     }
13332     // if the element isn't positioned, make it relative
13333     var position = this.el.getStyle("position");
13334     if(position != "absolute" && position != "fixed"){
13335         this.el.setStyle("position", "relative");
13336     }
13337     if(!this.handles){ // no handles passed, must be legacy style
13338         this.handles = 's,e,se';
13339         if(this.multiDirectional){
13340             this.handles += ',n,w';
13341         }
13342     }
13343     if(this.handles == "all"){
13344         this.handles = "n s e w ne nw se sw";
13345     }
13346     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13347     var ps = Roo.Resizable.positions;
13348     for(var i = 0, len = hs.length; i < len; i++){
13349         if(hs[i] && ps[hs[i]]){
13350             var pos = ps[hs[i]];
13351             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13352         }
13353     }
13354     // legacy
13355     this.corner = this.southeast;
13356     
13357     // updateBox = the box can move..
13358     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13359         this.updateBox = true;
13360     }
13361
13362     this.activeHandle = null;
13363
13364     if(this.resizeChild){
13365         if(typeof this.resizeChild == "boolean"){
13366             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13367         }else{
13368             this.resizeChild = Roo.get(this.resizeChild, true);
13369         }
13370     }
13371     
13372     if(this.adjustments == "auto"){
13373         var rc = this.resizeChild;
13374         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13375         if(rc && (hw || hn)){
13376             rc.position("relative");
13377             rc.setLeft(hw ? hw.el.getWidth() : 0);
13378             rc.setTop(hn ? hn.el.getHeight() : 0);
13379         }
13380         this.adjustments = [
13381             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13382             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13383         ];
13384     }
13385
13386     if(this.draggable){
13387         this.dd = this.dynamic ?
13388             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13389         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13390     }
13391
13392     // public events
13393     this.addEvents({
13394         /**
13395          * @event beforeresize
13396          * Fired before resize is allowed. Set enabled to false to cancel resize.
13397          * @param {Roo.Resizable} this
13398          * @param {Roo.EventObject} e The mousedown event
13399          */
13400         "beforeresize" : true,
13401         /**
13402          * @event resize
13403          * Fired after a resize.
13404          * @param {Roo.Resizable} this
13405          * @param {Number} width The new width
13406          * @param {Number} height The new height
13407          * @param {Roo.EventObject} e The mouseup event
13408          */
13409         "resize" : true
13410     });
13411
13412     if(this.width !== null && this.height !== null){
13413         this.resizeTo(this.width, this.height);
13414     }else{
13415         this.updateChildSize();
13416     }
13417     if(Roo.isIE){
13418         this.el.dom.style.zoom = 1;
13419     }
13420     Roo.Resizable.superclass.constructor.call(this);
13421 };
13422
13423 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13424         resizeChild : false,
13425         adjustments : [0, 0],
13426         minWidth : 5,
13427         minHeight : 5,
13428         maxWidth : 10000,
13429         maxHeight : 10000,
13430         enabled : true,
13431         animate : false,
13432         duration : .35,
13433         dynamic : false,
13434         handles : false,
13435         multiDirectional : false,
13436         disableTrackOver : false,
13437         easing : 'easeOutStrong',
13438         widthIncrement : 0,
13439         heightIncrement : 0,
13440         pinned : false,
13441         width : null,
13442         height : null,
13443         preserveRatio : false,
13444         transparent: false,
13445         minX: 0,
13446         minY: 0,
13447         draggable: false,
13448
13449         /**
13450          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13451          */
13452         constrainTo: undefined,
13453         /**
13454          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13455          */
13456         resizeRegion: undefined,
13457
13458
13459     /**
13460      * Perform a manual resize
13461      * @param {Number} width
13462      * @param {Number} height
13463      */
13464     resizeTo : function(width, height){
13465         this.el.setSize(width, height);
13466         this.updateChildSize();
13467         this.fireEvent("resize", this, width, height, null);
13468     },
13469
13470     // private
13471     startSizing : function(e, handle){
13472         this.fireEvent("beforeresize", this, e);
13473         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13474
13475             if(!this.overlay){
13476                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13477                 this.overlay.unselectable();
13478                 this.overlay.enableDisplayMode("block");
13479                 this.overlay.on("mousemove", this.onMouseMove, this);
13480                 this.overlay.on("mouseup", this.onMouseUp, this);
13481             }
13482             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13483
13484             this.resizing = true;
13485             this.startBox = this.el.getBox();
13486             this.startPoint = e.getXY();
13487             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13488                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13489
13490             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13491             this.overlay.show();
13492
13493             if(this.constrainTo) {
13494                 var ct = Roo.get(this.constrainTo);
13495                 this.resizeRegion = ct.getRegion().adjust(
13496                     ct.getFrameWidth('t'),
13497                     ct.getFrameWidth('l'),
13498                     -ct.getFrameWidth('b'),
13499                     -ct.getFrameWidth('r')
13500                 );
13501             }
13502
13503             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13504             this.proxy.show();
13505             this.proxy.setBox(this.startBox);
13506             if(!this.dynamic){
13507                 this.proxy.setStyle('visibility', 'visible');
13508             }
13509         }
13510     },
13511
13512     // private
13513     onMouseDown : function(handle, e){
13514         if(this.enabled){
13515             e.stopEvent();
13516             this.activeHandle = handle;
13517             this.startSizing(e, handle);
13518         }
13519     },
13520
13521     // private
13522     onMouseUp : function(e){
13523         var size = this.resizeElement();
13524         this.resizing = false;
13525         this.handleOut();
13526         this.overlay.hide();
13527         this.proxy.hide();
13528         this.fireEvent("resize", this, size.width, size.height, e);
13529     },
13530
13531     // private
13532     updateChildSize : function(){
13533         if(this.resizeChild){
13534             var el = this.el;
13535             var child = this.resizeChild;
13536             var adj = this.adjustments;
13537             if(el.dom.offsetWidth){
13538                 var b = el.getSize(true);
13539                 child.setSize(b.width+adj[0], b.height+adj[1]);
13540             }
13541             // Second call here for IE
13542             // The first call enables instant resizing and
13543             // the second call corrects scroll bars if they
13544             // exist
13545             if(Roo.isIE){
13546                 setTimeout(function(){
13547                     if(el.dom.offsetWidth){
13548                         var b = el.getSize(true);
13549                         child.setSize(b.width+adj[0], b.height+adj[1]);
13550                     }
13551                 }, 10);
13552             }
13553         }
13554     },
13555
13556     // private
13557     snap : function(value, inc, min){
13558         if(!inc || !value) return value;
13559         var newValue = value;
13560         var m = value % inc;
13561         if(m > 0){
13562             if(m > (inc/2)){
13563                 newValue = value + (inc-m);
13564             }else{
13565                 newValue = value - m;
13566             }
13567         }
13568         return Math.max(min, newValue);
13569     },
13570
13571     // private
13572     resizeElement : function(){
13573         var box = this.proxy.getBox();
13574         if(this.updateBox){
13575             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13576         }else{
13577             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13578         }
13579         this.updateChildSize();
13580         if(!this.dynamic){
13581             this.proxy.hide();
13582         }
13583         return box;
13584     },
13585
13586     // private
13587     constrain : function(v, diff, m, mx){
13588         if(v - diff < m){
13589             diff = v - m;
13590         }else if(v - diff > mx){
13591             diff = mx - v;
13592         }
13593         return diff;
13594     },
13595
13596     // private
13597     onMouseMove : function(e){
13598         if(this.enabled){
13599             try{// try catch so if something goes wrong the user doesn't get hung
13600
13601             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13602                 return;
13603             }
13604
13605             //var curXY = this.startPoint;
13606             var curSize = this.curSize || this.startBox;
13607             var x = this.startBox.x, y = this.startBox.y;
13608             var ox = x, oy = y;
13609             var w = curSize.width, h = curSize.height;
13610             var ow = w, oh = h;
13611             var mw = this.minWidth, mh = this.minHeight;
13612             var mxw = this.maxWidth, mxh = this.maxHeight;
13613             var wi = this.widthIncrement;
13614             var hi = this.heightIncrement;
13615
13616             var eventXY = e.getXY();
13617             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13618             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13619
13620             var pos = this.activeHandle.position;
13621
13622             switch(pos){
13623                 case "east":
13624                     w += diffX;
13625                     w = Math.min(Math.max(mw, w), mxw);
13626                     break;
13627              
13628                 case "south":
13629                     h += diffY;
13630                     h = Math.min(Math.max(mh, h), mxh);
13631                     break;
13632                 case "southeast":
13633                     w += diffX;
13634                     h += diffY;
13635                     w = Math.min(Math.max(mw, w), mxw);
13636                     h = Math.min(Math.max(mh, h), mxh);
13637                     break;
13638                 case "north":
13639                     diffY = this.constrain(h, diffY, mh, mxh);
13640                     y += diffY;
13641                     h -= diffY;
13642                     break;
13643                 case "hdrag":
13644                     
13645                     if (wi) {
13646                         var adiffX = Math.abs(diffX);
13647                         var sub = (adiffX % wi); // how much 
13648                         if (sub > (wi/2)) { // far enough to snap
13649                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13650                         } else {
13651                             // remove difference.. 
13652                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13653                         }
13654                     }
13655                     x += diffX;
13656                     x = Math.max(this.minX, x);
13657                     break;
13658                 case "west":
13659                     diffX = this.constrain(w, diffX, mw, mxw);
13660                     x += diffX;
13661                     w -= diffX;
13662                     break;
13663                 case "northeast":
13664                     w += diffX;
13665                     w = Math.min(Math.max(mw, w), mxw);
13666                     diffY = this.constrain(h, diffY, mh, mxh);
13667                     y += diffY;
13668                     h -= diffY;
13669                     break;
13670                 case "northwest":
13671                     diffX = this.constrain(w, diffX, mw, mxw);
13672                     diffY = this.constrain(h, diffY, mh, mxh);
13673                     y += diffY;
13674                     h -= diffY;
13675                     x += diffX;
13676                     w -= diffX;
13677                     break;
13678                case "southwest":
13679                     diffX = this.constrain(w, diffX, mw, mxw);
13680                     h += diffY;
13681                     h = Math.min(Math.max(mh, h), mxh);
13682                     x += diffX;
13683                     w -= diffX;
13684                     break;
13685             }
13686
13687             var sw = this.snap(w, wi, mw);
13688             var sh = this.snap(h, hi, mh);
13689             if(sw != w || sh != h){
13690                 switch(pos){
13691                     case "northeast":
13692                         y -= sh - h;
13693                     break;
13694                     case "north":
13695                         y -= sh - h;
13696                         break;
13697                     case "southwest":
13698                         x -= sw - w;
13699                     break;
13700                     case "west":
13701                         x -= sw - w;
13702                         break;
13703                     case "northwest":
13704                         x -= sw - w;
13705                         y -= sh - h;
13706                     break;
13707                 }
13708                 w = sw;
13709                 h = sh;
13710             }
13711
13712             if(this.preserveRatio){
13713                 switch(pos){
13714                     case "southeast":
13715                     case "east":
13716                         h = oh * (w/ow);
13717                         h = Math.min(Math.max(mh, h), mxh);
13718                         w = ow * (h/oh);
13719                        break;
13720                     case "south":
13721                         w = ow * (h/oh);
13722                         w = Math.min(Math.max(mw, w), mxw);
13723                         h = oh * (w/ow);
13724                         break;
13725                     case "northeast":
13726                         w = ow * (h/oh);
13727                         w = Math.min(Math.max(mw, w), mxw);
13728                         h = oh * (w/ow);
13729                     break;
13730                     case "north":
13731                         var tw = w;
13732                         w = ow * (h/oh);
13733                         w = Math.min(Math.max(mw, w), mxw);
13734                         h = oh * (w/ow);
13735                         x += (tw - w) / 2;
13736                         break;
13737                     case "southwest":
13738                         h = oh * (w/ow);
13739                         h = Math.min(Math.max(mh, h), mxh);
13740                         var tw = w;
13741                         w = ow * (h/oh);
13742                         x += tw - w;
13743                         break;
13744                     case "west":
13745                         var th = h;
13746                         h = oh * (w/ow);
13747                         h = Math.min(Math.max(mh, h), mxh);
13748                         y += (th - h) / 2;
13749                         var tw = w;
13750                         w = ow * (h/oh);
13751                         x += tw - w;
13752                        break;
13753                     case "northwest":
13754                         var tw = w;
13755                         var th = h;
13756                         h = oh * (w/ow);
13757                         h = Math.min(Math.max(mh, h), mxh);
13758                         w = ow * (h/oh);
13759                         y += th - h;
13760                         x += tw - w;
13761                        break;
13762
13763                 }
13764             }
13765             if (pos == 'hdrag') {
13766                 w = ow;
13767             }
13768             this.proxy.setBounds(x, y, w, h);
13769             if(this.dynamic){
13770                 this.resizeElement();
13771             }
13772             }catch(e){}
13773         }
13774     },
13775
13776     // private
13777     handleOver : function(){
13778         if(this.enabled){
13779             this.el.addClass("x-resizable-over");
13780         }
13781     },
13782
13783     // private
13784     handleOut : function(){
13785         if(!this.resizing){
13786             this.el.removeClass("x-resizable-over");
13787         }
13788     },
13789
13790     /**
13791      * Returns the element this component is bound to.
13792      * @return {Roo.Element}
13793      */
13794     getEl : function(){
13795         return this.el;
13796     },
13797
13798     /**
13799      * Returns the resizeChild element (or null).
13800      * @return {Roo.Element}
13801      */
13802     getResizeChild : function(){
13803         return this.resizeChild;
13804     },
13805
13806     /**
13807      * Destroys this resizable. If the element was wrapped and
13808      * removeEl is not true then the element remains.
13809      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13810      */
13811     destroy : function(removeEl){
13812         this.proxy.remove();
13813         if(this.overlay){
13814             this.overlay.removeAllListeners();
13815             this.overlay.remove();
13816         }
13817         var ps = Roo.Resizable.positions;
13818         for(var k in ps){
13819             if(typeof ps[k] != "function" && this[ps[k]]){
13820                 var h = this[ps[k]];
13821                 h.el.removeAllListeners();
13822                 h.el.remove();
13823             }
13824         }
13825         if(removeEl){
13826             this.el.update("");
13827             this.el.remove();
13828         }
13829     }
13830 });
13831
13832 // private
13833 // hash to map config positions to true positions
13834 Roo.Resizable.positions = {
13835     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13836     hd: "hdrag"
13837 };
13838
13839 // private
13840 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13841     if(!this.tpl){
13842         // only initialize the template if resizable is used
13843         var tpl = Roo.DomHelper.createTemplate(
13844             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13845         );
13846         tpl.compile();
13847         Roo.Resizable.Handle.prototype.tpl = tpl;
13848     }
13849     this.position = pos;
13850     this.rz = rz;
13851     // show north drag fro topdra
13852     var handlepos = pos == 'hdrag' ? 'north' : pos;
13853     
13854     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13855     if (pos == 'hdrag') {
13856         this.el.setStyle('cursor', 'pointer');
13857     }
13858     this.el.unselectable();
13859     if(transparent){
13860         this.el.setOpacity(0);
13861     }
13862     this.el.on("mousedown", this.onMouseDown, this);
13863     if(!disableTrackOver){
13864         this.el.on("mouseover", this.onMouseOver, this);
13865         this.el.on("mouseout", this.onMouseOut, this);
13866     }
13867 };
13868
13869 // private
13870 Roo.Resizable.Handle.prototype = {
13871     afterResize : function(rz){
13872         // do nothing
13873     },
13874     // private
13875     onMouseDown : function(e){
13876         this.rz.onMouseDown(this, e);
13877     },
13878     // private
13879     onMouseOver : function(e){
13880         this.rz.handleOver(this, e);
13881     },
13882     // private
13883     onMouseOut : function(e){
13884         this.rz.handleOut(this, e);
13885     }
13886 };/*
13887  * Based on:
13888  * Ext JS Library 1.1.1
13889  * Copyright(c) 2006-2007, Ext JS, LLC.
13890  *
13891  * Originally Released Under LGPL - original licence link has changed is not relivant.
13892  *
13893  * Fork - LGPL
13894  * <script type="text/javascript">
13895  */
13896
13897 /**
13898  * @class Roo.Editor
13899  * @extends Roo.Component
13900  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13901  * @constructor
13902  * Create a new Editor
13903  * @param {Roo.form.Field} field The Field object (or descendant)
13904  * @param {Object} config The config object
13905  */
13906 Roo.Editor = function(field, config){
13907     Roo.Editor.superclass.constructor.call(this, config);
13908     this.field = field;
13909     this.addEvents({
13910         /**
13911              * @event beforestartedit
13912              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13913              * false from the handler of this event.
13914              * @param {Editor} this
13915              * @param {Roo.Element} boundEl The underlying element bound to this editor
13916              * @param {Mixed} value The field value being set
13917              */
13918         "beforestartedit" : true,
13919         /**
13920              * @event startedit
13921              * Fires when this editor is displayed
13922              * @param {Roo.Element} boundEl The underlying element bound to this editor
13923              * @param {Mixed} value The starting field value
13924              */
13925         "startedit" : true,
13926         /**
13927              * @event beforecomplete
13928              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13929              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13930              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13931              * event will not fire since no edit actually occurred.
13932              * @param {Editor} this
13933              * @param {Mixed} value The current field value
13934              * @param {Mixed} startValue The original field value
13935              */
13936         "beforecomplete" : true,
13937         /**
13938              * @event complete
13939              * Fires after editing is complete and any changed value has been written to the underlying field.
13940              * @param {Editor} this
13941              * @param {Mixed} value The current field value
13942              * @param {Mixed} startValue The original field value
13943              */
13944         "complete" : true,
13945         /**
13946          * @event specialkey
13947          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13948          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13949          * @param {Roo.form.Field} this
13950          * @param {Roo.EventObject} e The event object
13951          */
13952         "specialkey" : true
13953     });
13954 };
13955
13956 Roo.extend(Roo.Editor, Roo.Component, {
13957     /**
13958      * @cfg {Boolean/String} autosize
13959      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13960      * or "height" to adopt the height only (defaults to false)
13961      */
13962     /**
13963      * @cfg {Boolean} revertInvalid
13964      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13965      * validation fails (defaults to true)
13966      */
13967     /**
13968      * @cfg {Boolean} ignoreNoChange
13969      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13970      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13971      * will never be ignored.
13972      */
13973     /**
13974      * @cfg {Boolean} hideEl
13975      * False to keep the bound element visible while the editor is displayed (defaults to true)
13976      */
13977     /**
13978      * @cfg {Mixed} value
13979      * The data value of the underlying field (defaults to "")
13980      */
13981     value : "",
13982     /**
13983      * @cfg {String} alignment
13984      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13985      */
13986     alignment: "c-c?",
13987     /**
13988      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13989      * for bottom-right shadow (defaults to "frame")
13990      */
13991     shadow : "frame",
13992     /**
13993      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13994      */
13995     constrain : false,
13996     /**
13997      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13998      */
13999     completeOnEnter : false,
14000     /**
14001      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14002      */
14003     cancelOnEsc : false,
14004     /**
14005      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14006      */
14007     updateEl : false,
14008
14009     // private
14010     onRender : function(ct, position){
14011         this.el = new Roo.Layer({
14012             shadow: this.shadow,
14013             cls: "x-editor",
14014             parentEl : ct,
14015             shim : this.shim,
14016             shadowOffset:4,
14017             id: this.id,
14018             constrain: this.constrain
14019         });
14020         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14021         if(this.field.msgTarget != 'title'){
14022             this.field.msgTarget = 'qtip';
14023         }
14024         this.field.render(this.el);
14025         if(Roo.isGecko){
14026             this.field.el.dom.setAttribute('autocomplete', 'off');
14027         }
14028         this.field.on("specialkey", this.onSpecialKey, this);
14029         if(this.swallowKeys){
14030             this.field.el.swallowEvent(['keydown','keypress']);
14031         }
14032         this.field.show();
14033         this.field.on("blur", this.onBlur, this);
14034         if(this.field.grow){
14035             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14036         }
14037     },
14038
14039     onSpecialKey : function(field, e)
14040     {
14041         //Roo.log('editor onSpecialKey');
14042         if(this.completeOnEnter && e.getKey() == e.ENTER){
14043             e.stopEvent();
14044             this.completeEdit();
14045             return;
14046         }
14047         // do not fire special key otherwise it might hide close the editor...
14048         if(e.getKey() == e.ENTER){    
14049             return;
14050         }
14051         if(this.cancelOnEsc && e.getKey() == e.ESC){
14052             this.cancelEdit();
14053             return;
14054         } 
14055         this.fireEvent('specialkey', field, e);
14056     
14057     },
14058
14059     /**
14060      * Starts the editing process and shows the editor.
14061      * @param {String/HTMLElement/Element} el The element to edit
14062      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14063       * to the innerHTML of el.
14064      */
14065     startEdit : function(el, value){
14066         if(this.editing){
14067             this.completeEdit();
14068         }
14069         this.boundEl = Roo.get(el);
14070         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14071         if(!this.rendered){
14072             this.render(this.parentEl || document.body);
14073         }
14074         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14075             return;
14076         }
14077         this.startValue = v;
14078         this.field.setValue(v);
14079         if(this.autoSize){
14080             var sz = this.boundEl.getSize();
14081             switch(this.autoSize){
14082                 case "width":
14083                 this.setSize(sz.width,  "");
14084                 break;
14085                 case "height":
14086                 this.setSize("",  sz.height);
14087                 break;
14088                 default:
14089                 this.setSize(sz.width,  sz.height);
14090             }
14091         }
14092         this.el.alignTo(this.boundEl, this.alignment);
14093         this.editing = true;
14094         if(Roo.QuickTips){
14095             Roo.QuickTips.disable();
14096         }
14097         this.show();
14098     },
14099
14100     /**
14101      * Sets the height and width of this editor.
14102      * @param {Number} width The new width
14103      * @param {Number} height The new height
14104      */
14105     setSize : function(w, h){
14106         this.field.setSize(w, h);
14107         if(this.el){
14108             this.el.sync();
14109         }
14110     },
14111
14112     /**
14113      * Realigns the editor to the bound field based on the current alignment config value.
14114      */
14115     realign : function(){
14116         this.el.alignTo(this.boundEl, this.alignment);
14117     },
14118
14119     /**
14120      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14121      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14122      */
14123     completeEdit : function(remainVisible){
14124         if(!this.editing){
14125             return;
14126         }
14127         var v = this.getValue();
14128         if(this.revertInvalid !== false && !this.field.isValid()){
14129             v = this.startValue;
14130             this.cancelEdit(true);
14131         }
14132         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14133             this.editing = false;
14134             this.hide();
14135             return;
14136         }
14137         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14138             this.editing = false;
14139             if(this.updateEl && this.boundEl){
14140                 this.boundEl.update(v);
14141             }
14142             if(remainVisible !== true){
14143                 this.hide();
14144             }
14145             this.fireEvent("complete", this, v, this.startValue);
14146         }
14147     },
14148
14149     // private
14150     onShow : function(){
14151         this.el.show();
14152         if(this.hideEl !== false){
14153             this.boundEl.hide();
14154         }
14155         this.field.show();
14156         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14157             this.fixIEFocus = true;
14158             this.deferredFocus.defer(50, this);
14159         }else{
14160             this.field.focus();
14161         }
14162         this.fireEvent("startedit", this.boundEl, this.startValue);
14163     },
14164
14165     deferredFocus : function(){
14166         if(this.editing){
14167             this.field.focus();
14168         }
14169     },
14170
14171     /**
14172      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14173      * reverted to the original starting value.
14174      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14175      * cancel (defaults to false)
14176      */
14177     cancelEdit : function(remainVisible){
14178         if(this.editing){
14179             this.setValue(this.startValue);
14180             if(remainVisible !== true){
14181                 this.hide();
14182             }
14183         }
14184     },
14185
14186     // private
14187     onBlur : function(){
14188         if(this.allowBlur !== true && this.editing){
14189             this.completeEdit();
14190         }
14191     },
14192
14193     // private
14194     onHide : function(){
14195         if(this.editing){
14196             this.completeEdit();
14197             return;
14198         }
14199         this.field.blur();
14200         if(this.field.collapse){
14201             this.field.collapse();
14202         }
14203         this.el.hide();
14204         if(this.hideEl !== false){
14205             this.boundEl.show();
14206         }
14207         if(Roo.QuickTips){
14208             Roo.QuickTips.enable();
14209         }
14210     },
14211
14212     /**
14213      * Sets the data value of the editor
14214      * @param {Mixed} value Any valid value supported by the underlying field
14215      */
14216     setValue : function(v){
14217         this.field.setValue(v);
14218     },
14219
14220     /**
14221      * Gets the data value of the editor
14222      * @return {Mixed} The data value
14223      */
14224     getValue : function(){
14225         return this.field.getValue();
14226     }
14227 });/*
14228  * Based on:
14229  * Ext JS Library 1.1.1
14230  * Copyright(c) 2006-2007, Ext JS, LLC.
14231  *
14232  * Originally Released Under LGPL - original licence link has changed is not relivant.
14233  *
14234  * Fork - LGPL
14235  * <script type="text/javascript">
14236  */
14237  
14238 /**
14239  * @class Roo.BasicDialog
14240  * @extends Roo.util.Observable
14241  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14242  * <pre><code>
14243 var dlg = new Roo.BasicDialog("my-dlg", {
14244     height: 200,
14245     width: 300,
14246     minHeight: 100,
14247     minWidth: 150,
14248     modal: true,
14249     proxyDrag: true,
14250     shadow: true
14251 });
14252 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14253 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14254 dlg.addButton('Cancel', dlg.hide, dlg);
14255 dlg.show();
14256 </code></pre>
14257   <b>A Dialog should always be a direct child of the body element.</b>
14258  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14259  * @cfg {String} title Default text to display in the title bar (defaults to null)
14260  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14261  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14262  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14263  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14264  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14265  * (defaults to null with no animation)
14266  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14267  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14268  * property for valid values (defaults to 'all')
14269  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14270  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14271  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14272  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14273  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14274  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14275  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14276  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14277  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14278  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14279  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14280  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14281  * draggable = true (defaults to false)
14282  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14283  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14284  * shadow (defaults to false)
14285  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14286  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14287  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14288  * @cfg {Array} buttons Array of buttons
14289  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14290  * @constructor
14291  * Create a new BasicDialog.
14292  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14293  * @param {Object} config Configuration options
14294  */
14295 Roo.BasicDialog = function(el, config){
14296     this.el = Roo.get(el);
14297     var dh = Roo.DomHelper;
14298     if(!this.el && config && config.autoCreate){
14299         if(typeof config.autoCreate == "object"){
14300             if(!config.autoCreate.id){
14301                 config.autoCreate.id = el;
14302             }
14303             this.el = dh.append(document.body,
14304                         config.autoCreate, true);
14305         }else{
14306             this.el = dh.append(document.body,
14307                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14308         }
14309     }
14310     el = this.el;
14311     el.setDisplayed(true);
14312     el.hide = this.hideAction;
14313     this.id = el.id;
14314     el.addClass("x-dlg");
14315
14316     Roo.apply(this, config);
14317
14318     this.proxy = el.createProxy("x-dlg-proxy");
14319     this.proxy.hide = this.hideAction;
14320     this.proxy.setOpacity(.5);
14321     this.proxy.hide();
14322
14323     if(config.width){
14324         el.setWidth(config.width);
14325     }
14326     if(config.height){
14327         el.setHeight(config.height);
14328     }
14329     this.size = el.getSize();
14330     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14331         this.xy = [config.x,config.y];
14332     }else{
14333         this.xy = el.getCenterXY(true);
14334     }
14335     /** The header element @type Roo.Element */
14336     this.header = el.child("> .x-dlg-hd");
14337     /** The body element @type Roo.Element */
14338     this.body = el.child("> .x-dlg-bd");
14339     /** The footer element @type Roo.Element */
14340     this.footer = el.child("> .x-dlg-ft");
14341
14342     if(!this.header){
14343         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14344     }
14345     if(!this.body){
14346         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14347     }
14348
14349     this.header.unselectable();
14350     if(this.title){
14351         this.header.update(this.title);
14352     }
14353     // this element allows the dialog to be focused for keyboard event
14354     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14355     this.focusEl.swallowEvent("click", true);
14356
14357     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14358
14359     // wrap the body and footer for special rendering
14360     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14361     if(this.footer){
14362         this.bwrap.dom.appendChild(this.footer.dom);
14363     }
14364
14365     this.bg = this.el.createChild({
14366         tag: "div", cls:"x-dlg-bg",
14367         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14368     });
14369     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14370
14371
14372     if(this.autoScroll !== false && !this.autoTabs){
14373         this.body.setStyle("overflow", "auto");
14374     }
14375
14376     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14377
14378     if(this.closable !== false){
14379         this.el.addClass("x-dlg-closable");
14380         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14381         this.close.on("click", this.closeClick, this);
14382         this.close.addClassOnOver("x-dlg-close-over");
14383     }
14384     if(this.collapsible !== false){
14385         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14386         this.collapseBtn.on("click", this.collapseClick, this);
14387         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14388         this.header.on("dblclick", this.collapseClick, this);
14389     }
14390     if(this.resizable !== false){
14391         this.el.addClass("x-dlg-resizable");
14392         this.resizer = new Roo.Resizable(el, {
14393             minWidth: this.minWidth || 80,
14394             minHeight:this.minHeight || 80,
14395             handles: this.resizeHandles || "all",
14396             pinned: true
14397         });
14398         this.resizer.on("beforeresize", this.beforeResize, this);
14399         this.resizer.on("resize", this.onResize, this);
14400     }
14401     if(this.draggable !== false){
14402         el.addClass("x-dlg-draggable");
14403         if (!this.proxyDrag) {
14404             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14405         }
14406         else {
14407             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14408         }
14409         dd.setHandleElId(this.header.id);
14410         dd.endDrag = this.endMove.createDelegate(this);
14411         dd.startDrag = this.startMove.createDelegate(this);
14412         dd.onDrag = this.onDrag.createDelegate(this);
14413         dd.scroll = false;
14414         this.dd = dd;
14415     }
14416     if(this.modal){
14417         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14418         this.mask.enableDisplayMode("block");
14419         this.mask.hide();
14420         this.el.addClass("x-dlg-modal");
14421     }
14422     if(this.shadow){
14423         this.shadow = new Roo.Shadow({
14424             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14425             offset : this.shadowOffset
14426         });
14427     }else{
14428         this.shadowOffset = 0;
14429     }
14430     if(Roo.useShims && this.shim !== false){
14431         this.shim = this.el.createShim();
14432         this.shim.hide = this.hideAction;
14433         this.shim.hide();
14434     }else{
14435         this.shim = false;
14436     }
14437     if(this.autoTabs){
14438         this.initTabs();
14439     }
14440     if (this.buttons) { 
14441         var bts= this.buttons;
14442         this.buttons = [];
14443         Roo.each(bts, function(b) {
14444             this.addButton(b);
14445         }, this);
14446     }
14447     
14448     
14449     this.addEvents({
14450         /**
14451          * @event keydown
14452          * Fires when a key is pressed
14453          * @param {Roo.BasicDialog} this
14454          * @param {Roo.EventObject} e
14455          */
14456         "keydown" : true,
14457         /**
14458          * @event move
14459          * Fires when this dialog is moved by the user.
14460          * @param {Roo.BasicDialog} this
14461          * @param {Number} x The new page X
14462          * @param {Number} y The new page Y
14463          */
14464         "move" : true,
14465         /**
14466          * @event resize
14467          * Fires when this dialog is resized by the user.
14468          * @param {Roo.BasicDialog} this
14469          * @param {Number} width The new width
14470          * @param {Number} height The new height
14471          */
14472         "resize" : true,
14473         /**
14474          * @event beforehide
14475          * Fires before this dialog is hidden.
14476          * @param {Roo.BasicDialog} this
14477          */
14478         "beforehide" : true,
14479         /**
14480          * @event hide
14481          * Fires when this dialog is hidden.
14482          * @param {Roo.BasicDialog} this
14483          */
14484         "hide" : true,
14485         /**
14486          * @event beforeshow
14487          * Fires before this dialog is shown.
14488          * @param {Roo.BasicDialog} this
14489          */
14490         "beforeshow" : true,
14491         /**
14492          * @event show
14493          * Fires when this dialog is shown.
14494          * @param {Roo.BasicDialog} this
14495          */
14496         "show" : true
14497     });
14498     el.on("keydown", this.onKeyDown, this);
14499     el.on("mousedown", this.toFront, this);
14500     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14501     this.el.hide();
14502     Roo.DialogManager.register(this);
14503     Roo.BasicDialog.superclass.constructor.call(this);
14504 };
14505
14506 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14507     shadowOffset: Roo.isIE ? 6 : 5,
14508     minHeight: 80,
14509     minWidth: 200,
14510     minButtonWidth: 75,
14511     defaultButton: null,
14512     buttonAlign: "right",
14513     tabTag: 'div',
14514     firstShow: true,
14515
14516     /**
14517      * Sets the dialog title text
14518      * @param {String} text The title text to display
14519      * @return {Roo.BasicDialog} this
14520      */
14521     setTitle : function(text){
14522         this.header.update(text);
14523         return this;
14524     },
14525
14526     // private
14527     closeClick : function(){
14528         this.hide();
14529     },
14530
14531     // private
14532     collapseClick : function(){
14533         this[this.collapsed ? "expand" : "collapse"]();
14534     },
14535
14536     /**
14537      * Collapses the dialog to its minimized state (only the title bar is visible).
14538      * Equivalent to the user clicking the collapse dialog button.
14539      */
14540     collapse : function(){
14541         if(!this.collapsed){
14542             this.collapsed = true;
14543             this.el.addClass("x-dlg-collapsed");
14544             this.restoreHeight = this.el.getHeight();
14545             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14546         }
14547     },
14548
14549     /**
14550      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14551      * clicking the expand dialog button.
14552      */
14553     expand : function(){
14554         if(this.collapsed){
14555             this.collapsed = false;
14556             this.el.removeClass("x-dlg-collapsed");
14557             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14558         }
14559     },
14560
14561     /**
14562      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14563      * @return {Roo.TabPanel} The tabs component
14564      */
14565     initTabs : function(){
14566         var tabs = this.getTabs();
14567         while(tabs.getTab(0)){
14568             tabs.removeTab(0);
14569         }
14570         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14571             var dom = el.dom;
14572             tabs.addTab(Roo.id(dom), dom.title);
14573             dom.title = "";
14574         });
14575         tabs.activate(0);
14576         return tabs;
14577     },
14578
14579     // private
14580     beforeResize : function(){
14581         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14582     },
14583
14584     // private
14585     onResize : function(){
14586         this.refreshSize();
14587         this.syncBodyHeight();
14588         this.adjustAssets();
14589         this.focus();
14590         this.fireEvent("resize", this, this.size.width, this.size.height);
14591     },
14592
14593     // private
14594     onKeyDown : function(e){
14595         if(this.isVisible()){
14596             this.fireEvent("keydown", this, e);
14597         }
14598     },
14599
14600     /**
14601      * Resizes the dialog.
14602      * @param {Number} width
14603      * @param {Number} height
14604      * @return {Roo.BasicDialog} this
14605      */
14606     resizeTo : function(width, height){
14607         this.el.setSize(width, height);
14608         this.size = {width: width, height: height};
14609         this.syncBodyHeight();
14610         if(this.fixedcenter){
14611             this.center();
14612         }
14613         if(this.isVisible()){
14614             this.constrainXY();
14615             this.adjustAssets();
14616         }
14617         this.fireEvent("resize", this, width, height);
14618         return this;
14619     },
14620
14621
14622     /**
14623      * Resizes the dialog to fit the specified content size.
14624      * @param {Number} width
14625      * @param {Number} height
14626      * @return {Roo.BasicDialog} this
14627      */
14628     setContentSize : function(w, h){
14629         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14630         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14631         //if(!this.el.isBorderBox()){
14632             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14633             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14634         //}
14635         if(this.tabs){
14636             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14637             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14638         }
14639         this.resizeTo(w, h);
14640         return this;
14641     },
14642
14643     /**
14644      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14645      * executed in response to a particular key being pressed while the dialog is active.
14646      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14647      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14648      * @param {Function} fn The function to call
14649      * @param {Object} scope (optional) The scope of the function
14650      * @return {Roo.BasicDialog} this
14651      */
14652     addKeyListener : function(key, fn, scope){
14653         var keyCode, shift, ctrl, alt;
14654         if(typeof key == "object" && !(key instanceof Array)){
14655             keyCode = key["key"];
14656             shift = key["shift"];
14657             ctrl = key["ctrl"];
14658             alt = key["alt"];
14659         }else{
14660             keyCode = key;
14661         }
14662         var handler = function(dlg, e){
14663             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14664                 var k = e.getKey();
14665                 if(keyCode instanceof Array){
14666                     for(var i = 0, len = keyCode.length; i < len; i++){
14667                         if(keyCode[i] == k){
14668                           fn.call(scope || window, dlg, k, e);
14669                           return;
14670                         }
14671                     }
14672                 }else{
14673                     if(k == keyCode){
14674                         fn.call(scope || window, dlg, k, e);
14675                     }
14676                 }
14677             }
14678         };
14679         this.on("keydown", handler);
14680         return this;
14681     },
14682
14683     /**
14684      * Returns the TabPanel component (creates it if it doesn't exist).
14685      * Note: If you wish to simply check for the existence of tabs without creating them,
14686      * check for a null 'tabs' property.
14687      * @return {Roo.TabPanel} The tabs component
14688      */
14689     getTabs : function(){
14690         if(!this.tabs){
14691             this.el.addClass("x-dlg-auto-tabs");
14692             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14693             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14694         }
14695         return this.tabs;
14696     },
14697
14698     /**
14699      * Adds a button to the footer section of the dialog.
14700      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14701      * object or a valid Roo.DomHelper element config
14702      * @param {Function} handler The function called when the button is clicked
14703      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14704      * @return {Roo.Button} The new button
14705      */
14706     addButton : function(config, handler, scope){
14707         var dh = Roo.DomHelper;
14708         if(!this.footer){
14709             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14710         }
14711         if(!this.btnContainer){
14712             var tb = this.footer.createChild({
14713
14714                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14715                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14716             }, null, true);
14717             this.btnContainer = tb.firstChild.firstChild.firstChild;
14718         }
14719         var bconfig = {
14720             handler: handler,
14721             scope: scope,
14722             minWidth: this.minButtonWidth,
14723             hideParent:true
14724         };
14725         if(typeof config == "string"){
14726             bconfig.text = config;
14727         }else{
14728             if(config.tag){
14729                 bconfig.dhconfig = config;
14730             }else{
14731                 Roo.apply(bconfig, config);
14732             }
14733         }
14734         var fc = false;
14735         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14736             bconfig.position = Math.max(0, bconfig.position);
14737             fc = this.btnContainer.childNodes[bconfig.position];
14738         }
14739          
14740         var btn = new Roo.Button(
14741             fc ? 
14742                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14743                 : this.btnContainer.appendChild(document.createElement("td")),
14744             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14745             bconfig
14746         );
14747         this.syncBodyHeight();
14748         if(!this.buttons){
14749             /**
14750              * Array of all the buttons that have been added to this dialog via addButton
14751              * @type Array
14752              */
14753             this.buttons = [];
14754         }
14755         this.buttons.push(btn);
14756         return btn;
14757     },
14758
14759     /**
14760      * Sets the default button to be focused when the dialog is displayed.
14761      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14762      * @return {Roo.BasicDialog} this
14763      */
14764     setDefaultButton : function(btn){
14765         this.defaultButton = btn;
14766         return this;
14767     },
14768
14769     // private
14770     getHeaderFooterHeight : function(safe){
14771         var height = 0;
14772         if(this.header){
14773            height += this.header.getHeight();
14774         }
14775         if(this.footer){
14776            var fm = this.footer.getMargins();
14777             height += (this.footer.getHeight()+fm.top+fm.bottom);
14778         }
14779         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14780         height += this.centerBg.getPadding("tb");
14781         return height;
14782     },
14783
14784     // private
14785     syncBodyHeight : function(){
14786         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14787         var height = this.size.height - this.getHeaderFooterHeight(false);
14788         bd.setHeight(height-bd.getMargins("tb"));
14789         var hh = this.header.getHeight();
14790         var h = this.size.height-hh;
14791         cb.setHeight(h);
14792         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14793         bw.setHeight(h-cb.getPadding("tb"));
14794         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14795         bd.setWidth(bw.getWidth(true));
14796         if(this.tabs){
14797             this.tabs.syncHeight();
14798             if(Roo.isIE){
14799                 this.tabs.el.repaint();
14800             }
14801         }
14802     },
14803
14804     /**
14805      * Restores the previous state of the dialog if Roo.state is configured.
14806      * @return {Roo.BasicDialog} this
14807      */
14808     restoreState : function(){
14809         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14810         if(box && box.width){
14811             this.xy = [box.x, box.y];
14812             this.resizeTo(box.width, box.height);
14813         }
14814         return this;
14815     },
14816
14817     // private
14818     beforeShow : function(){
14819         this.expand();
14820         if(this.fixedcenter){
14821             this.xy = this.el.getCenterXY(true);
14822         }
14823         if(this.modal){
14824             Roo.get(document.body).addClass("x-body-masked");
14825             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14826             this.mask.show();
14827         }
14828         this.constrainXY();
14829     },
14830
14831     // private
14832     animShow : function(){
14833         var b = Roo.get(this.animateTarget).getBox();
14834         this.proxy.setSize(b.width, b.height);
14835         this.proxy.setLocation(b.x, b.y);
14836         this.proxy.show();
14837         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14838                     true, .35, this.showEl.createDelegate(this));
14839     },
14840
14841     /**
14842      * Shows the dialog.
14843      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14844      * @return {Roo.BasicDialog} this
14845      */
14846     show : function(animateTarget){
14847         if (this.fireEvent("beforeshow", this) === false){
14848             return;
14849         }
14850         if(this.syncHeightBeforeShow){
14851             this.syncBodyHeight();
14852         }else if(this.firstShow){
14853             this.firstShow = false;
14854             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14855         }
14856         this.animateTarget = animateTarget || this.animateTarget;
14857         if(!this.el.isVisible()){
14858             this.beforeShow();
14859             if(this.animateTarget && Roo.get(this.animateTarget)){
14860                 this.animShow();
14861             }else{
14862                 this.showEl();
14863             }
14864         }
14865         return this;
14866     },
14867
14868     // private
14869     showEl : function(){
14870         this.proxy.hide();
14871         this.el.setXY(this.xy);
14872         this.el.show();
14873         this.adjustAssets(true);
14874         this.toFront();
14875         this.focus();
14876         // IE peekaboo bug - fix found by Dave Fenwick
14877         if(Roo.isIE){
14878             this.el.repaint();
14879         }
14880         this.fireEvent("show", this);
14881     },
14882
14883     /**
14884      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14885      * dialog itself will receive focus.
14886      */
14887     focus : function(){
14888         if(this.defaultButton){
14889             this.defaultButton.focus();
14890         }else{
14891             this.focusEl.focus();
14892         }
14893     },
14894
14895     // private
14896     constrainXY : function(){
14897         if(this.constraintoviewport !== false){
14898             if(!this.viewSize){
14899                 if(this.container){
14900                     var s = this.container.getSize();
14901                     this.viewSize = [s.width, s.height];
14902                 }else{
14903                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14904                 }
14905             }
14906             var s = Roo.get(this.container||document).getScroll();
14907
14908             var x = this.xy[0], y = this.xy[1];
14909             var w = this.size.width, h = this.size.height;
14910             var vw = this.viewSize[0], vh = this.viewSize[1];
14911             // only move it if it needs it
14912             var moved = false;
14913             // first validate right/bottom
14914             if(x + w > vw+s.left){
14915                 x = vw - w;
14916                 moved = true;
14917             }
14918             if(y + h > vh+s.top){
14919                 y = vh - h;
14920                 moved = true;
14921             }
14922             // then make sure top/left isn't negative
14923             if(x < s.left){
14924                 x = s.left;
14925                 moved = true;
14926             }
14927             if(y < s.top){
14928                 y = s.top;
14929                 moved = true;
14930             }
14931             if(moved){
14932                 // cache xy
14933                 this.xy = [x, y];
14934                 if(this.isVisible()){
14935                     this.el.setLocation(x, y);
14936                     this.adjustAssets();
14937                 }
14938             }
14939         }
14940     },
14941
14942     // private
14943     onDrag : function(){
14944         if(!this.proxyDrag){
14945             this.xy = this.el.getXY();
14946             this.adjustAssets();
14947         }
14948     },
14949
14950     // private
14951     adjustAssets : function(doShow){
14952         var x = this.xy[0], y = this.xy[1];
14953         var w = this.size.width, h = this.size.height;
14954         if(doShow === true){
14955             if(this.shadow){
14956                 this.shadow.show(this.el);
14957             }
14958             if(this.shim){
14959                 this.shim.show();
14960             }
14961         }
14962         if(this.shadow && this.shadow.isVisible()){
14963             this.shadow.show(this.el);
14964         }
14965         if(this.shim && this.shim.isVisible()){
14966             this.shim.setBounds(x, y, w, h);
14967         }
14968     },
14969
14970     // private
14971     adjustViewport : function(w, h){
14972         if(!w || !h){
14973             w = Roo.lib.Dom.getViewWidth();
14974             h = Roo.lib.Dom.getViewHeight();
14975         }
14976         // cache the size
14977         this.viewSize = [w, h];
14978         if(this.modal && this.mask.isVisible()){
14979             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14980             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14981         }
14982         if(this.isVisible()){
14983             this.constrainXY();
14984         }
14985     },
14986
14987     /**
14988      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14989      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14990      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14991      */
14992     destroy : function(removeEl){
14993         if(this.isVisible()){
14994             this.animateTarget = null;
14995             this.hide();
14996         }
14997         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14998         if(this.tabs){
14999             this.tabs.destroy(removeEl);
15000         }
15001         Roo.destroy(
15002              this.shim,
15003              this.proxy,
15004              this.resizer,
15005              this.close,
15006              this.mask
15007         );
15008         if(this.dd){
15009             this.dd.unreg();
15010         }
15011         if(this.buttons){
15012            for(var i = 0, len = this.buttons.length; i < len; i++){
15013                this.buttons[i].destroy();
15014            }
15015         }
15016         this.el.removeAllListeners();
15017         if(removeEl === true){
15018             this.el.update("");
15019             this.el.remove();
15020         }
15021         Roo.DialogManager.unregister(this);
15022     },
15023
15024     // private
15025     startMove : function(){
15026         if(this.proxyDrag){
15027             this.proxy.show();
15028         }
15029         if(this.constraintoviewport !== false){
15030             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15031         }
15032     },
15033
15034     // private
15035     endMove : function(){
15036         if(!this.proxyDrag){
15037             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15038         }else{
15039             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15040             this.proxy.hide();
15041         }
15042         this.refreshSize();
15043         this.adjustAssets();
15044         this.focus();
15045         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15046     },
15047
15048     /**
15049      * Brings this dialog to the front of any other visible dialogs
15050      * @return {Roo.BasicDialog} this
15051      */
15052     toFront : function(){
15053         Roo.DialogManager.bringToFront(this);
15054         return this;
15055     },
15056
15057     /**
15058      * Sends this dialog to the back (under) of any other visible dialogs
15059      * @return {Roo.BasicDialog} this
15060      */
15061     toBack : function(){
15062         Roo.DialogManager.sendToBack(this);
15063         return this;
15064     },
15065
15066     /**
15067      * Centers this dialog in the viewport
15068      * @return {Roo.BasicDialog} this
15069      */
15070     center : function(){
15071         var xy = this.el.getCenterXY(true);
15072         this.moveTo(xy[0], xy[1]);
15073         return this;
15074     },
15075
15076     /**
15077      * Moves the dialog's top-left corner to the specified point
15078      * @param {Number} x
15079      * @param {Number} y
15080      * @return {Roo.BasicDialog} this
15081      */
15082     moveTo : function(x, y){
15083         this.xy = [x,y];
15084         if(this.isVisible()){
15085             this.el.setXY(this.xy);
15086             this.adjustAssets();
15087         }
15088         return this;
15089     },
15090
15091     /**
15092      * Aligns the dialog to the specified element
15093      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15094      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15095      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15096      * @return {Roo.BasicDialog} this
15097      */
15098     alignTo : function(element, position, offsets){
15099         this.xy = this.el.getAlignToXY(element, position, offsets);
15100         if(this.isVisible()){
15101             this.el.setXY(this.xy);
15102             this.adjustAssets();
15103         }
15104         return this;
15105     },
15106
15107     /**
15108      * Anchors an element to another element and realigns it when the window is resized.
15109      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15110      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15111      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15112      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15113      * is a number, it is used as the buffer delay (defaults to 50ms).
15114      * @return {Roo.BasicDialog} this
15115      */
15116     anchorTo : function(el, alignment, offsets, monitorScroll){
15117         var action = function(){
15118             this.alignTo(el, alignment, offsets);
15119         };
15120         Roo.EventManager.onWindowResize(action, this);
15121         var tm = typeof monitorScroll;
15122         if(tm != 'undefined'){
15123             Roo.EventManager.on(window, 'scroll', action, this,
15124                 {buffer: tm == 'number' ? monitorScroll : 50});
15125         }
15126         action.call(this);
15127         return this;
15128     },
15129
15130     /**
15131      * Returns true if the dialog is visible
15132      * @return {Boolean}
15133      */
15134     isVisible : function(){
15135         return this.el.isVisible();
15136     },
15137
15138     // private
15139     animHide : function(callback){
15140         var b = Roo.get(this.animateTarget).getBox();
15141         this.proxy.show();
15142         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15143         this.el.hide();
15144         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15145                     this.hideEl.createDelegate(this, [callback]));
15146     },
15147
15148     /**
15149      * Hides the dialog.
15150      * @param {Function} callback (optional) Function to call when the dialog is hidden
15151      * @return {Roo.BasicDialog} this
15152      */
15153     hide : function(callback){
15154         if (this.fireEvent("beforehide", this) === false){
15155             return;
15156         }
15157         if(this.shadow){
15158             this.shadow.hide();
15159         }
15160         if(this.shim) {
15161           this.shim.hide();
15162         }
15163         // sometimes animateTarget seems to get set.. causing problems...
15164         // this just double checks..
15165         if(this.animateTarget && Roo.get(this.animateTarget)) {
15166            this.animHide(callback);
15167         }else{
15168             this.el.hide();
15169             this.hideEl(callback);
15170         }
15171         return this;
15172     },
15173
15174     // private
15175     hideEl : function(callback){
15176         this.proxy.hide();
15177         if(this.modal){
15178             this.mask.hide();
15179             Roo.get(document.body).removeClass("x-body-masked");
15180         }
15181         this.fireEvent("hide", this);
15182         if(typeof callback == "function"){
15183             callback();
15184         }
15185     },
15186
15187     // private
15188     hideAction : function(){
15189         this.setLeft("-10000px");
15190         this.setTop("-10000px");
15191         this.setStyle("visibility", "hidden");
15192     },
15193
15194     // private
15195     refreshSize : function(){
15196         this.size = this.el.getSize();
15197         this.xy = this.el.getXY();
15198         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15199     },
15200
15201     // private
15202     // z-index is managed by the DialogManager and may be overwritten at any time
15203     setZIndex : function(index){
15204         if(this.modal){
15205             this.mask.setStyle("z-index", index);
15206         }
15207         if(this.shim){
15208             this.shim.setStyle("z-index", ++index);
15209         }
15210         if(this.shadow){
15211             this.shadow.setZIndex(++index);
15212         }
15213         this.el.setStyle("z-index", ++index);
15214         if(this.proxy){
15215             this.proxy.setStyle("z-index", ++index);
15216         }
15217         if(this.resizer){
15218             this.resizer.proxy.setStyle("z-index", ++index);
15219         }
15220
15221         this.lastZIndex = index;
15222     },
15223
15224     /**
15225      * Returns the element for this dialog
15226      * @return {Roo.Element} The underlying dialog Element
15227      */
15228     getEl : function(){
15229         return this.el;
15230     }
15231 });
15232
15233 /**
15234  * @class Roo.DialogManager
15235  * Provides global access to BasicDialogs that have been created and
15236  * support for z-indexing (layering) multiple open dialogs.
15237  */
15238 Roo.DialogManager = function(){
15239     var list = {};
15240     var accessList = [];
15241     var front = null;
15242
15243     // private
15244     var sortDialogs = function(d1, d2){
15245         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15246     };
15247
15248     // private
15249     var orderDialogs = function(){
15250         accessList.sort(sortDialogs);
15251         var seed = Roo.DialogManager.zseed;
15252         for(var i = 0, len = accessList.length; i < len; i++){
15253             var dlg = accessList[i];
15254             if(dlg){
15255                 dlg.setZIndex(seed + (i*10));
15256             }
15257         }
15258     };
15259
15260     return {
15261         /**
15262          * The starting z-index for BasicDialogs (defaults to 9000)
15263          * @type Number The z-index value
15264          */
15265         zseed : 9000,
15266
15267         // private
15268         register : function(dlg){
15269             list[dlg.id] = dlg;
15270             accessList.push(dlg);
15271         },
15272
15273         // private
15274         unregister : function(dlg){
15275             delete list[dlg.id];
15276             var i=0;
15277             var len=0;
15278             if(!accessList.indexOf){
15279                 for(  i = 0, len = accessList.length; i < len; i++){
15280                     if(accessList[i] == dlg){
15281                         accessList.splice(i, 1);
15282                         return;
15283                     }
15284                 }
15285             }else{
15286                  i = accessList.indexOf(dlg);
15287                 if(i != -1){
15288                     accessList.splice(i, 1);
15289                 }
15290             }
15291         },
15292
15293         /**
15294          * Gets a registered dialog by id
15295          * @param {String/Object} id The id of the dialog or a dialog
15296          * @return {Roo.BasicDialog} this
15297          */
15298         get : function(id){
15299             return typeof id == "object" ? id : list[id];
15300         },
15301
15302         /**
15303          * Brings the specified dialog to the front
15304          * @param {String/Object} dlg The id of the dialog or a dialog
15305          * @return {Roo.BasicDialog} this
15306          */
15307         bringToFront : function(dlg){
15308             dlg = this.get(dlg);
15309             if(dlg != front){
15310                 front = dlg;
15311                 dlg._lastAccess = new Date().getTime();
15312                 orderDialogs();
15313             }
15314             return dlg;
15315         },
15316
15317         /**
15318          * Sends the specified dialog to the back
15319          * @param {String/Object} dlg The id of the dialog or a dialog
15320          * @return {Roo.BasicDialog} this
15321          */
15322         sendToBack : function(dlg){
15323             dlg = this.get(dlg);
15324             dlg._lastAccess = -(new Date().getTime());
15325             orderDialogs();
15326             return dlg;
15327         },
15328
15329         /**
15330          * Hides all dialogs
15331          */
15332         hideAll : function(){
15333             for(var id in list){
15334                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15335                     list[id].hide();
15336                 }
15337             }
15338         }
15339     };
15340 }();
15341
15342 /**
15343  * @class Roo.LayoutDialog
15344  * @extends Roo.BasicDialog
15345  * Dialog which provides adjustments for working with a layout in a Dialog.
15346  * Add your necessary layout config options to the dialog's config.<br>
15347  * Example usage (including a nested layout):
15348  * <pre><code>
15349 if(!dialog){
15350     dialog = new Roo.LayoutDialog("download-dlg", {
15351         modal: true,
15352         width:600,
15353         height:450,
15354         shadow:true,
15355         minWidth:500,
15356         minHeight:350,
15357         autoTabs:true,
15358         proxyDrag:true,
15359         // layout config merges with the dialog config
15360         center:{
15361             tabPosition: "top",
15362             alwaysShowTabs: true
15363         }
15364     });
15365     dialog.addKeyListener(27, dialog.hide, dialog);
15366     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15367     dialog.addButton("Build It!", this.getDownload, this);
15368
15369     // we can even add nested layouts
15370     var innerLayout = new Roo.BorderLayout("dl-inner", {
15371         east: {
15372             initialSize: 200,
15373             autoScroll:true,
15374             split:true
15375         },
15376         center: {
15377             autoScroll:true
15378         }
15379     });
15380     innerLayout.beginUpdate();
15381     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15382     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15383     innerLayout.endUpdate(true);
15384
15385     var layout = dialog.getLayout();
15386     layout.beginUpdate();
15387     layout.add("center", new Roo.ContentPanel("standard-panel",
15388                         {title: "Download the Source", fitToFrame:true}));
15389     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15390                {title: "Build your own roo.js"}));
15391     layout.getRegion("center").showPanel(sp);
15392     layout.endUpdate();
15393 }
15394 </code></pre>
15395     * @constructor
15396     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15397     * @param {Object} config configuration options
15398   */
15399 Roo.LayoutDialog = function(el, cfg){
15400     
15401     var config=  cfg;
15402     if (typeof(cfg) == 'undefined') {
15403         config = Roo.apply({}, el);
15404         // not sure why we use documentElement here.. - it should always be body.
15405         // IE7 borks horribly if we use documentElement.
15406         // webkit also does not like documentElement - it creates a body element...
15407         el = Roo.get( document.body || document.documentElement ).createChild();
15408         //config.autoCreate = true;
15409     }
15410     
15411     
15412     config.autoTabs = false;
15413     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15414     this.body.setStyle({overflow:"hidden", position:"relative"});
15415     this.layout = new Roo.BorderLayout(this.body.dom, config);
15416     this.layout.monitorWindowResize = false;
15417     this.el.addClass("x-dlg-auto-layout");
15418     // fix case when center region overwrites center function
15419     this.center = Roo.BasicDialog.prototype.center;
15420     this.on("show", this.layout.layout, this.layout, true);
15421     if (config.items) {
15422         var xitems = config.items;
15423         delete config.items;
15424         Roo.each(xitems, this.addxtype, this);
15425     }
15426     
15427     
15428 };
15429 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15430     /**
15431      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15432      * @deprecated
15433      */
15434     endUpdate : function(){
15435         this.layout.endUpdate();
15436     },
15437
15438     /**
15439      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15440      *  @deprecated
15441      */
15442     beginUpdate : function(){
15443         this.layout.beginUpdate();
15444     },
15445
15446     /**
15447      * Get the BorderLayout for this dialog
15448      * @return {Roo.BorderLayout}
15449      */
15450     getLayout : function(){
15451         return this.layout;
15452     },
15453
15454     showEl : function(){
15455         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15456         if(Roo.isIE7){
15457             this.layout.layout();
15458         }
15459     },
15460
15461     // private
15462     // Use the syncHeightBeforeShow config option to control this automatically
15463     syncBodyHeight : function(){
15464         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15465         if(this.layout){this.layout.layout();}
15466     },
15467     
15468       /**
15469      * Add an xtype element (actually adds to the layout.)
15470      * @return {Object} xdata xtype object data.
15471      */
15472     
15473     addxtype : function(c) {
15474         return this.layout.addxtype(c);
15475     }
15476 });/*
15477  * Based on:
15478  * Ext JS Library 1.1.1
15479  * Copyright(c) 2006-2007, Ext JS, LLC.
15480  *
15481  * Originally Released Under LGPL - original licence link has changed is not relivant.
15482  *
15483  * Fork - LGPL
15484  * <script type="text/javascript">
15485  */
15486  
15487 /**
15488  * @class Roo.MessageBox
15489  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15490  * Example usage:
15491  *<pre><code>
15492 // Basic alert:
15493 Roo.Msg.alert('Status', 'Changes saved successfully.');
15494
15495 // Prompt for user data:
15496 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15497     if (btn == 'ok'){
15498         // process text value...
15499     }
15500 });
15501
15502 // Show a dialog using config options:
15503 Roo.Msg.show({
15504    title:'Save Changes?',
15505    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15506    buttons: Roo.Msg.YESNOCANCEL,
15507    fn: processResult,
15508    animEl: 'elId'
15509 });
15510 </code></pre>
15511  * @singleton
15512  */
15513 Roo.MessageBox = function(){
15514     var dlg, opt, mask, waitTimer;
15515     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15516     var buttons, activeTextEl, bwidth;
15517
15518     // private
15519     var handleButton = function(button){
15520         dlg.hide();
15521         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15522     };
15523
15524     // private
15525     var handleHide = function(){
15526         if(opt && opt.cls){
15527             dlg.el.removeClass(opt.cls);
15528         }
15529         if(waitTimer){
15530             Roo.TaskMgr.stop(waitTimer);
15531             waitTimer = null;
15532         }
15533     };
15534
15535     // private
15536     var updateButtons = function(b){
15537         var width = 0;
15538         if(!b){
15539             buttons["ok"].hide();
15540             buttons["cancel"].hide();
15541             buttons["yes"].hide();
15542             buttons["no"].hide();
15543             dlg.footer.dom.style.display = 'none';
15544             return width;
15545         }
15546         dlg.footer.dom.style.display = '';
15547         for(var k in buttons){
15548             if(typeof buttons[k] != "function"){
15549                 if(b[k]){
15550                     buttons[k].show();
15551                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15552                     width += buttons[k].el.getWidth()+15;
15553                 }else{
15554                     buttons[k].hide();
15555                 }
15556             }
15557         }
15558         return width;
15559     };
15560
15561     // private
15562     var handleEsc = function(d, k, e){
15563         if(opt && opt.closable !== false){
15564             dlg.hide();
15565         }
15566         if(e){
15567             e.stopEvent();
15568         }
15569     };
15570
15571     return {
15572         /**
15573          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15574          * @return {Roo.BasicDialog} The BasicDialog element
15575          */
15576         getDialog : function(){
15577            if(!dlg){
15578                 dlg = new Roo.BasicDialog("x-msg-box", {
15579                     autoCreate : true,
15580                     shadow: true,
15581                     draggable: true,
15582                     resizable:false,
15583                     constraintoviewport:false,
15584                     fixedcenter:true,
15585                     collapsible : false,
15586                     shim:true,
15587                     modal: true,
15588                     width:400, height:100,
15589                     buttonAlign:"center",
15590                     closeClick : function(){
15591                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15592                             handleButton("no");
15593                         }else{
15594                             handleButton("cancel");
15595                         }
15596                     }
15597                 });
15598                 dlg.on("hide", handleHide);
15599                 mask = dlg.mask;
15600                 dlg.addKeyListener(27, handleEsc);
15601                 buttons = {};
15602                 var bt = this.buttonText;
15603                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15604                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15605                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15606                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15607                 bodyEl = dlg.body.createChild({
15608
15609                     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>'
15610                 });
15611                 msgEl = bodyEl.dom.firstChild;
15612                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15613                 textboxEl.enableDisplayMode();
15614                 textboxEl.addKeyListener([10,13], function(){
15615                     if(dlg.isVisible() && opt && opt.buttons){
15616                         if(opt.buttons.ok){
15617                             handleButton("ok");
15618                         }else if(opt.buttons.yes){
15619                             handleButton("yes");
15620                         }
15621                     }
15622                 });
15623                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15624                 textareaEl.enableDisplayMode();
15625                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15626                 progressEl.enableDisplayMode();
15627                 var pf = progressEl.dom.firstChild;
15628                 if (pf) {
15629                     pp = Roo.get(pf.firstChild);
15630                     pp.setHeight(pf.offsetHeight);
15631                 }
15632                 
15633             }
15634             return dlg;
15635         },
15636
15637         /**
15638          * Updates the message box body text
15639          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15640          * the XHTML-compliant non-breaking space character '&amp;#160;')
15641          * @return {Roo.MessageBox} This message box
15642          */
15643         updateText : function(text){
15644             if(!dlg.isVisible() && !opt.width){
15645                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15646             }
15647             msgEl.innerHTML = text || '&#160;';
15648             
15649             var w = Math.max(
15650                     Math.min(opt.width || Math.max(msgEl.offsetWidth, msgEl.scrollWidth), this.maxWidth), 
15651                     Math.max(opt.minWidth || this.minWidth, bwidth)
15652             );
15653             if(opt.prompt){
15654                 activeTextEl.setWidth(w);
15655             }
15656             if(dlg.isVisible()){
15657                 dlg.fixedcenter = false;
15658             }
15659             // to big, make it scoll.
15660             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15661                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15662                 bodyEl.dom.style.overflowY = 'auto !important';
15663             } else {
15664                 bodyEl.dom.style.height = '';
15665                 bodyEl.dom.style.overflowY = '';
15666             }
15667             
15668             dlg.setContentSize(w, bodyEl.getHeight());
15669             if(dlg.isVisible()){
15670                 dlg.fixedcenter = true;
15671             }
15672             return this;
15673         },
15674
15675         /**
15676          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15677          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15678          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15679          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15680          * @return {Roo.MessageBox} This message box
15681          */
15682         updateProgress : function(value, text){
15683             if(text){
15684                 this.updateText(text);
15685             }
15686             if (pp) { // weird bug on my firefox - for some reason this is not defined
15687                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15688             }
15689             return this;
15690         },        
15691
15692         /**
15693          * Returns true if the message box is currently displayed
15694          * @return {Boolean} True if the message box is visible, else false
15695          */
15696         isVisible : function(){
15697             return dlg && dlg.isVisible();  
15698         },
15699
15700         /**
15701          * Hides the message box if it is displayed
15702          */
15703         hide : function(){
15704             if(this.isVisible()){
15705                 dlg.hide();
15706             }  
15707         },
15708
15709         /**
15710          * Displays a new message box, or reinitializes an existing message box, based on the config options
15711          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15712          * The following config object properties are supported:
15713          * <pre>
15714 Property    Type             Description
15715 ----------  ---------------  ------------------------------------------------------------------------------------
15716 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15717                                    closes (defaults to undefined)
15718 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15719                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15720 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15721                                    progress and wait dialogs will ignore this property and always hide the
15722                                    close button as they can only be closed programmatically.
15723 cls               String           A custom CSS class to apply to the message box element
15724 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15725                                    displayed (defaults to 75)
15726 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15727                                    function will be btn (the name of the button that was clicked, if applicable,
15728                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15729                                    Progress and wait dialogs will ignore this option since they do not respond to
15730                                    user actions and can only be closed programmatically, so any required function
15731                                    should be called by the same code after it closes the dialog.
15732 icon              String           A CSS class that provides a background image to be used as an icon for
15733                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15734 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15735 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15736 modal             Boolean          False to allow user interaction with the page while the message box is
15737                                    displayed (defaults to true)
15738 msg               String           A string that will replace the existing message box body text (defaults
15739                                    to the XHTML-compliant non-breaking space character '&#160;')
15740 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15741 progress          Boolean          True to display a progress bar (defaults to false)
15742 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15743 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15744 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15745 title             String           The title text
15746 value             String           The string value to set into the active textbox element if displayed
15747 wait              Boolean          True to display a progress bar (defaults to false)
15748 width             Number           The width of the dialog in pixels
15749 </pre>
15750          *
15751          * Example usage:
15752          * <pre><code>
15753 Roo.Msg.show({
15754    title: 'Address',
15755    msg: 'Please enter your address:',
15756    width: 300,
15757    buttons: Roo.MessageBox.OKCANCEL,
15758    multiline: true,
15759    fn: saveAddress,
15760    animEl: 'addAddressBtn'
15761 });
15762 </code></pre>
15763          * @param {Object} config Configuration options
15764          * @return {Roo.MessageBox} This message box
15765          */
15766         show : function(options)
15767         {
15768             
15769             // this causes nightmares if you show one dialog after another
15770             // especially on callbacks..
15771              
15772             if(this.isVisible()){
15773                 
15774                 this.hide();
15775                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
15776                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15777                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15778                 
15779             }
15780             var d = this.getDialog();
15781             opt = options;
15782             d.setTitle(opt.title || "&#160;");
15783             d.close.setDisplayed(opt.closable !== false);
15784             activeTextEl = textboxEl;
15785             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15786             if(opt.prompt){
15787                 if(opt.multiline){
15788                     textboxEl.hide();
15789                     textareaEl.show();
15790                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15791                         opt.multiline : this.defaultTextHeight);
15792                     activeTextEl = textareaEl;
15793                 }else{
15794                     textboxEl.show();
15795                     textareaEl.hide();
15796                 }
15797             }else{
15798                 textboxEl.hide();
15799                 textareaEl.hide();
15800             }
15801             progressEl.setDisplayed(opt.progress === true);
15802             this.updateProgress(0);
15803             activeTextEl.dom.value = opt.value || "";
15804             if(opt.prompt){
15805                 dlg.setDefaultButton(activeTextEl);
15806             }else{
15807                 var bs = opt.buttons;
15808                 var db = null;
15809                 if(bs && bs.ok){
15810                     db = buttons["ok"];
15811                 }else if(bs && bs.yes){
15812                     db = buttons["yes"];
15813                 }
15814                 dlg.setDefaultButton(db);
15815             }
15816             bwidth = updateButtons(opt.buttons);
15817             this.updateText(opt.msg);
15818             if(opt.cls){
15819                 d.el.addClass(opt.cls);
15820             }
15821             d.proxyDrag = opt.proxyDrag === true;
15822             d.modal = opt.modal !== false;
15823             d.mask = opt.modal !== false ? mask : false;
15824             if(!d.isVisible()){
15825                 // force it to the end of the z-index stack so it gets a cursor in FF
15826                 document.body.appendChild(dlg.el.dom);
15827                 d.animateTarget = null;
15828                 d.show(options.animEl);
15829             }
15830             return this;
15831         },
15832
15833         /**
15834          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15835          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15836          * and closing the message box when the process is complete.
15837          * @param {String} title The title bar text
15838          * @param {String} msg The message box body text
15839          * @return {Roo.MessageBox} This message box
15840          */
15841         progress : function(title, msg){
15842             this.show({
15843                 title : title,
15844                 msg : msg,
15845                 buttons: false,
15846                 progress:true,
15847                 closable:false,
15848                 minWidth: this.minProgressWidth,
15849                 modal : true
15850             });
15851             return this;
15852         },
15853
15854         /**
15855          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15856          * If a callback function is passed it will be called after the user clicks the button, and the
15857          * id of the button that was clicked will be passed as the only parameter to the callback
15858          * (could also be the top-right close button).
15859          * @param {String} title The title bar text
15860          * @param {String} msg The message box body text
15861          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15862          * @param {Object} scope (optional) The scope of the callback function
15863          * @return {Roo.MessageBox} This message box
15864          */
15865         alert : function(title, msg, fn, scope){
15866             this.show({
15867                 title : title,
15868                 msg : msg,
15869                 buttons: this.OK,
15870                 fn: fn,
15871                 scope : scope,
15872                 modal : true
15873             });
15874             return this;
15875         },
15876
15877         /**
15878          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15879          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15880          * You are responsible for closing the message box when the process is complete.
15881          * @param {String} msg The message box body text
15882          * @param {String} title (optional) The title bar text
15883          * @return {Roo.MessageBox} This message box
15884          */
15885         wait : function(msg, title){
15886             this.show({
15887                 title : title,
15888                 msg : msg,
15889                 buttons: false,
15890                 closable:false,
15891                 progress:true,
15892                 modal:true,
15893                 width:300,
15894                 wait:true
15895             });
15896             waitTimer = Roo.TaskMgr.start({
15897                 run: function(i){
15898                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15899                 },
15900                 interval: 1000
15901             });
15902             return this;
15903         },
15904
15905         /**
15906          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15907          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15908          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15909          * @param {String} title The title bar text
15910          * @param {String} msg The message box body text
15911          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15912          * @param {Object} scope (optional) The scope of the callback function
15913          * @return {Roo.MessageBox} This message box
15914          */
15915         confirm : function(title, msg, fn, scope){
15916             this.show({
15917                 title : title,
15918                 msg : msg,
15919                 buttons: this.YESNO,
15920                 fn: fn,
15921                 scope : scope,
15922                 modal : true
15923             });
15924             return this;
15925         },
15926
15927         /**
15928          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15929          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15930          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15931          * (could also be the top-right close button) and the text that was entered will be passed as the two
15932          * parameters to the callback.
15933          * @param {String} title The title bar text
15934          * @param {String} msg The message box body text
15935          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15936          * @param {Object} scope (optional) The scope of the callback function
15937          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15938          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15939          * @return {Roo.MessageBox} This message box
15940          */
15941         prompt : function(title, msg, fn, scope, multiline){
15942             this.show({
15943                 title : title,
15944                 msg : msg,
15945                 buttons: this.OKCANCEL,
15946                 fn: fn,
15947                 minWidth:250,
15948                 scope : scope,
15949                 prompt:true,
15950                 multiline: multiline,
15951                 modal : true
15952             });
15953             return this;
15954         },
15955
15956         /**
15957          * Button config that displays a single OK button
15958          * @type Object
15959          */
15960         OK : {ok:true},
15961         /**
15962          * Button config that displays Yes and No buttons
15963          * @type Object
15964          */
15965         YESNO : {yes:true, no:true},
15966         /**
15967          * Button config that displays OK and Cancel buttons
15968          * @type Object
15969          */
15970         OKCANCEL : {ok:true, cancel:true},
15971         /**
15972          * Button config that displays Yes, No and Cancel buttons
15973          * @type Object
15974          */
15975         YESNOCANCEL : {yes:true, no:true, cancel:true},
15976
15977         /**
15978          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15979          * @type Number
15980          */
15981         defaultTextHeight : 75,
15982         /**
15983          * The maximum width in pixels of the message box (defaults to 600)
15984          * @type Number
15985          */
15986         maxWidth : 600,
15987         /**
15988          * The minimum width in pixels of the message box (defaults to 100)
15989          * @type Number
15990          */
15991         minWidth : 100,
15992         /**
15993          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15994          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15995          * @type Number
15996          */
15997         minProgressWidth : 250,
15998         /**
15999          * An object containing the default button text strings that can be overriden for localized language support.
16000          * Supported properties are: ok, cancel, yes and no.
16001          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16002          * @type Object
16003          */
16004         buttonText : {
16005             ok : "OK",
16006             cancel : "Cancel",
16007             yes : "Yes",
16008             no : "No"
16009         }
16010     };
16011 }();
16012
16013 /**
16014  * Shorthand for {@link Roo.MessageBox}
16015  */
16016 Roo.Msg = Roo.MessageBox;/*
16017  * Based on:
16018  * Ext JS Library 1.1.1
16019  * Copyright(c) 2006-2007, Ext JS, LLC.
16020  *
16021  * Originally Released Under LGPL - original licence link has changed is not relivant.
16022  *
16023  * Fork - LGPL
16024  * <script type="text/javascript">
16025  */
16026 /**
16027  * @class Roo.QuickTips
16028  * Provides attractive and customizable tooltips for any element.
16029  * @singleton
16030  */
16031 Roo.QuickTips = function(){
16032     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16033     var ce, bd, xy, dd;
16034     var visible = false, disabled = true, inited = false;
16035     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16036     
16037     var onOver = function(e){
16038         if(disabled){
16039             return;
16040         }
16041         var t = e.getTarget();
16042         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16043             return;
16044         }
16045         if(ce && t == ce.el){
16046             clearTimeout(hideProc);
16047             return;
16048         }
16049         if(t && tagEls[t.id]){
16050             tagEls[t.id].el = t;
16051             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16052             return;
16053         }
16054         var ttp, et = Roo.fly(t);
16055         var ns = cfg.namespace;
16056         if(tm.interceptTitles && t.title){
16057             ttp = t.title;
16058             t.qtip = ttp;
16059             t.removeAttribute("title");
16060             e.preventDefault();
16061         }else{
16062             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16063         }
16064         if(ttp){
16065             showProc = show.defer(tm.showDelay, tm, [{
16066                 el: t, 
16067                 text: ttp, 
16068                 width: et.getAttributeNS(ns, cfg.width),
16069                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16070                 title: et.getAttributeNS(ns, cfg.title),
16071                     cls: et.getAttributeNS(ns, cfg.cls)
16072             }]);
16073         }
16074     };
16075     
16076     var onOut = function(e){
16077         clearTimeout(showProc);
16078         var t = e.getTarget();
16079         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16080             hideProc = setTimeout(hide, tm.hideDelay);
16081         }
16082     };
16083     
16084     var onMove = function(e){
16085         if(disabled){
16086             return;
16087         }
16088         xy = e.getXY();
16089         xy[1] += 18;
16090         if(tm.trackMouse && ce){
16091             el.setXY(xy);
16092         }
16093     };
16094     
16095     var onDown = function(e){
16096         clearTimeout(showProc);
16097         clearTimeout(hideProc);
16098         if(!e.within(el)){
16099             if(tm.hideOnClick){
16100                 hide();
16101                 tm.disable();
16102                 tm.enable.defer(100, tm);
16103             }
16104         }
16105     };
16106     
16107     var getPad = function(){
16108         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16109     };
16110
16111     var show = function(o){
16112         if(disabled){
16113             return;
16114         }
16115         clearTimeout(dismissProc);
16116         ce = o;
16117         if(removeCls){ // in case manually hidden
16118             el.removeClass(removeCls);
16119             removeCls = null;
16120         }
16121         if(ce.cls){
16122             el.addClass(ce.cls);
16123             removeCls = ce.cls;
16124         }
16125         if(ce.title){
16126             tipTitle.update(ce.title);
16127             tipTitle.show();
16128         }else{
16129             tipTitle.update('');
16130             tipTitle.hide();
16131         }
16132         el.dom.style.width  = tm.maxWidth+'px';
16133         //tipBody.dom.style.width = '';
16134         tipBodyText.update(o.text);
16135         var p = getPad(), w = ce.width;
16136         if(!w){
16137             var td = tipBodyText.dom;
16138             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16139             if(aw > tm.maxWidth){
16140                 w = tm.maxWidth;
16141             }else if(aw < tm.minWidth){
16142                 w = tm.minWidth;
16143             }else{
16144                 w = aw;
16145             }
16146         }
16147         //tipBody.setWidth(w);
16148         el.setWidth(parseInt(w, 10) + p);
16149         if(ce.autoHide === false){
16150             close.setDisplayed(true);
16151             if(dd){
16152                 dd.unlock();
16153             }
16154         }else{
16155             close.setDisplayed(false);
16156             if(dd){
16157                 dd.lock();
16158             }
16159         }
16160         if(xy){
16161             el.avoidY = xy[1]-18;
16162             el.setXY(xy);
16163         }
16164         if(tm.animate){
16165             el.setOpacity(.1);
16166             el.setStyle("visibility", "visible");
16167             el.fadeIn({callback: afterShow});
16168         }else{
16169             afterShow();
16170         }
16171     };
16172     
16173     var afterShow = function(){
16174         if(ce){
16175             el.show();
16176             esc.enable();
16177             if(tm.autoDismiss && ce.autoHide !== false){
16178                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16179             }
16180         }
16181     };
16182     
16183     var hide = function(noanim){
16184         clearTimeout(dismissProc);
16185         clearTimeout(hideProc);
16186         ce = null;
16187         if(el.isVisible()){
16188             esc.disable();
16189             if(noanim !== true && tm.animate){
16190                 el.fadeOut({callback: afterHide});
16191             }else{
16192                 afterHide();
16193             } 
16194         }
16195     };
16196     
16197     var afterHide = function(){
16198         el.hide();
16199         if(removeCls){
16200             el.removeClass(removeCls);
16201             removeCls = null;
16202         }
16203     };
16204     
16205     return {
16206         /**
16207         * @cfg {Number} minWidth
16208         * The minimum width of the quick tip (defaults to 40)
16209         */
16210        minWidth : 40,
16211         /**
16212         * @cfg {Number} maxWidth
16213         * The maximum width of the quick tip (defaults to 300)
16214         */
16215        maxWidth : 300,
16216         /**
16217         * @cfg {Boolean} interceptTitles
16218         * True to automatically use the element's DOM title value if available (defaults to false)
16219         */
16220        interceptTitles : false,
16221         /**
16222         * @cfg {Boolean} trackMouse
16223         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16224         */
16225        trackMouse : false,
16226         /**
16227         * @cfg {Boolean} hideOnClick
16228         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16229         */
16230        hideOnClick : true,
16231         /**
16232         * @cfg {Number} showDelay
16233         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16234         */
16235        showDelay : 500,
16236         /**
16237         * @cfg {Number} hideDelay
16238         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16239         */
16240        hideDelay : 200,
16241         /**
16242         * @cfg {Boolean} autoHide
16243         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16244         * Used in conjunction with hideDelay.
16245         */
16246        autoHide : true,
16247         /**
16248         * @cfg {Boolean}
16249         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16250         * (defaults to true).  Used in conjunction with autoDismissDelay.
16251         */
16252        autoDismiss : true,
16253         /**
16254         * @cfg {Number}
16255         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16256         */
16257        autoDismissDelay : 5000,
16258        /**
16259         * @cfg {Boolean} animate
16260         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16261         */
16262        animate : false,
16263
16264        /**
16265         * @cfg {String} title
16266         * Title text to display (defaults to '').  This can be any valid HTML markup.
16267         */
16268         title: '',
16269        /**
16270         * @cfg {String} text
16271         * Body text to display (defaults to '').  This can be any valid HTML markup.
16272         */
16273         text : '',
16274        /**
16275         * @cfg {String} cls
16276         * A CSS class to apply to the base quick tip element (defaults to '').
16277         */
16278         cls : '',
16279        /**
16280         * @cfg {Number} width
16281         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16282         * minWidth or maxWidth.
16283         */
16284         width : null,
16285
16286     /**
16287      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16288      * or display QuickTips in a page.
16289      */
16290        init : function(){
16291           tm = Roo.QuickTips;
16292           cfg = tm.tagConfig;
16293           if(!inited){
16294               if(!Roo.isReady){ // allow calling of init() before onReady
16295                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16296                   return;
16297               }
16298               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16299               el.fxDefaults = {stopFx: true};
16300               // maximum custom styling
16301               //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>');
16302               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>');              
16303               tipTitle = el.child('h3');
16304               tipTitle.enableDisplayMode("block");
16305               tipBody = el.child('div.x-tip-bd');
16306               tipBodyText = el.child('div.x-tip-bd-inner');
16307               //bdLeft = el.child('div.x-tip-bd-left');
16308               //bdRight = el.child('div.x-tip-bd-right');
16309               close = el.child('div.x-tip-close');
16310               close.enableDisplayMode("block");
16311               close.on("click", hide);
16312               var d = Roo.get(document);
16313               d.on("mousedown", onDown);
16314               d.on("mouseover", onOver);
16315               d.on("mouseout", onOut);
16316               d.on("mousemove", onMove);
16317               esc = d.addKeyListener(27, hide);
16318               esc.disable();
16319               if(Roo.dd.DD){
16320                   dd = el.initDD("default", null, {
16321                       onDrag : function(){
16322                           el.sync();  
16323                       }
16324                   });
16325                   dd.setHandleElId(tipTitle.id);
16326                   dd.lock();
16327               }
16328               inited = true;
16329           }
16330           this.enable(); 
16331        },
16332
16333     /**
16334      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16335      * are supported:
16336      * <pre>
16337 Property    Type                   Description
16338 ----------  ---------------------  ------------------------------------------------------------------------
16339 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16340      * </ul>
16341      * @param {Object} config The config object
16342      */
16343        register : function(config){
16344            var cs = config instanceof Array ? config : arguments;
16345            for(var i = 0, len = cs.length; i < len; i++) {
16346                var c = cs[i];
16347                var target = c.target;
16348                if(target){
16349                    if(target instanceof Array){
16350                        for(var j = 0, jlen = target.length; j < jlen; j++){
16351                            tagEls[target[j]] = c;
16352                        }
16353                    }else{
16354                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16355                    }
16356                }
16357            }
16358        },
16359
16360     /**
16361      * Removes this quick tip from its element and destroys it.
16362      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16363      */
16364        unregister : function(el){
16365            delete tagEls[Roo.id(el)];
16366        },
16367
16368     /**
16369      * Enable this quick tip.
16370      */
16371        enable : function(){
16372            if(inited && disabled){
16373                locks.pop();
16374                if(locks.length < 1){
16375                    disabled = false;
16376                }
16377            }
16378        },
16379
16380     /**
16381      * Disable this quick tip.
16382      */
16383        disable : function(){
16384           disabled = true;
16385           clearTimeout(showProc);
16386           clearTimeout(hideProc);
16387           clearTimeout(dismissProc);
16388           if(ce){
16389               hide(true);
16390           }
16391           locks.push(1);
16392        },
16393
16394     /**
16395      * Returns true if the quick tip is enabled, else false.
16396      */
16397        isEnabled : function(){
16398             return !disabled;
16399        },
16400
16401         // private
16402        tagConfig : {
16403            namespace : "ext",
16404            attribute : "qtip",
16405            width : "width",
16406            target : "target",
16407            title : "qtitle",
16408            hide : "hide",
16409            cls : "qclass"
16410        }
16411    };
16412 }();
16413
16414 // backwards compat
16415 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16416  * Based on:
16417  * Ext JS Library 1.1.1
16418  * Copyright(c) 2006-2007, Ext JS, LLC.
16419  *
16420  * Originally Released Under LGPL - original licence link has changed is not relivant.
16421  *
16422  * Fork - LGPL
16423  * <script type="text/javascript">
16424  */
16425  
16426
16427 /**
16428  * @class Roo.tree.TreePanel
16429  * @extends Roo.data.Tree
16430
16431  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16432  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16433  * @cfg {Boolean} enableDD true to enable drag and drop
16434  * @cfg {Boolean} enableDrag true to enable just drag
16435  * @cfg {Boolean} enableDrop true to enable just drop
16436  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16437  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16438  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16439  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16440  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16441  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16442  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16443  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16444  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16445  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16446  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16447  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16448  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16449  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16450  * @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>
16451  * @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>
16452  * 
16453  * @constructor
16454  * @param {String/HTMLElement/Element} el The container element
16455  * @param {Object} config
16456  */
16457 Roo.tree.TreePanel = function(el, config){
16458     var root = false;
16459     var loader = false;
16460     if (config.root) {
16461         root = config.root;
16462         delete config.root;
16463     }
16464     if (config.loader) {
16465         loader = config.loader;
16466         delete config.loader;
16467     }
16468     
16469     Roo.apply(this, config);
16470     Roo.tree.TreePanel.superclass.constructor.call(this);
16471     this.el = Roo.get(el);
16472     this.el.addClass('x-tree');
16473     //console.log(root);
16474     if (root) {
16475         this.setRootNode( Roo.factory(root, Roo.tree));
16476     }
16477     if (loader) {
16478         this.loader = Roo.factory(loader, Roo.tree);
16479     }
16480    /**
16481     * Read-only. The id of the container element becomes this TreePanel's id.
16482     */
16483     this.id = this.el.id;
16484     this.addEvents({
16485         /**
16486         * @event beforeload
16487         * Fires before a node is loaded, return false to cancel
16488         * @param {Node} node The node being loaded
16489         */
16490         "beforeload" : true,
16491         /**
16492         * @event load
16493         * Fires when a node is loaded
16494         * @param {Node} node The node that was loaded
16495         */
16496         "load" : true,
16497         /**
16498         * @event textchange
16499         * Fires when the text for a node is changed
16500         * @param {Node} node The node
16501         * @param {String} text The new text
16502         * @param {String} oldText The old text
16503         */
16504         "textchange" : true,
16505         /**
16506         * @event beforeexpand
16507         * Fires before a node is expanded, return false to cancel.
16508         * @param {Node} node The node
16509         * @param {Boolean} deep
16510         * @param {Boolean} anim
16511         */
16512         "beforeexpand" : true,
16513         /**
16514         * @event beforecollapse
16515         * Fires before a node is collapsed, return false to cancel.
16516         * @param {Node} node The node
16517         * @param {Boolean} deep
16518         * @param {Boolean} anim
16519         */
16520         "beforecollapse" : true,
16521         /**
16522         * @event expand
16523         * Fires when a node is expanded
16524         * @param {Node} node The node
16525         */
16526         "expand" : true,
16527         /**
16528         * @event disabledchange
16529         * Fires when the disabled status of a node changes
16530         * @param {Node} node The node
16531         * @param {Boolean} disabled
16532         */
16533         "disabledchange" : true,
16534         /**
16535         * @event collapse
16536         * Fires when a node is collapsed
16537         * @param {Node} node The node
16538         */
16539         "collapse" : true,
16540         /**
16541         * @event beforeclick
16542         * Fires before click processing on a node. Return false to cancel the default action.
16543         * @param {Node} node The node
16544         * @param {Roo.EventObject} e The event object
16545         */
16546         "beforeclick":true,
16547         /**
16548         * @event checkchange
16549         * Fires when a node with a checkbox's checked property changes
16550         * @param {Node} this This node
16551         * @param {Boolean} checked
16552         */
16553         "checkchange":true,
16554         /**
16555         * @event click
16556         * Fires when a node is clicked
16557         * @param {Node} node The node
16558         * @param {Roo.EventObject} e The event object
16559         */
16560         "click":true,
16561         /**
16562         * @event dblclick
16563         * Fires when a node is double clicked
16564         * @param {Node} node The node
16565         * @param {Roo.EventObject} e The event object
16566         */
16567         "dblclick":true,
16568         /**
16569         * @event contextmenu
16570         * Fires when a node is right clicked
16571         * @param {Node} node The node
16572         * @param {Roo.EventObject} e The event object
16573         */
16574         "contextmenu":true,
16575         /**
16576         * @event beforechildrenrendered
16577         * Fires right before the child nodes for a node are rendered
16578         * @param {Node} node The node
16579         */
16580         "beforechildrenrendered":true,
16581         /**
16582         * @event startdrag
16583         * Fires when a node starts being dragged
16584         * @param {Roo.tree.TreePanel} this
16585         * @param {Roo.tree.TreeNode} node
16586         * @param {event} e The raw browser event
16587         */ 
16588        "startdrag" : true,
16589        /**
16590         * @event enddrag
16591         * Fires when a drag operation is complete
16592         * @param {Roo.tree.TreePanel} this
16593         * @param {Roo.tree.TreeNode} node
16594         * @param {event} e The raw browser event
16595         */
16596        "enddrag" : true,
16597        /**
16598         * @event dragdrop
16599         * Fires when a dragged node is dropped on a valid DD target
16600         * @param {Roo.tree.TreePanel} this
16601         * @param {Roo.tree.TreeNode} node
16602         * @param {DD} dd The dd it was dropped on
16603         * @param {event} e The raw browser event
16604         */
16605        "dragdrop" : true,
16606        /**
16607         * @event beforenodedrop
16608         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16609         * passed to handlers has the following properties:<br />
16610         * <ul style="padding:5px;padding-left:16px;">
16611         * <li>tree - The TreePanel</li>
16612         * <li>target - The node being targeted for the drop</li>
16613         * <li>data - The drag data from the drag source</li>
16614         * <li>point - The point of the drop - append, above or below</li>
16615         * <li>source - The drag source</li>
16616         * <li>rawEvent - Raw mouse event</li>
16617         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16618         * to be inserted by setting them on this object.</li>
16619         * <li>cancel - Set this to true to cancel the drop.</li>
16620         * </ul>
16621         * @param {Object} dropEvent
16622         */
16623        "beforenodedrop" : true,
16624        /**
16625         * @event nodedrop
16626         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16627         * passed to handlers has the following properties:<br />
16628         * <ul style="padding:5px;padding-left:16px;">
16629         * <li>tree - The TreePanel</li>
16630         * <li>target - The node being targeted for the drop</li>
16631         * <li>data - The drag data from the drag source</li>
16632         * <li>point - The point of the drop - append, above or below</li>
16633         * <li>source - The drag source</li>
16634         * <li>rawEvent - Raw mouse event</li>
16635         * <li>dropNode - Dropped node(s).</li>
16636         * </ul>
16637         * @param {Object} dropEvent
16638         */
16639        "nodedrop" : true,
16640         /**
16641         * @event nodedragover
16642         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16643         * passed to handlers has the following properties:<br />
16644         * <ul style="padding:5px;padding-left:16px;">
16645         * <li>tree - The TreePanel</li>
16646         * <li>target - The node being targeted for the drop</li>
16647         * <li>data - The drag data from the drag source</li>
16648         * <li>point - The point of the drop - append, above or below</li>
16649         * <li>source - The drag source</li>
16650         * <li>rawEvent - Raw mouse event</li>
16651         * <li>dropNode - Drop node(s) provided by the source.</li>
16652         * <li>cancel - Set this to true to signal drop not allowed.</li>
16653         * </ul>
16654         * @param {Object} dragOverEvent
16655         */
16656        "nodedragover" : true
16657         
16658     });
16659     if(this.singleExpand){
16660        this.on("beforeexpand", this.restrictExpand, this);
16661     }
16662     if (this.editor) {
16663         this.editor.tree = this;
16664         this.editor = Roo.factory(this.editor, Roo.tree);
16665     }
16666     
16667     if (this.selModel) {
16668         this.selModel = Roo.factory(this.selModel, Roo.tree);
16669     }
16670    
16671 };
16672 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16673     rootVisible : true,
16674     animate: Roo.enableFx,
16675     lines : true,
16676     enableDD : false,
16677     hlDrop : Roo.enableFx,
16678   
16679     renderer: false,
16680     
16681     rendererTip: false,
16682     // private
16683     restrictExpand : function(node){
16684         var p = node.parentNode;
16685         if(p){
16686             if(p.expandedChild && p.expandedChild.parentNode == p){
16687                 p.expandedChild.collapse();
16688             }
16689             p.expandedChild = node;
16690         }
16691     },
16692
16693     // private override
16694     setRootNode : function(node){
16695         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16696         if(!this.rootVisible){
16697             node.ui = new Roo.tree.RootTreeNodeUI(node);
16698         }
16699         return node;
16700     },
16701
16702     /**
16703      * Returns the container element for this TreePanel
16704      */
16705     getEl : function(){
16706         return this.el;
16707     },
16708
16709     /**
16710      * Returns the default TreeLoader for this TreePanel
16711      */
16712     getLoader : function(){
16713         return this.loader;
16714     },
16715
16716     /**
16717      * Expand all nodes
16718      */
16719     expandAll : function(){
16720         this.root.expand(true);
16721     },
16722
16723     /**
16724      * Collapse all nodes
16725      */
16726     collapseAll : function(){
16727         this.root.collapse(true);
16728     },
16729
16730     /**
16731      * Returns the selection model used by this TreePanel
16732      */
16733     getSelectionModel : function(){
16734         if(!this.selModel){
16735             this.selModel = new Roo.tree.DefaultSelectionModel();
16736         }
16737         return this.selModel;
16738     },
16739
16740     /**
16741      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16742      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16743      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16744      * @return {Array}
16745      */
16746     getChecked : function(a, startNode){
16747         startNode = startNode || this.root;
16748         var r = [];
16749         var f = function(){
16750             if(this.attributes.checked){
16751                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16752             }
16753         }
16754         startNode.cascade(f);
16755         return r;
16756     },
16757
16758     /**
16759      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16760      * @param {String} path
16761      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16762      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16763      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16764      */
16765     expandPath : function(path, attr, callback){
16766         attr = attr || "id";
16767         var keys = path.split(this.pathSeparator);
16768         var curNode = this.root;
16769         if(curNode.attributes[attr] != keys[1]){ // invalid root
16770             if(callback){
16771                 callback(false, null);
16772             }
16773             return;
16774         }
16775         var index = 1;
16776         var f = function(){
16777             if(++index == keys.length){
16778                 if(callback){
16779                     callback(true, curNode);
16780                 }
16781                 return;
16782             }
16783             var c = curNode.findChild(attr, keys[index]);
16784             if(!c){
16785                 if(callback){
16786                     callback(false, curNode);
16787                 }
16788                 return;
16789             }
16790             curNode = c;
16791             c.expand(false, false, f);
16792         };
16793         curNode.expand(false, false, f);
16794     },
16795
16796     /**
16797      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16798      * @param {String} path
16799      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16800      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16801      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16802      */
16803     selectPath : function(path, attr, callback){
16804         attr = attr || "id";
16805         var keys = path.split(this.pathSeparator);
16806         var v = keys.pop();
16807         if(keys.length > 0){
16808             var f = function(success, node){
16809                 if(success && node){
16810                     var n = node.findChild(attr, v);
16811                     if(n){
16812                         n.select();
16813                         if(callback){
16814                             callback(true, n);
16815                         }
16816                     }else if(callback){
16817                         callback(false, n);
16818                     }
16819                 }else{
16820                     if(callback){
16821                         callback(false, n);
16822                     }
16823                 }
16824             };
16825             this.expandPath(keys.join(this.pathSeparator), attr, f);
16826         }else{
16827             this.root.select();
16828             if(callback){
16829                 callback(true, this.root);
16830             }
16831         }
16832     },
16833
16834     getTreeEl : function(){
16835         return this.el;
16836     },
16837
16838     /**
16839      * Trigger rendering of this TreePanel
16840      */
16841     render : function(){
16842         if (this.innerCt) {
16843             return this; // stop it rendering more than once!!
16844         }
16845         
16846         this.innerCt = this.el.createChild({tag:"ul",
16847                cls:"x-tree-root-ct " +
16848                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16849
16850         if(this.containerScroll){
16851             Roo.dd.ScrollManager.register(this.el);
16852         }
16853         if((this.enableDD || this.enableDrop) && !this.dropZone){
16854            /**
16855             * The dropZone used by this tree if drop is enabled
16856             * @type Roo.tree.TreeDropZone
16857             */
16858              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16859                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16860            });
16861         }
16862         if((this.enableDD || this.enableDrag) && !this.dragZone){
16863            /**
16864             * The dragZone used by this tree if drag is enabled
16865             * @type Roo.tree.TreeDragZone
16866             */
16867             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16868                ddGroup: this.ddGroup || "TreeDD",
16869                scroll: this.ddScroll
16870            });
16871         }
16872         this.getSelectionModel().init(this);
16873         if (!this.root) {
16874             console.log("ROOT not set in tree");
16875             return;
16876         }
16877         this.root.render();
16878         if(!this.rootVisible){
16879             this.root.renderChildren();
16880         }
16881         return this;
16882     }
16883 });/*
16884  * Based on:
16885  * Ext JS Library 1.1.1
16886  * Copyright(c) 2006-2007, Ext JS, LLC.
16887  *
16888  * Originally Released Under LGPL - original licence link has changed is not relivant.
16889  *
16890  * Fork - LGPL
16891  * <script type="text/javascript">
16892  */
16893  
16894
16895 /**
16896  * @class Roo.tree.DefaultSelectionModel
16897  * @extends Roo.util.Observable
16898  * The default single selection for a TreePanel.
16899  * @param {Object} cfg Configuration
16900  */
16901 Roo.tree.DefaultSelectionModel = function(cfg){
16902    this.selNode = null;
16903    
16904    
16905    
16906    this.addEvents({
16907        /**
16908         * @event selectionchange
16909         * Fires when the selected node changes
16910         * @param {DefaultSelectionModel} this
16911         * @param {TreeNode} node the new selection
16912         */
16913        "selectionchange" : true,
16914
16915        /**
16916         * @event beforeselect
16917         * Fires before the selected node changes, return false to cancel the change
16918         * @param {DefaultSelectionModel} this
16919         * @param {TreeNode} node the new selection
16920         * @param {TreeNode} node the old selection
16921         */
16922        "beforeselect" : true
16923    });
16924    
16925     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16926 };
16927
16928 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16929     init : function(tree){
16930         this.tree = tree;
16931         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16932         tree.on("click", this.onNodeClick, this);
16933     },
16934     
16935     onNodeClick : function(node, e){
16936         if (e.ctrlKey && this.selNode == node)  {
16937             this.unselect(node);
16938             return;
16939         }
16940         this.select(node);
16941     },
16942     
16943     /**
16944      * Select a node.
16945      * @param {TreeNode} node The node to select
16946      * @return {TreeNode} The selected node
16947      */
16948     select : function(node){
16949         var last = this.selNode;
16950         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16951             if(last){
16952                 last.ui.onSelectedChange(false);
16953             }
16954             this.selNode = node;
16955             node.ui.onSelectedChange(true);
16956             this.fireEvent("selectionchange", this, node, last);
16957         }
16958         return node;
16959     },
16960     
16961     /**
16962      * Deselect a node.
16963      * @param {TreeNode} node The node to unselect
16964      */
16965     unselect : function(node){
16966         if(this.selNode == node){
16967             this.clearSelections();
16968         }    
16969     },
16970     
16971     /**
16972      * Clear all selections
16973      */
16974     clearSelections : function(){
16975         var n = this.selNode;
16976         if(n){
16977             n.ui.onSelectedChange(false);
16978             this.selNode = null;
16979             this.fireEvent("selectionchange", this, null);
16980         }
16981         return n;
16982     },
16983     
16984     /**
16985      * Get the selected node
16986      * @return {TreeNode} The selected node
16987      */
16988     getSelectedNode : function(){
16989         return this.selNode;    
16990     },
16991     
16992     /**
16993      * Returns true if the node is selected
16994      * @param {TreeNode} node The node to check
16995      * @return {Boolean}
16996      */
16997     isSelected : function(node){
16998         return this.selNode == node;  
16999     },
17000
17001     /**
17002      * Selects the node above the selected node in the tree, intelligently walking the nodes
17003      * @return TreeNode The new selection
17004      */
17005     selectPrevious : function(){
17006         var s = this.selNode || this.lastSelNode;
17007         if(!s){
17008             return null;
17009         }
17010         var ps = s.previousSibling;
17011         if(ps){
17012             if(!ps.isExpanded() || ps.childNodes.length < 1){
17013                 return this.select(ps);
17014             } else{
17015                 var lc = ps.lastChild;
17016                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17017                     lc = lc.lastChild;
17018                 }
17019                 return this.select(lc);
17020             }
17021         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17022             return this.select(s.parentNode);
17023         }
17024         return null;
17025     },
17026
17027     /**
17028      * Selects the node above the selected node in the tree, intelligently walking the nodes
17029      * @return TreeNode The new selection
17030      */
17031     selectNext : function(){
17032         var s = this.selNode || this.lastSelNode;
17033         if(!s){
17034             return null;
17035         }
17036         if(s.firstChild && s.isExpanded()){
17037              return this.select(s.firstChild);
17038          }else if(s.nextSibling){
17039              return this.select(s.nextSibling);
17040          }else if(s.parentNode){
17041             var newS = null;
17042             s.parentNode.bubble(function(){
17043                 if(this.nextSibling){
17044                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17045                     return false;
17046                 }
17047             });
17048             return newS;
17049          }
17050         return null;
17051     },
17052
17053     onKeyDown : function(e){
17054         var s = this.selNode || this.lastSelNode;
17055         // undesirable, but required
17056         var sm = this;
17057         if(!s){
17058             return;
17059         }
17060         var k = e.getKey();
17061         switch(k){
17062              case e.DOWN:
17063                  e.stopEvent();
17064                  this.selectNext();
17065              break;
17066              case e.UP:
17067                  e.stopEvent();
17068                  this.selectPrevious();
17069              break;
17070              case e.RIGHT:
17071                  e.preventDefault();
17072                  if(s.hasChildNodes()){
17073                      if(!s.isExpanded()){
17074                          s.expand();
17075                      }else if(s.firstChild){
17076                          this.select(s.firstChild, e);
17077                      }
17078                  }
17079              break;
17080              case e.LEFT:
17081                  e.preventDefault();
17082                  if(s.hasChildNodes() && s.isExpanded()){
17083                      s.collapse();
17084                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17085                      this.select(s.parentNode, e);
17086                  }
17087              break;
17088         };
17089     }
17090 });
17091
17092 /**
17093  * @class Roo.tree.MultiSelectionModel
17094  * @extends Roo.util.Observable
17095  * Multi selection for a TreePanel.
17096  * @param {Object} cfg Configuration
17097  */
17098 Roo.tree.MultiSelectionModel = function(){
17099    this.selNodes = [];
17100    this.selMap = {};
17101    this.addEvents({
17102        /**
17103         * @event selectionchange
17104         * Fires when the selected nodes change
17105         * @param {MultiSelectionModel} this
17106         * @param {Array} nodes Array of the selected nodes
17107         */
17108        "selectionchange" : true
17109    });
17110    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17111    
17112 };
17113
17114 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17115     init : function(tree){
17116         this.tree = tree;
17117         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17118         tree.on("click", this.onNodeClick, this);
17119     },
17120     
17121     onNodeClick : function(node, e){
17122         this.select(node, e, e.ctrlKey);
17123     },
17124     
17125     /**
17126      * Select a node.
17127      * @param {TreeNode} node The node to select
17128      * @param {EventObject} e (optional) An event associated with the selection
17129      * @param {Boolean} keepExisting True to retain existing selections
17130      * @return {TreeNode} The selected node
17131      */
17132     select : function(node, e, keepExisting){
17133         if(keepExisting !== true){
17134             this.clearSelections(true);
17135         }
17136         if(this.isSelected(node)){
17137             this.lastSelNode = node;
17138             return node;
17139         }
17140         this.selNodes.push(node);
17141         this.selMap[node.id] = node;
17142         this.lastSelNode = node;
17143         node.ui.onSelectedChange(true);
17144         this.fireEvent("selectionchange", this, this.selNodes);
17145         return node;
17146     },
17147     
17148     /**
17149      * Deselect a node.
17150      * @param {TreeNode} node The node to unselect
17151      */
17152     unselect : function(node){
17153         if(this.selMap[node.id]){
17154             node.ui.onSelectedChange(false);
17155             var sn = this.selNodes;
17156             var index = -1;
17157             if(sn.indexOf){
17158                 index = sn.indexOf(node);
17159             }else{
17160                 for(var i = 0, len = sn.length; i < len; i++){
17161                     if(sn[i] == node){
17162                         index = i;
17163                         break;
17164                     }
17165                 }
17166             }
17167             if(index != -1){
17168                 this.selNodes.splice(index, 1);
17169             }
17170             delete this.selMap[node.id];
17171             this.fireEvent("selectionchange", this, this.selNodes);
17172         }
17173     },
17174     
17175     /**
17176      * Clear all selections
17177      */
17178     clearSelections : function(suppressEvent){
17179         var sn = this.selNodes;
17180         if(sn.length > 0){
17181             for(var i = 0, len = sn.length; i < len; i++){
17182                 sn[i].ui.onSelectedChange(false);
17183             }
17184             this.selNodes = [];
17185             this.selMap = {};
17186             if(suppressEvent !== true){
17187                 this.fireEvent("selectionchange", this, this.selNodes);
17188             }
17189         }
17190     },
17191     
17192     /**
17193      * Returns true if the node is selected
17194      * @param {TreeNode} node The node to check
17195      * @return {Boolean}
17196      */
17197     isSelected : function(node){
17198         return this.selMap[node.id] ? true : false;  
17199     },
17200     
17201     /**
17202      * Returns an array of the selected nodes
17203      * @return {Array}
17204      */
17205     getSelectedNodes : function(){
17206         return this.selNodes;    
17207     },
17208
17209     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17210
17211     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17212
17213     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17214 });/*
17215  * Based on:
17216  * Ext JS Library 1.1.1
17217  * Copyright(c) 2006-2007, Ext JS, LLC.
17218  *
17219  * Originally Released Under LGPL - original licence link has changed is not relivant.
17220  *
17221  * Fork - LGPL
17222  * <script type="text/javascript">
17223  */
17224  
17225 /**
17226  * @class Roo.tree.TreeNode
17227  * @extends Roo.data.Node
17228  * @cfg {String} text The text for this node
17229  * @cfg {Boolean} expanded true to start the node expanded
17230  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17231  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17232  * @cfg {Boolean} disabled true to start the node disabled
17233  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17234  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17235  * @cfg {String} cls A css class to be added to the node
17236  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17237  * @cfg {String} href URL of the link used for the node (defaults to #)
17238  * @cfg {String} hrefTarget target frame for the link
17239  * @cfg {String} qtip An Ext QuickTip for the node
17240  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17241  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17242  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17243  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17244  * (defaults to undefined with no checkbox rendered)
17245  * @constructor
17246  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17247  */
17248 Roo.tree.TreeNode = function(attributes){
17249     attributes = attributes || {};
17250     if(typeof attributes == "string"){
17251         attributes = {text: attributes};
17252     }
17253     this.childrenRendered = false;
17254     this.rendered = false;
17255     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17256     this.expanded = attributes.expanded === true;
17257     this.isTarget = attributes.isTarget !== false;
17258     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17259     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17260
17261     /**
17262      * Read-only. The text for this node. To change it use setText().
17263      * @type String
17264      */
17265     this.text = attributes.text;
17266     /**
17267      * True if this node is disabled.
17268      * @type Boolean
17269      */
17270     this.disabled = attributes.disabled === true;
17271
17272     this.addEvents({
17273         /**
17274         * @event textchange
17275         * Fires when the text for this node is changed
17276         * @param {Node} this This node
17277         * @param {String} text The new text
17278         * @param {String} oldText The old text
17279         */
17280         "textchange" : true,
17281         /**
17282         * @event beforeexpand
17283         * Fires before this node is expanded, return false to cancel.
17284         * @param {Node} this This node
17285         * @param {Boolean} deep
17286         * @param {Boolean} anim
17287         */
17288         "beforeexpand" : true,
17289         /**
17290         * @event beforecollapse
17291         * Fires before this node is collapsed, return false to cancel.
17292         * @param {Node} this This node
17293         * @param {Boolean} deep
17294         * @param {Boolean} anim
17295         */
17296         "beforecollapse" : true,
17297         /**
17298         * @event expand
17299         * Fires when this node is expanded
17300         * @param {Node} this This node
17301         */
17302         "expand" : true,
17303         /**
17304         * @event disabledchange
17305         * Fires when the disabled status of this node changes
17306         * @param {Node} this This node
17307         * @param {Boolean} disabled
17308         */
17309         "disabledchange" : true,
17310         /**
17311         * @event collapse
17312         * Fires when this node is collapsed
17313         * @param {Node} this This node
17314         */
17315         "collapse" : true,
17316         /**
17317         * @event beforeclick
17318         * Fires before click processing. Return false to cancel the default action.
17319         * @param {Node} this This node
17320         * @param {Roo.EventObject} e The event object
17321         */
17322         "beforeclick":true,
17323         /**
17324         * @event checkchange
17325         * Fires when a node with a checkbox's checked property changes
17326         * @param {Node} this This node
17327         * @param {Boolean} checked
17328         */
17329         "checkchange":true,
17330         /**
17331         * @event click
17332         * Fires when this node is clicked
17333         * @param {Node} this This node
17334         * @param {Roo.EventObject} e The event object
17335         */
17336         "click":true,
17337         /**
17338         * @event dblclick
17339         * Fires when this node is double clicked
17340         * @param {Node} this This node
17341         * @param {Roo.EventObject} e The event object
17342         */
17343         "dblclick":true,
17344         /**
17345         * @event contextmenu
17346         * Fires when this node is right clicked
17347         * @param {Node} this This node
17348         * @param {Roo.EventObject} e The event object
17349         */
17350         "contextmenu":true,
17351         /**
17352         * @event beforechildrenrendered
17353         * Fires right before the child nodes for this node are rendered
17354         * @param {Node} this This node
17355         */
17356         "beforechildrenrendered":true
17357     });
17358
17359     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17360
17361     /**
17362      * Read-only. The UI for this node
17363      * @type TreeNodeUI
17364      */
17365     this.ui = new uiClass(this);
17366 };
17367 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17368     preventHScroll: true,
17369     /**
17370      * Returns true if this node is expanded
17371      * @return {Boolean}
17372      */
17373     isExpanded : function(){
17374         return this.expanded;
17375     },
17376
17377     /**
17378      * Returns the UI object for this node
17379      * @return {TreeNodeUI}
17380      */
17381     getUI : function(){
17382         return this.ui;
17383     },
17384
17385     // private override
17386     setFirstChild : function(node){
17387         var of = this.firstChild;
17388         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17389         if(this.childrenRendered && of && node != of){
17390             of.renderIndent(true, true);
17391         }
17392         if(this.rendered){
17393             this.renderIndent(true, true);
17394         }
17395     },
17396
17397     // private override
17398     setLastChild : function(node){
17399         var ol = this.lastChild;
17400         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17401         if(this.childrenRendered && ol && node != ol){
17402             ol.renderIndent(true, true);
17403         }
17404         if(this.rendered){
17405             this.renderIndent(true, true);
17406         }
17407     },
17408
17409     // these methods are overridden to provide lazy rendering support
17410     // private override
17411     appendChild : function(){
17412         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17413         if(node && this.childrenRendered){
17414             node.render();
17415         }
17416         this.ui.updateExpandIcon();
17417         return node;
17418     },
17419
17420     // private override
17421     removeChild : function(node){
17422         this.ownerTree.getSelectionModel().unselect(node);
17423         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17424         // if it's been rendered remove dom node
17425         if(this.childrenRendered){
17426             node.ui.remove();
17427         }
17428         if(this.childNodes.length < 1){
17429             this.collapse(false, false);
17430         }else{
17431             this.ui.updateExpandIcon();
17432         }
17433         if(!this.firstChild) {
17434             this.childrenRendered = false;
17435         }
17436         return node;
17437     },
17438
17439     // private override
17440     insertBefore : function(node, refNode){
17441         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17442         if(newNode && refNode && this.childrenRendered){
17443             node.render();
17444         }
17445         this.ui.updateExpandIcon();
17446         return newNode;
17447     },
17448
17449     /**
17450      * Sets the text for this node
17451      * @param {String} text
17452      */
17453     setText : function(text){
17454         var oldText = this.text;
17455         this.text = text;
17456         this.attributes.text = text;
17457         if(this.rendered){ // event without subscribing
17458             this.ui.onTextChange(this, text, oldText);
17459         }
17460         this.fireEvent("textchange", this, text, oldText);
17461     },
17462
17463     /**
17464      * Triggers selection of this node
17465      */
17466     select : function(){
17467         this.getOwnerTree().getSelectionModel().select(this);
17468     },
17469
17470     /**
17471      * Triggers deselection of this node
17472      */
17473     unselect : function(){
17474         this.getOwnerTree().getSelectionModel().unselect(this);
17475     },
17476
17477     /**
17478      * Returns true if this node is selected
17479      * @return {Boolean}
17480      */
17481     isSelected : function(){
17482         return this.getOwnerTree().getSelectionModel().isSelected(this);
17483     },
17484
17485     /**
17486      * Expand this node.
17487      * @param {Boolean} deep (optional) True to expand all children as well
17488      * @param {Boolean} anim (optional) false to cancel the default animation
17489      * @param {Function} callback (optional) A callback to be called when
17490      * expanding this node completes (does not wait for deep expand to complete).
17491      * Called with 1 parameter, this node.
17492      */
17493     expand : function(deep, anim, callback){
17494         if(!this.expanded){
17495             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17496                 return;
17497             }
17498             if(!this.childrenRendered){
17499                 this.renderChildren();
17500             }
17501             this.expanded = true;
17502             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17503                 this.ui.animExpand(function(){
17504                     this.fireEvent("expand", this);
17505                     if(typeof callback == "function"){
17506                         callback(this);
17507                     }
17508                     if(deep === true){
17509                         this.expandChildNodes(true);
17510                     }
17511                 }.createDelegate(this));
17512                 return;
17513             }else{
17514                 this.ui.expand();
17515                 this.fireEvent("expand", this);
17516                 if(typeof callback == "function"){
17517                     callback(this);
17518                 }
17519             }
17520         }else{
17521            if(typeof callback == "function"){
17522                callback(this);
17523            }
17524         }
17525         if(deep === true){
17526             this.expandChildNodes(true);
17527         }
17528     },
17529
17530     isHiddenRoot : function(){
17531         return this.isRoot && !this.getOwnerTree().rootVisible;
17532     },
17533
17534     /**
17535      * Collapse this node.
17536      * @param {Boolean} deep (optional) True to collapse all children as well
17537      * @param {Boolean} anim (optional) false to cancel the default animation
17538      */
17539     collapse : function(deep, anim){
17540         if(this.expanded && !this.isHiddenRoot()){
17541             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17542                 return;
17543             }
17544             this.expanded = false;
17545             if((this.getOwnerTree().animate && anim !== false) || anim){
17546                 this.ui.animCollapse(function(){
17547                     this.fireEvent("collapse", this);
17548                     if(deep === true){
17549                         this.collapseChildNodes(true);
17550                     }
17551                 }.createDelegate(this));
17552                 return;
17553             }else{
17554                 this.ui.collapse();
17555                 this.fireEvent("collapse", this);
17556             }
17557         }
17558         if(deep === true){
17559             var cs = this.childNodes;
17560             for(var i = 0, len = cs.length; i < len; i++) {
17561                 cs[i].collapse(true, false);
17562             }
17563         }
17564     },
17565
17566     // private
17567     delayedExpand : function(delay){
17568         if(!this.expandProcId){
17569             this.expandProcId = this.expand.defer(delay, this);
17570         }
17571     },
17572
17573     // private
17574     cancelExpand : function(){
17575         if(this.expandProcId){
17576             clearTimeout(this.expandProcId);
17577         }
17578         this.expandProcId = false;
17579     },
17580
17581     /**
17582      * Toggles expanded/collapsed state of the node
17583      */
17584     toggle : function(){
17585         if(this.expanded){
17586             this.collapse();
17587         }else{
17588             this.expand();
17589         }
17590     },
17591
17592     /**
17593      * Ensures all parent nodes are expanded
17594      */
17595     ensureVisible : function(callback){
17596         var tree = this.getOwnerTree();
17597         tree.expandPath(this.parentNode.getPath(), false, function(){
17598             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17599             Roo.callback(callback);
17600         }.createDelegate(this));
17601     },
17602
17603     /**
17604      * Expand all child nodes
17605      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17606      */
17607     expandChildNodes : function(deep){
17608         var cs = this.childNodes;
17609         for(var i = 0, len = cs.length; i < len; i++) {
17610                 cs[i].expand(deep);
17611         }
17612     },
17613
17614     /**
17615      * Collapse all child nodes
17616      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17617      */
17618     collapseChildNodes : function(deep){
17619         var cs = this.childNodes;
17620         for(var i = 0, len = cs.length; i < len; i++) {
17621                 cs[i].collapse(deep);
17622         }
17623     },
17624
17625     /**
17626      * Disables this node
17627      */
17628     disable : function(){
17629         this.disabled = true;
17630         this.unselect();
17631         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17632             this.ui.onDisableChange(this, true);
17633         }
17634         this.fireEvent("disabledchange", this, true);
17635     },
17636
17637     /**
17638      * Enables this node
17639      */
17640     enable : function(){
17641         this.disabled = false;
17642         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17643             this.ui.onDisableChange(this, false);
17644         }
17645         this.fireEvent("disabledchange", this, false);
17646     },
17647
17648     // private
17649     renderChildren : function(suppressEvent){
17650         if(suppressEvent !== false){
17651             this.fireEvent("beforechildrenrendered", this);
17652         }
17653         var cs = this.childNodes;
17654         for(var i = 0, len = cs.length; i < len; i++){
17655             cs[i].render(true);
17656         }
17657         this.childrenRendered = true;
17658     },
17659
17660     // private
17661     sort : function(fn, scope){
17662         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17663         if(this.childrenRendered){
17664             var cs = this.childNodes;
17665             for(var i = 0, len = cs.length; i < len; i++){
17666                 cs[i].render(true);
17667             }
17668         }
17669     },
17670
17671     // private
17672     render : function(bulkRender){
17673         this.ui.render(bulkRender);
17674         if(!this.rendered){
17675             this.rendered = true;
17676             if(this.expanded){
17677                 this.expanded = false;
17678                 this.expand(false, false);
17679             }
17680         }
17681     },
17682
17683     // private
17684     renderIndent : function(deep, refresh){
17685         if(refresh){
17686             this.ui.childIndent = null;
17687         }
17688         this.ui.renderIndent();
17689         if(deep === true && this.childrenRendered){
17690             var cs = this.childNodes;
17691             for(var i = 0, len = cs.length; i < len; i++){
17692                 cs[i].renderIndent(true, refresh);
17693             }
17694         }
17695     }
17696 });/*
17697  * Based on:
17698  * Ext JS Library 1.1.1
17699  * Copyright(c) 2006-2007, Ext JS, LLC.
17700  *
17701  * Originally Released Under LGPL - original licence link has changed is not relivant.
17702  *
17703  * Fork - LGPL
17704  * <script type="text/javascript">
17705  */
17706  
17707 /**
17708  * @class Roo.tree.AsyncTreeNode
17709  * @extends Roo.tree.TreeNode
17710  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17711  * @constructor
17712  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17713  */
17714  Roo.tree.AsyncTreeNode = function(config){
17715     this.loaded = false;
17716     this.loading = false;
17717     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17718     /**
17719     * @event beforeload
17720     * Fires before this node is loaded, return false to cancel
17721     * @param {Node} this This node
17722     */
17723     this.addEvents({'beforeload':true, 'load': true});
17724     /**
17725     * @event load
17726     * Fires when this node is loaded
17727     * @param {Node} this This node
17728     */
17729     /**
17730      * The loader used by this node (defaults to using the tree's defined loader)
17731      * @type TreeLoader
17732      * @property loader
17733      */
17734 };
17735 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17736     expand : function(deep, anim, callback){
17737         if(this.loading){ // if an async load is already running, waiting til it's done
17738             var timer;
17739             var f = function(){
17740                 if(!this.loading){ // done loading
17741                     clearInterval(timer);
17742                     this.expand(deep, anim, callback);
17743                 }
17744             }.createDelegate(this);
17745             timer = setInterval(f, 200);
17746             return;
17747         }
17748         if(!this.loaded){
17749             if(this.fireEvent("beforeload", this) === false){
17750                 return;
17751             }
17752             this.loading = true;
17753             this.ui.beforeLoad(this);
17754             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17755             if(loader){
17756                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17757                 return;
17758             }
17759         }
17760         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17761     },
17762     
17763     /**
17764      * Returns true if this node is currently loading
17765      * @return {Boolean}
17766      */
17767     isLoading : function(){
17768         return this.loading;  
17769     },
17770     
17771     loadComplete : function(deep, anim, callback){
17772         this.loading = false;
17773         this.loaded = true;
17774         this.ui.afterLoad(this);
17775         this.fireEvent("load", this);
17776         this.expand(deep, anim, callback);
17777     },
17778     
17779     /**
17780      * Returns true if this node has been loaded
17781      * @return {Boolean}
17782      */
17783     isLoaded : function(){
17784         return this.loaded;
17785     },
17786     
17787     hasChildNodes : function(){
17788         if(!this.isLeaf() && !this.loaded){
17789             return true;
17790         }else{
17791             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17792         }
17793     },
17794
17795     /**
17796      * Trigger a reload for this node
17797      * @param {Function} callback
17798      */
17799     reload : function(callback){
17800         this.collapse(false, false);
17801         while(this.firstChild){
17802             this.removeChild(this.firstChild);
17803         }
17804         this.childrenRendered = false;
17805         this.loaded = false;
17806         if(this.isHiddenRoot()){
17807             this.expanded = false;
17808         }
17809         this.expand(false, false, callback);
17810     }
17811 });/*
17812  * Based on:
17813  * Ext JS Library 1.1.1
17814  * Copyright(c) 2006-2007, Ext JS, LLC.
17815  *
17816  * Originally Released Under LGPL - original licence link has changed is not relivant.
17817  *
17818  * Fork - LGPL
17819  * <script type="text/javascript">
17820  */
17821  
17822 /**
17823  * @class Roo.tree.TreeNodeUI
17824  * @constructor
17825  * @param {Object} node The node to render
17826  * The TreeNode UI implementation is separate from the
17827  * tree implementation. Unless you are customizing the tree UI,
17828  * you should never have to use this directly.
17829  */
17830 Roo.tree.TreeNodeUI = function(node){
17831     this.node = node;
17832     this.rendered = false;
17833     this.animating = false;
17834     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17835 };
17836
17837 Roo.tree.TreeNodeUI.prototype = {
17838     removeChild : function(node){
17839         if(this.rendered){
17840             this.ctNode.removeChild(node.ui.getEl());
17841         }
17842     },
17843
17844     beforeLoad : function(){
17845          this.addClass("x-tree-node-loading");
17846     },
17847
17848     afterLoad : function(){
17849          this.removeClass("x-tree-node-loading");
17850     },
17851
17852     onTextChange : function(node, text, oldText){
17853         if(this.rendered){
17854             this.textNode.innerHTML = text;
17855         }
17856     },
17857
17858     onDisableChange : function(node, state){
17859         this.disabled = state;
17860         if(state){
17861             this.addClass("x-tree-node-disabled");
17862         }else{
17863             this.removeClass("x-tree-node-disabled");
17864         }
17865     },
17866
17867     onSelectedChange : function(state){
17868         if(state){
17869             this.focus();
17870             this.addClass("x-tree-selected");
17871         }else{
17872             //this.blur();
17873             this.removeClass("x-tree-selected");
17874         }
17875     },
17876
17877     onMove : function(tree, node, oldParent, newParent, index, refNode){
17878         this.childIndent = null;
17879         if(this.rendered){
17880             var targetNode = newParent.ui.getContainer();
17881             if(!targetNode){//target not rendered
17882                 this.holder = document.createElement("div");
17883                 this.holder.appendChild(this.wrap);
17884                 return;
17885             }
17886             var insertBefore = refNode ? refNode.ui.getEl() : null;
17887             if(insertBefore){
17888                 targetNode.insertBefore(this.wrap, insertBefore);
17889             }else{
17890                 targetNode.appendChild(this.wrap);
17891             }
17892             this.node.renderIndent(true);
17893         }
17894     },
17895
17896     addClass : function(cls){
17897         if(this.elNode){
17898             Roo.fly(this.elNode).addClass(cls);
17899         }
17900     },
17901
17902     removeClass : function(cls){
17903         if(this.elNode){
17904             Roo.fly(this.elNode).removeClass(cls);
17905         }
17906     },
17907
17908     remove : function(){
17909         if(this.rendered){
17910             this.holder = document.createElement("div");
17911             this.holder.appendChild(this.wrap);
17912         }
17913     },
17914
17915     fireEvent : function(){
17916         return this.node.fireEvent.apply(this.node, arguments);
17917     },
17918
17919     initEvents : function(){
17920         this.node.on("move", this.onMove, this);
17921         var E = Roo.EventManager;
17922         var a = this.anchor;
17923
17924         var el = Roo.fly(a, '_treeui');
17925
17926         if(Roo.isOpera){ // opera render bug ignores the CSS
17927             el.setStyle("text-decoration", "none");
17928         }
17929
17930         el.on("click", this.onClick, this);
17931         el.on("dblclick", this.onDblClick, this);
17932
17933         if(this.checkbox){
17934             Roo.EventManager.on(this.checkbox,
17935                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17936         }
17937
17938         el.on("contextmenu", this.onContextMenu, this);
17939
17940         var icon = Roo.fly(this.iconNode);
17941         icon.on("click", this.onClick, this);
17942         icon.on("dblclick", this.onDblClick, this);
17943         icon.on("contextmenu", this.onContextMenu, this);
17944         E.on(this.ecNode, "click", this.ecClick, this, true);
17945
17946         if(this.node.disabled){
17947             this.addClass("x-tree-node-disabled");
17948         }
17949         if(this.node.hidden){
17950             this.addClass("x-tree-node-disabled");
17951         }
17952         var ot = this.node.getOwnerTree();
17953         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17954         if(dd && (!this.node.isRoot || ot.rootVisible)){
17955             Roo.dd.Registry.register(this.elNode, {
17956                 node: this.node,
17957                 handles: this.getDDHandles(),
17958                 isHandle: false
17959             });
17960         }
17961     },
17962
17963     getDDHandles : function(){
17964         return [this.iconNode, this.textNode];
17965     },
17966
17967     hide : function(){
17968         if(this.rendered){
17969             this.wrap.style.display = "none";
17970         }
17971     },
17972
17973     show : function(){
17974         if(this.rendered){
17975             this.wrap.style.display = "";
17976         }
17977     },
17978
17979     onContextMenu : function(e){
17980         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17981             e.preventDefault();
17982             this.focus();
17983             this.fireEvent("contextmenu", this.node, e);
17984         }
17985     },
17986
17987     onClick : function(e){
17988         if(this.dropping){
17989             e.stopEvent();
17990             return;
17991         }
17992         if(this.fireEvent("beforeclick", this.node, e) !== false){
17993             if(!this.disabled && this.node.attributes.href){
17994                 this.fireEvent("click", this.node, e);
17995                 return;
17996             }
17997             e.preventDefault();
17998             if(this.disabled){
17999                 return;
18000             }
18001
18002             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18003                 this.node.toggle();
18004             }
18005
18006             this.fireEvent("click", this.node, e);
18007         }else{
18008             e.stopEvent();
18009         }
18010     },
18011
18012     onDblClick : function(e){
18013         e.preventDefault();
18014         if(this.disabled){
18015             return;
18016         }
18017         if(this.checkbox){
18018             this.toggleCheck();
18019         }
18020         if(!this.animating && this.node.hasChildNodes()){
18021             this.node.toggle();
18022         }
18023         this.fireEvent("dblclick", this.node, e);
18024     },
18025
18026     onCheckChange : function(){
18027         var checked = this.checkbox.checked;
18028         this.node.attributes.checked = checked;
18029         this.fireEvent('checkchange', this.node, checked);
18030     },
18031
18032     ecClick : function(e){
18033         if(!this.animating && this.node.hasChildNodes()){
18034             this.node.toggle();
18035         }
18036     },
18037
18038     startDrop : function(){
18039         this.dropping = true;
18040     },
18041
18042     // delayed drop so the click event doesn't get fired on a drop
18043     endDrop : function(){
18044        setTimeout(function(){
18045            this.dropping = false;
18046        }.createDelegate(this), 50);
18047     },
18048
18049     expand : function(){
18050         this.updateExpandIcon();
18051         this.ctNode.style.display = "";
18052     },
18053
18054     focus : function(){
18055         if(!this.node.preventHScroll){
18056             try{this.anchor.focus();
18057             }catch(e){}
18058         }else if(!Roo.isIE){
18059             try{
18060                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18061                 var l = noscroll.scrollLeft;
18062                 this.anchor.focus();
18063                 noscroll.scrollLeft = l;
18064             }catch(e){}
18065         }
18066     },
18067
18068     toggleCheck : function(value){
18069         var cb = this.checkbox;
18070         if(cb){
18071             cb.checked = (value === undefined ? !cb.checked : value);
18072         }
18073     },
18074
18075     blur : function(){
18076         try{
18077             this.anchor.blur();
18078         }catch(e){}
18079     },
18080
18081     animExpand : function(callback){
18082         var ct = Roo.get(this.ctNode);
18083         ct.stopFx();
18084         if(!this.node.hasChildNodes()){
18085             this.updateExpandIcon();
18086             this.ctNode.style.display = "";
18087             Roo.callback(callback);
18088             return;
18089         }
18090         this.animating = true;
18091         this.updateExpandIcon();
18092
18093         ct.slideIn('t', {
18094            callback : function(){
18095                this.animating = false;
18096                Roo.callback(callback);
18097             },
18098             scope: this,
18099             duration: this.node.ownerTree.duration || .25
18100         });
18101     },
18102
18103     highlight : function(){
18104         var tree = this.node.getOwnerTree();
18105         Roo.fly(this.wrap).highlight(
18106             tree.hlColor || "C3DAF9",
18107             {endColor: tree.hlBaseColor}
18108         );
18109     },
18110
18111     collapse : function(){
18112         this.updateExpandIcon();
18113         this.ctNode.style.display = "none";
18114     },
18115
18116     animCollapse : function(callback){
18117         var ct = Roo.get(this.ctNode);
18118         ct.enableDisplayMode('block');
18119         ct.stopFx();
18120
18121         this.animating = true;
18122         this.updateExpandIcon();
18123
18124         ct.slideOut('t', {
18125             callback : function(){
18126                this.animating = false;
18127                Roo.callback(callback);
18128             },
18129             scope: this,
18130             duration: this.node.ownerTree.duration || .25
18131         });
18132     },
18133
18134     getContainer : function(){
18135         return this.ctNode;
18136     },
18137
18138     getEl : function(){
18139         return this.wrap;
18140     },
18141
18142     appendDDGhost : function(ghostNode){
18143         ghostNode.appendChild(this.elNode.cloneNode(true));
18144     },
18145
18146     getDDRepairXY : function(){
18147         return Roo.lib.Dom.getXY(this.iconNode);
18148     },
18149
18150     onRender : function(){
18151         this.render();
18152     },
18153
18154     render : function(bulkRender){
18155         var n = this.node, a = n.attributes;
18156         var targetNode = n.parentNode ?
18157               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18158
18159         if(!this.rendered){
18160             this.rendered = true;
18161
18162             this.renderElements(n, a, targetNode, bulkRender);
18163
18164             if(a.qtip){
18165                if(this.textNode.setAttributeNS){
18166                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18167                    if(a.qtipTitle){
18168                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18169                    }
18170                }else{
18171                    this.textNode.setAttribute("ext:qtip", a.qtip);
18172                    if(a.qtipTitle){
18173                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18174                    }
18175                }
18176             }else if(a.qtipCfg){
18177                 a.qtipCfg.target = Roo.id(this.textNode);
18178                 Roo.QuickTips.register(a.qtipCfg);
18179             }
18180             this.initEvents();
18181             if(!this.node.expanded){
18182                 this.updateExpandIcon();
18183             }
18184         }else{
18185             if(bulkRender === true) {
18186                 targetNode.appendChild(this.wrap);
18187             }
18188         }
18189     },
18190
18191     renderElements : function(n, a, targetNode, bulkRender)
18192     {
18193         // add some indent caching, this helps performance when rendering a large tree
18194         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18195         var t = n.getOwnerTree();
18196         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18197         if (typeof(n.attributes.html) != 'undefined') {
18198             txt = n.attributes.html;
18199         }
18200         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18201         var cb = typeof a.checked == 'boolean';
18202         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18203         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18204             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18205             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18206             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18207             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18208             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18209              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18210                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18211             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18212             "</li>"];
18213
18214         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18215             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18216                                 n.nextSibling.ui.getEl(), buf.join(""));
18217         }else{
18218             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18219         }
18220
18221         this.elNode = this.wrap.childNodes[0];
18222         this.ctNode = this.wrap.childNodes[1];
18223         var cs = this.elNode.childNodes;
18224         this.indentNode = cs[0];
18225         this.ecNode = cs[1];
18226         this.iconNode = cs[2];
18227         var index = 3;
18228         if(cb){
18229             this.checkbox = cs[3];
18230             index++;
18231         }
18232         this.anchor = cs[index];
18233         this.textNode = cs[index].firstChild;
18234     },
18235
18236     getAnchor : function(){
18237         return this.anchor;
18238     },
18239
18240     getTextEl : function(){
18241         return this.textNode;
18242     },
18243
18244     getIconEl : function(){
18245         return this.iconNode;
18246     },
18247
18248     isChecked : function(){
18249         return this.checkbox ? this.checkbox.checked : false;
18250     },
18251
18252     updateExpandIcon : function(){
18253         if(this.rendered){
18254             var n = this.node, c1, c2;
18255             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18256             var hasChild = n.hasChildNodes();
18257             if(hasChild){
18258                 if(n.expanded){
18259                     cls += "-minus";
18260                     c1 = "x-tree-node-collapsed";
18261                     c2 = "x-tree-node-expanded";
18262                 }else{
18263                     cls += "-plus";
18264                     c1 = "x-tree-node-expanded";
18265                     c2 = "x-tree-node-collapsed";
18266                 }
18267                 if(this.wasLeaf){
18268                     this.removeClass("x-tree-node-leaf");
18269                     this.wasLeaf = false;
18270                 }
18271                 if(this.c1 != c1 || this.c2 != c2){
18272                     Roo.fly(this.elNode).replaceClass(c1, c2);
18273                     this.c1 = c1; this.c2 = c2;
18274                 }
18275             }else{
18276                 // this changes non-leafs into leafs if they have no children.
18277                 // it's not very rational behaviour..
18278                 
18279                 if(!this.wasLeaf && this.node.leaf){
18280                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18281                     delete this.c1;
18282                     delete this.c2;
18283                     this.wasLeaf = true;
18284                 }
18285             }
18286             var ecc = "x-tree-ec-icon "+cls;
18287             if(this.ecc != ecc){
18288                 this.ecNode.className = ecc;
18289                 this.ecc = ecc;
18290             }
18291         }
18292     },
18293
18294     getChildIndent : function(){
18295         if(!this.childIndent){
18296             var buf = [];
18297             var p = this.node;
18298             while(p){
18299                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18300                     if(!p.isLast()) {
18301                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18302                     } else {
18303                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18304                     }
18305                 }
18306                 p = p.parentNode;
18307             }
18308             this.childIndent = buf.join("");
18309         }
18310         return this.childIndent;
18311     },
18312
18313     renderIndent : function(){
18314         if(this.rendered){
18315             var indent = "";
18316             var p = this.node.parentNode;
18317             if(p){
18318                 indent = p.ui.getChildIndent();
18319             }
18320             if(this.indentMarkup != indent){ // don't rerender if not required
18321                 this.indentNode.innerHTML = indent;
18322                 this.indentMarkup = indent;
18323             }
18324             this.updateExpandIcon();
18325         }
18326     }
18327 };
18328
18329 Roo.tree.RootTreeNodeUI = function(){
18330     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18331 };
18332 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18333     render : function(){
18334         if(!this.rendered){
18335             var targetNode = this.node.ownerTree.innerCt.dom;
18336             this.node.expanded = true;
18337             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18338             this.wrap = this.ctNode = targetNode.firstChild;
18339         }
18340     },
18341     collapse : function(){
18342     },
18343     expand : function(){
18344     }
18345 });/*
18346  * Based on:
18347  * Ext JS Library 1.1.1
18348  * Copyright(c) 2006-2007, Ext JS, LLC.
18349  *
18350  * Originally Released Under LGPL - original licence link has changed is not relivant.
18351  *
18352  * Fork - LGPL
18353  * <script type="text/javascript">
18354  */
18355 /**
18356  * @class Roo.tree.TreeLoader
18357  * @extends Roo.util.Observable
18358  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18359  * nodes from a specified URL. The response must be a javascript Array definition
18360  * who's elements are node definition objects. eg:
18361  * <pre><code>
18362    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18363     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18364 </code></pre>
18365  * <br><br>
18366  * A server request is sent, and child nodes are loaded only when a node is expanded.
18367  * The loading node's id is passed to the server under the parameter name "node" to
18368  * enable the server to produce the correct child nodes.
18369  * <br><br>
18370  * To pass extra parameters, an event handler may be attached to the "beforeload"
18371  * event, and the parameters specified in the TreeLoader's baseParams property:
18372  * <pre><code>
18373     myTreeLoader.on("beforeload", function(treeLoader, node) {
18374         this.baseParams.category = node.attributes.category;
18375     }, this);
18376 </code></pre><
18377  * This would pass an HTTP parameter called "category" to the server containing
18378  * the value of the Node's "category" attribute.
18379  * @constructor
18380  * Creates a new Treeloader.
18381  * @param {Object} config A config object containing config properties.
18382  */
18383 Roo.tree.TreeLoader = function(config){
18384     this.baseParams = {};
18385     this.requestMethod = "POST";
18386     Roo.apply(this, config);
18387
18388     this.addEvents({
18389     
18390         /**
18391          * @event beforeload
18392          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18393          * @param {Object} This TreeLoader object.
18394          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18395          * @param {Object} callback The callback function specified in the {@link #load} call.
18396          */
18397         beforeload : true,
18398         /**
18399          * @event load
18400          * Fires when the node has been successfuly loaded.
18401          * @param {Object} This TreeLoader object.
18402          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18403          * @param {Object} response The response object containing the data from the server.
18404          */
18405         load : true,
18406         /**
18407          * @event loadexception
18408          * Fires if the network request failed.
18409          * @param {Object} This TreeLoader object.
18410          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18411          * @param {Object} response The response object containing the data from the server.
18412          */
18413         loadexception : true,
18414         /**
18415          * @event create
18416          * Fires before a node is created, enabling you to return custom Node types 
18417          * @param {Object} This TreeLoader object.
18418          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18419          */
18420         create : true
18421     });
18422
18423     Roo.tree.TreeLoader.superclass.constructor.call(this);
18424 };
18425
18426 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18427     /**
18428     * @cfg {String} dataUrl The URL from which to request a Json string which
18429     * specifies an array of node definition object representing the child nodes
18430     * to be loaded.
18431     */
18432     /**
18433     * @cfg {Object} baseParams (optional) An object containing properties which
18434     * specify HTTP parameters to be passed to each request for child nodes.
18435     */
18436     /**
18437     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18438     * created by this loader. If the attributes sent by the server have an attribute in this object,
18439     * they take priority.
18440     */
18441     /**
18442     * @cfg {Object} uiProviders (optional) An object containing properties which
18443     * 
18444     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18445     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18446     * <i>uiProvider</i> attribute of a returned child node is a string rather
18447     * than a reference to a TreeNodeUI implementation, this that string value
18448     * is used as a property name in the uiProviders object. You can define the provider named
18449     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18450     */
18451     uiProviders : {},
18452
18453     /**
18454     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18455     * child nodes before loading.
18456     */
18457     clearOnLoad : true,
18458
18459     /**
18460     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18461     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18462     * Grid query { data : [ .....] }
18463     */
18464     
18465     root : false,
18466      /**
18467     * @cfg {String} queryParam (optional) 
18468     * Name of the query as it will be passed on the querystring (defaults to 'node')
18469     * eg. the request will be ?node=[id]
18470     */
18471     
18472     
18473     queryParam: false,
18474     
18475     /**
18476      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18477      * This is called automatically when a node is expanded, but may be used to reload
18478      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18479      * @param {Roo.tree.TreeNode} node
18480      * @param {Function} callback
18481      */
18482     load : function(node, callback){
18483         if(this.clearOnLoad){
18484             while(node.firstChild){
18485                 node.removeChild(node.firstChild);
18486             }
18487         }
18488         if(node.attributes.children){ // preloaded json children
18489             var cs = node.attributes.children;
18490             for(var i = 0, len = cs.length; i < len; i++){
18491                 node.appendChild(this.createNode(cs[i]));
18492             }
18493             if(typeof callback == "function"){
18494                 callback();
18495             }
18496         }else if(this.dataUrl){
18497             this.requestData(node, callback);
18498         }
18499     },
18500
18501     getParams: function(node){
18502         var buf = [], bp = this.baseParams;
18503         for(var key in bp){
18504             if(typeof bp[key] != "function"){
18505                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18506             }
18507         }
18508         var n = this.queryParam === false ? 'node' : this.queryParam;
18509         buf.push(n + "=", encodeURIComponent(node.id));
18510         return buf.join("");
18511     },
18512
18513     requestData : function(node, callback){
18514         if(this.fireEvent("beforeload", this, node, callback) !== false){
18515             this.transId = Roo.Ajax.request({
18516                 method:this.requestMethod,
18517                 url: this.dataUrl||this.url,
18518                 success: this.handleResponse,
18519                 failure: this.handleFailure,
18520                 scope: this,
18521                 argument: {callback: callback, node: node},
18522                 params: this.getParams(node)
18523             });
18524         }else{
18525             // if the load is cancelled, make sure we notify
18526             // the node that we are done
18527             if(typeof callback == "function"){
18528                 callback();
18529             }
18530         }
18531     },
18532
18533     isLoading : function(){
18534         return this.transId ? true : false;
18535     },
18536
18537     abort : function(){
18538         if(this.isLoading()){
18539             Roo.Ajax.abort(this.transId);
18540         }
18541     },
18542
18543     // private
18544     createNode : function(attr)
18545     {
18546         // apply baseAttrs, nice idea Corey!
18547         if(this.baseAttrs){
18548             Roo.applyIf(attr, this.baseAttrs);
18549         }
18550         if(this.applyLoader !== false){
18551             attr.loader = this;
18552         }
18553         // uiProvider = depreciated..
18554         
18555         if(typeof(attr.uiProvider) == 'string'){
18556            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18557                 /**  eval:var:attr */ eval(attr.uiProvider);
18558         }
18559         if(typeof(this.uiProviders['default']) != 'undefined') {
18560             attr.uiProvider = this.uiProviders['default'];
18561         }
18562         
18563         this.fireEvent('create', this, attr);
18564         
18565         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18566         return(attr.leaf ?
18567                         new Roo.tree.TreeNode(attr) :
18568                         new Roo.tree.AsyncTreeNode(attr));
18569     },
18570
18571     processResponse : function(response, node, callback)
18572     {
18573         var json = response.responseText;
18574         try {
18575             
18576             var o = Roo.decode(json);
18577             
18578             if (!o.success) {
18579                 // it's a failure condition.
18580                 var a = response.argument;
18581                 this.fireEvent("loadexception", this, a.node, response);
18582                 Roo.log("Load failed - should have a handler really");
18583                 return;
18584             }
18585             
18586             if (this.root !== false) {
18587                 o = o[this.root];
18588             }
18589             
18590             for(var i = 0, len = o.length; i < len; i++){
18591                 var n = this.createNode(o[i]);
18592                 if(n){
18593                     node.appendChild(n);
18594                 }
18595             }
18596             if(typeof callback == "function"){
18597                 callback(this, node);
18598             }
18599         }catch(e){
18600             this.handleFailure(response);
18601         }
18602     },
18603
18604     handleResponse : function(response){
18605         this.transId = false;
18606         var a = response.argument;
18607         this.processResponse(response, a.node, a.callback);
18608         this.fireEvent("load", this, a.node, response);
18609     },
18610
18611     handleFailure : function(response)
18612     {
18613         // should handle failure better..
18614         this.transId = false;
18615         var a = response.argument;
18616         this.fireEvent("loadexception", this, a.node, response);
18617         if(typeof a.callback == "function"){
18618             a.callback(this, a.node);
18619         }
18620     }
18621 });/*
18622  * Based on:
18623  * Ext JS Library 1.1.1
18624  * Copyright(c) 2006-2007, Ext JS, LLC.
18625  *
18626  * Originally Released Under LGPL - original licence link has changed is not relivant.
18627  *
18628  * Fork - LGPL
18629  * <script type="text/javascript">
18630  */
18631
18632 /**
18633 * @class Roo.tree.TreeFilter
18634 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18635 * @param {TreePanel} tree
18636 * @param {Object} config (optional)
18637  */
18638 Roo.tree.TreeFilter = function(tree, config){
18639     this.tree = tree;
18640     this.filtered = {};
18641     Roo.apply(this, config);
18642 };
18643
18644 Roo.tree.TreeFilter.prototype = {
18645     clearBlank:false,
18646     reverse:false,
18647     autoClear:false,
18648     remove:false,
18649
18650      /**
18651      * Filter the data by a specific attribute.
18652      * @param {String/RegExp} value Either string that the attribute value
18653      * should start with or a RegExp to test against the attribute
18654      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18655      * @param {TreeNode} startNode (optional) The node to start the filter at.
18656      */
18657     filter : function(value, attr, startNode){
18658         attr = attr || "text";
18659         var f;
18660         if(typeof value == "string"){
18661             var vlen = value.length;
18662             // auto clear empty filter
18663             if(vlen == 0 && this.clearBlank){
18664                 this.clear();
18665                 return;
18666             }
18667             value = value.toLowerCase();
18668             f = function(n){
18669                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18670             };
18671         }else if(value.exec){ // regex?
18672             f = function(n){
18673                 return value.test(n.attributes[attr]);
18674             };
18675         }else{
18676             throw 'Illegal filter type, must be string or regex';
18677         }
18678         this.filterBy(f, null, startNode);
18679         },
18680
18681     /**
18682      * Filter by a function. The passed function will be called with each
18683      * node in the tree (or from the startNode). If the function returns true, the node is kept
18684      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18685      * @param {Function} fn The filter function
18686      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18687      */
18688     filterBy : function(fn, scope, startNode){
18689         startNode = startNode || this.tree.root;
18690         if(this.autoClear){
18691             this.clear();
18692         }
18693         var af = this.filtered, rv = this.reverse;
18694         var f = function(n){
18695             if(n == startNode){
18696                 return true;
18697             }
18698             if(af[n.id]){
18699                 return false;
18700             }
18701             var m = fn.call(scope || n, n);
18702             if(!m || rv){
18703                 af[n.id] = n;
18704                 n.ui.hide();
18705                 return false;
18706             }
18707             return true;
18708         };
18709         startNode.cascade(f);
18710         if(this.remove){
18711            for(var id in af){
18712                if(typeof id != "function"){
18713                    var n = af[id];
18714                    if(n && n.parentNode){
18715                        n.parentNode.removeChild(n);
18716                    }
18717                }
18718            }
18719         }
18720     },
18721
18722     /**
18723      * Clears the current filter. Note: with the "remove" option
18724      * set a filter cannot be cleared.
18725      */
18726     clear : function(){
18727         var t = this.tree;
18728         var af = this.filtered;
18729         for(var id in af){
18730             if(typeof id != "function"){
18731                 var n = af[id];
18732                 if(n){
18733                     n.ui.show();
18734                 }
18735             }
18736         }
18737         this.filtered = {};
18738     }
18739 };
18740 /*
18741  * Based on:
18742  * Ext JS Library 1.1.1
18743  * Copyright(c) 2006-2007, Ext JS, LLC.
18744  *
18745  * Originally Released Under LGPL - original licence link has changed is not relivant.
18746  *
18747  * Fork - LGPL
18748  * <script type="text/javascript">
18749  */
18750  
18751
18752 /**
18753  * @class Roo.tree.TreeSorter
18754  * Provides sorting of nodes in a TreePanel
18755  * 
18756  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18757  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18758  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18759  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18760  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18761  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18762  * @constructor
18763  * @param {TreePanel} tree
18764  * @param {Object} config
18765  */
18766 Roo.tree.TreeSorter = function(tree, config){
18767     Roo.apply(this, config);
18768     tree.on("beforechildrenrendered", this.doSort, this);
18769     tree.on("append", this.updateSort, this);
18770     tree.on("insert", this.updateSort, this);
18771     
18772     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18773     var p = this.property || "text";
18774     var sortType = this.sortType;
18775     var fs = this.folderSort;
18776     var cs = this.caseSensitive === true;
18777     var leafAttr = this.leafAttr || 'leaf';
18778
18779     this.sortFn = function(n1, n2){
18780         if(fs){
18781             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18782                 return 1;
18783             }
18784             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18785                 return -1;
18786             }
18787         }
18788         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18789         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18790         if(v1 < v2){
18791                         return dsc ? +1 : -1;
18792                 }else if(v1 > v2){
18793                         return dsc ? -1 : +1;
18794         }else{
18795                 return 0;
18796         }
18797     };
18798 };
18799
18800 Roo.tree.TreeSorter.prototype = {
18801     doSort : function(node){
18802         node.sort(this.sortFn);
18803     },
18804     
18805     compareNodes : function(n1, n2){
18806         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18807     },
18808     
18809     updateSort : function(tree, node){
18810         if(node.childrenRendered){
18811             this.doSort.defer(1, this, [node]);
18812         }
18813     }
18814 };/*
18815  * Based on:
18816  * Ext JS Library 1.1.1
18817  * Copyright(c) 2006-2007, Ext JS, LLC.
18818  *
18819  * Originally Released Under LGPL - original licence link has changed is not relivant.
18820  *
18821  * Fork - LGPL
18822  * <script type="text/javascript">
18823  */
18824
18825 if(Roo.dd.DropZone){
18826     
18827 Roo.tree.TreeDropZone = function(tree, config){
18828     this.allowParentInsert = false;
18829     this.allowContainerDrop = false;
18830     this.appendOnly = false;
18831     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18832     this.tree = tree;
18833     this.lastInsertClass = "x-tree-no-status";
18834     this.dragOverData = {};
18835 };
18836
18837 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18838     ddGroup : "TreeDD",
18839     
18840     expandDelay : 1000,
18841     
18842     expandNode : function(node){
18843         if(node.hasChildNodes() && !node.isExpanded()){
18844             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18845         }
18846     },
18847     
18848     queueExpand : function(node){
18849         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18850     },
18851     
18852     cancelExpand : function(){
18853         if(this.expandProcId){
18854             clearTimeout(this.expandProcId);
18855             this.expandProcId = false;
18856         }
18857     },
18858     
18859     isValidDropPoint : function(n, pt, dd, e, data){
18860         if(!n || !data){ return false; }
18861         var targetNode = n.node;
18862         var dropNode = data.node;
18863         // default drop rules
18864         if(!(targetNode && targetNode.isTarget && pt)){
18865             return false;
18866         }
18867         if(pt == "append" && targetNode.allowChildren === false){
18868             return false;
18869         }
18870         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18871             return false;
18872         }
18873         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18874             return false;
18875         }
18876         // reuse the object
18877         var overEvent = this.dragOverData;
18878         overEvent.tree = this.tree;
18879         overEvent.target = targetNode;
18880         overEvent.data = data;
18881         overEvent.point = pt;
18882         overEvent.source = dd;
18883         overEvent.rawEvent = e;
18884         overEvent.dropNode = dropNode;
18885         overEvent.cancel = false;  
18886         var result = this.tree.fireEvent("nodedragover", overEvent);
18887         return overEvent.cancel === false && result !== false;
18888     },
18889     
18890     getDropPoint : function(e, n, dd){
18891         var tn = n.node;
18892         if(tn.isRoot){
18893             return tn.allowChildren !== false ? "append" : false; // always append for root
18894         }
18895         var dragEl = n.ddel;
18896         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18897         var y = Roo.lib.Event.getPageY(e);
18898         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18899         
18900         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18901         var noAppend = tn.allowChildren === false;
18902         if(this.appendOnly || tn.parentNode.allowChildren === false){
18903             return noAppend ? false : "append";
18904         }
18905         var noBelow = false;
18906         if(!this.allowParentInsert){
18907             noBelow = tn.hasChildNodes() && tn.isExpanded();
18908         }
18909         var q = (b - t) / (noAppend ? 2 : 3);
18910         if(y >= t && y < (t + q)){
18911             return "above";
18912         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18913             return "below";
18914         }else{
18915             return "append";
18916         }
18917     },
18918     
18919     onNodeEnter : function(n, dd, e, data){
18920         this.cancelExpand();
18921     },
18922     
18923     onNodeOver : function(n, dd, e, data){
18924         var pt = this.getDropPoint(e, n, dd);
18925         var node = n.node;
18926         
18927         // auto node expand check
18928         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18929             this.queueExpand(node);
18930         }else if(pt != "append"){
18931             this.cancelExpand();
18932         }
18933         
18934         // set the insert point style on the target node
18935         var returnCls = this.dropNotAllowed;
18936         if(this.isValidDropPoint(n, pt, dd, e, data)){
18937            if(pt){
18938                var el = n.ddel;
18939                var cls;
18940                if(pt == "above"){
18941                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18942                    cls = "x-tree-drag-insert-above";
18943                }else if(pt == "below"){
18944                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18945                    cls = "x-tree-drag-insert-below";
18946                }else{
18947                    returnCls = "x-tree-drop-ok-append";
18948                    cls = "x-tree-drag-append";
18949                }
18950                if(this.lastInsertClass != cls){
18951                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18952                    this.lastInsertClass = cls;
18953                }
18954            }
18955        }
18956        return returnCls;
18957     },
18958     
18959     onNodeOut : function(n, dd, e, data){
18960         this.cancelExpand();
18961         this.removeDropIndicators(n);
18962     },
18963     
18964     onNodeDrop : function(n, dd, e, data){
18965         var point = this.getDropPoint(e, n, dd);
18966         var targetNode = n.node;
18967         targetNode.ui.startDrop();
18968         if(!this.isValidDropPoint(n, point, dd, e, data)){
18969             targetNode.ui.endDrop();
18970             return false;
18971         }
18972         // first try to find the drop node
18973         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18974         var dropEvent = {
18975             tree : this.tree,
18976             target: targetNode,
18977             data: data,
18978             point: point,
18979             source: dd,
18980             rawEvent: e,
18981             dropNode: dropNode,
18982             cancel: !dropNode   
18983         };
18984         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18985         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18986             targetNode.ui.endDrop();
18987             return false;
18988         }
18989         // allow target changing
18990         targetNode = dropEvent.target;
18991         if(point == "append" && !targetNode.isExpanded()){
18992             targetNode.expand(false, null, function(){
18993                 this.completeDrop(dropEvent);
18994             }.createDelegate(this));
18995         }else{
18996             this.completeDrop(dropEvent);
18997         }
18998         return true;
18999     },
19000     
19001     completeDrop : function(de){
19002         var ns = de.dropNode, p = de.point, t = de.target;
19003         if(!(ns instanceof Array)){
19004             ns = [ns];
19005         }
19006         var n;
19007         for(var i = 0, len = ns.length; i < len; i++){
19008             n = ns[i];
19009             if(p == "above"){
19010                 t.parentNode.insertBefore(n, t);
19011             }else if(p == "below"){
19012                 t.parentNode.insertBefore(n, t.nextSibling);
19013             }else{
19014                 t.appendChild(n);
19015             }
19016         }
19017         n.ui.focus();
19018         if(this.tree.hlDrop){
19019             n.ui.highlight();
19020         }
19021         t.ui.endDrop();
19022         this.tree.fireEvent("nodedrop", de);
19023     },
19024     
19025     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19026         if(this.tree.hlDrop){
19027             dropNode.ui.focus();
19028             dropNode.ui.highlight();
19029         }
19030         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19031     },
19032     
19033     getTree : function(){
19034         return this.tree;
19035     },
19036     
19037     removeDropIndicators : function(n){
19038         if(n && n.ddel){
19039             var el = n.ddel;
19040             Roo.fly(el).removeClass([
19041                     "x-tree-drag-insert-above",
19042                     "x-tree-drag-insert-below",
19043                     "x-tree-drag-append"]);
19044             this.lastInsertClass = "_noclass";
19045         }
19046     },
19047     
19048     beforeDragDrop : function(target, e, id){
19049         this.cancelExpand();
19050         return true;
19051     },
19052     
19053     afterRepair : function(data){
19054         if(data && Roo.enableFx){
19055             data.node.ui.highlight();
19056         }
19057         this.hideProxy();
19058     }    
19059 });
19060
19061 }
19062 /*
19063  * Based on:
19064  * Ext JS Library 1.1.1
19065  * Copyright(c) 2006-2007, Ext JS, LLC.
19066  *
19067  * Originally Released Under LGPL - original licence link has changed is not relivant.
19068  *
19069  * Fork - LGPL
19070  * <script type="text/javascript">
19071  */
19072  
19073
19074 if(Roo.dd.DragZone){
19075 Roo.tree.TreeDragZone = function(tree, config){
19076     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19077     this.tree = tree;
19078 };
19079
19080 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19081     ddGroup : "TreeDD",
19082     
19083     onBeforeDrag : function(data, e){
19084         var n = data.node;
19085         return n && n.draggable && !n.disabled;
19086     },
19087     
19088     onInitDrag : function(e){
19089         var data = this.dragData;
19090         this.tree.getSelectionModel().select(data.node);
19091         this.proxy.update("");
19092         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19093         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19094     },
19095     
19096     getRepairXY : function(e, data){
19097         return data.node.ui.getDDRepairXY();
19098     },
19099     
19100     onEndDrag : function(data, e){
19101         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19102     },
19103     
19104     onValidDrop : function(dd, e, id){
19105         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19106         this.hideProxy();
19107     },
19108     
19109     beforeInvalidDrop : function(e, id){
19110         // this scrolls the original position back into view
19111         var sm = this.tree.getSelectionModel();
19112         sm.clearSelections();
19113         sm.select(this.dragData.node);
19114     }
19115 });
19116 }/*
19117  * Based on:
19118  * Ext JS Library 1.1.1
19119  * Copyright(c) 2006-2007, Ext JS, LLC.
19120  *
19121  * Originally Released Under LGPL - original licence link has changed is not relivant.
19122  *
19123  * Fork - LGPL
19124  * <script type="text/javascript">
19125  */
19126 /**
19127  * @class Roo.tree.TreeEditor
19128  * @extends Roo.Editor
19129  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19130  * as the editor field.
19131  * @constructor
19132  * @param {Object} config (used to be the tree panel.)
19133  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19134  * 
19135  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19136  * @cfg {Roo.form.TextField|Object} field The field configuration
19137  *
19138  * 
19139  */
19140 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19141     var tree = config;
19142     var field;
19143     if (oldconfig) { // old style..
19144         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19145     } else {
19146         // new style..
19147         tree = config.tree;
19148         config.field = config.field  || {};
19149         config.field.xtype = 'TextField';
19150         field = Roo.factory(config.field, Roo.form);
19151     }
19152     config = config || {};
19153     
19154     
19155     this.addEvents({
19156         /**
19157          * @event beforenodeedit
19158          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19159          * false from the handler of this event.
19160          * @param {Editor} this
19161          * @param {Roo.tree.Node} node 
19162          */
19163         "beforenodeedit" : true
19164     });
19165     
19166     //Roo.log(config);
19167     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19168
19169     this.tree = tree;
19170
19171     tree.on('beforeclick', this.beforeNodeClick, this);
19172     tree.getTreeEl().on('mousedown', this.hide, this);
19173     this.on('complete', this.updateNode, this);
19174     this.on('beforestartedit', this.fitToTree, this);
19175     this.on('startedit', this.bindScroll, this, {delay:10});
19176     this.on('specialkey', this.onSpecialKey, this);
19177 };
19178
19179 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19180     /**
19181      * @cfg {String} alignment
19182      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19183      */
19184     alignment: "l-l",
19185     // inherit
19186     autoSize: false,
19187     /**
19188      * @cfg {Boolean} hideEl
19189      * True to hide the bound element while the editor is displayed (defaults to false)
19190      */
19191     hideEl : false,
19192     /**
19193      * @cfg {String} cls
19194      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19195      */
19196     cls: "x-small-editor x-tree-editor",
19197     /**
19198      * @cfg {Boolean} shim
19199      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19200      */
19201     shim:false,
19202     // inherit
19203     shadow:"frame",
19204     /**
19205      * @cfg {Number} maxWidth
19206      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19207      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19208      * scroll and client offsets into account prior to each edit.
19209      */
19210     maxWidth: 250,
19211
19212     editDelay : 350,
19213
19214     // private
19215     fitToTree : function(ed, el){
19216         var td = this.tree.getTreeEl().dom, nd = el.dom;
19217         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19218             td.scrollLeft = nd.offsetLeft;
19219         }
19220         var w = Math.min(
19221                 this.maxWidth,
19222                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19223         this.setSize(w, '');
19224         
19225         return this.fireEvent('beforenodeedit', this, this.editNode);
19226         
19227     },
19228
19229     // private
19230     triggerEdit : function(node){
19231         this.completeEdit();
19232         this.editNode = node;
19233         this.startEdit(node.ui.textNode, node.text);
19234     },
19235
19236     // private
19237     bindScroll : function(){
19238         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19239     },
19240
19241     // private
19242     beforeNodeClick : function(node, e){
19243         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19244         this.lastClick = new Date();
19245         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19246             e.stopEvent();
19247             this.triggerEdit(node);
19248             return false;
19249         }
19250         return true;
19251     },
19252
19253     // private
19254     updateNode : function(ed, value){
19255         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19256         this.editNode.setText(value);
19257     },
19258
19259     // private
19260     onHide : function(){
19261         Roo.tree.TreeEditor.superclass.onHide.call(this);
19262         if(this.editNode){
19263             this.editNode.ui.focus();
19264         }
19265     },
19266
19267     // private
19268     onSpecialKey : function(field, e){
19269         var k = e.getKey();
19270         if(k == e.ESC){
19271             e.stopEvent();
19272             this.cancelEdit();
19273         }else if(k == e.ENTER && !e.hasModifier()){
19274             e.stopEvent();
19275             this.completeEdit();
19276         }
19277     }
19278 });//<Script type="text/javascript">
19279 /*
19280  * Based on:
19281  * Ext JS Library 1.1.1
19282  * Copyright(c) 2006-2007, Ext JS, LLC.
19283  *
19284  * Originally Released Under LGPL - original licence link has changed is not relivant.
19285  *
19286  * Fork - LGPL
19287  * <script type="text/javascript">
19288  */
19289  
19290 /**
19291  * Not documented??? - probably should be...
19292  */
19293
19294 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19295     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19296     
19297     renderElements : function(n, a, targetNode, bulkRender){
19298         //consel.log("renderElements?");
19299         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19300
19301         var t = n.getOwnerTree();
19302         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19303         
19304         var cols = t.columns;
19305         var bw = t.borderWidth;
19306         var c = cols[0];
19307         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19308          var cb = typeof a.checked == "boolean";
19309         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19310         var colcls = 'x-t-' + tid + '-c0';
19311         var buf = [
19312             '<li class="x-tree-node">',
19313             
19314                 
19315                 '<div class="x-tree-node-el ', a.cls,'">',
19316                     // extran...
19317                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19318                 
19319                 
19320                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19321                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19322                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19323                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19324                            (a.iconCls ? ' '+a.iconCls : ''),
19325                            '" unselectable="on" />',
19326                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19327                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19328                              
19329                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19330                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19331                             '<span unselectable="on" qtip="' + tx + '">',
19332                              tx,
19333                              '</span></a>' ,
19334                     '</div>',
19335                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19336                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19337                  ];
19338         for(var i = 1, len = cols.length; i < len; i++){
19339             c = cols[i];
19340             colcls = 'x-t-' + tid + '-c' +i;
19341             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19342             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19343                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19344                       "</div>");
19345          }
19346          
19347          buf.push(
19348             '</a>',
19349             '<div class="x-clear"></div></div>',
19350             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19351             "</li>");
19352         
19353         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19354             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19355                                 n.nextSibling.ui.getEl(), buf.join(""));
19356         }else{
19357             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19358         }
19359         var el = this.wrap.firstChild;
19360         this.elRow = el;
19361         this.elNode = el.firstChild;
19362         this.ranchor = el.childNodes[1];
19363         this.ctNode = this.wrap.childNodes[1];
19364         var cs = el.firstChild.childNodes;
19365         this.indentNode = cs[0];
19366         this.ecNode = cs[1];
19367         this.iconNode = cs[2];
19368         var index = 3;
19369         if(cb){
19370             this.checkbox = cs[3];
19371             index++;
19372         }
19373         this.anchor = cs[index];
19374         
19375         this.textNode = cs[index].firstChild;
19376         
19377         //el.on("click", this.onClick, this);
19378         //el.on("dblclick", this.onDblClick, this);
19379         
19380         
19381        // console.log(this);
19382     },
19383     initEvents : function(){
19384         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19385         
19386             
19387         var a = this.ranchor;
19388
19389         var el = Roo.get(a);
19390
19391         if(Roo.isOpera){ // opera render bug ignores the CSS
19392             el.setStyle("text-decoration", "none");
19393         }
19394
19395         el.on("click", this.onClick, this);
19396         el.on("dblclick", this.onDblClick, this);
19397         el.on("contextmenu", this.onContextMenu, this);
19398         
19399     },
19400     
19401     /*onSelectedChange : function(state){
19402         if(state){
19403             this.focus();
19404             this.addClass("x-tree-selected");
19405         }else{
19406             //this.blur();
19407             this.removeClass("x-tree-selected");
19408         }
19409     },*/
19410     addClass : function(cls){
19411         if(this.elRow){
19412             Roo.fly(this.elRow).addClass(cls);
19413         }
19414         
19415     },
19416     
19417     
19418     removeClass : function(cls){
19419         if(this.elRow){
19420             Roo.fly(this.elRow).removeClass(cls);
19421         }
19422     }
19423
19424     
19425     
19426 });//<Script type="text/javascript">
19427
19428 /*
19429  * Based on:
19430  * Ext JS Library 1.1.1
19431  * Copyright(c) 2006-2007, Ext JS, LLC.
19432  *
19433  * Originally Released Under LGPL - original licence link has changed is not relivant.
19434  *
19435  * Fork - LGPL
19436  * <script type="text/javascript">
19437  */
19438  
19439
19440 /**
19441  * @class Roo.tree.ColumnTree
19442  * @extends Roo.data.TreePanel
19443  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19444  * @cfg {int} borderWidth  compined right/left border allowance
19445  * @constructor
19446  * @param {String/HTMLElement/Element} el The container element
19447  * @param {Object} config
19448  */
19449 Roo.tree.ColumnTree =  function(el, config)
19450 {
19451    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19452    this.addEvents({
19453         /**
19454         * @event resize
19455         * Fire this event on a container when it resizes
19456         * @param {int} w Width
19457         * @param {int} h Height
19458         */
19459        "resize" : true
19460     });
19461     this.on('resize', this.onResize, this);
19462 };
19463
19464 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19465     //lines:false,
19466     
19467     
19468     borderWidth: Roo.isBorderBox ? 0 : 2, 
19469     headEls : false,
19470     
19471     render : function(){
19472         // add the header.....
19473        
19474         Roo.tree.ColumnTree.superclass.render.apply(this);
19475         
19476         this.el.addClass('x-column-tree');
19477         
19478         this.headers = this.el.createChild(
19479             {cls:'x-tree-headers'},this.innerCt.dom);
19480    
19481         var cols = this.columns, c;
19482         var totalWidth = 0;
19483         this.headEls = [];
19484         var  len = cols.length;
19485         for(var i = 0; i < len; i++){
19486              c = cols[i];
19487              totalWidth += c.width;
19488             this.headEls.push(this.headers.createChild({
19489                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19490                  cn: {
19491                      cls:'x-tree-hd-text',
19492                      html: c.header
19493                  },
19494                  style:'width:'+(c.width-this.borderWidth)+'px;'
19495              }));
19496         }
19497         this.headers.createChild({cls:'x-clear'});
19498         // prevent floats from wrapping when clipped
19499         this.headers.setWidth(totalWidth);
19500         //this.innerCt.setWidth(totalWidth);
19501         this.innerCt.setStyle({ overflow: 'auto' });
19502         this.onResize(this.width, this.height);
19503              
19504         
19505     },
19506     onResize : function(w,h)
19507     {
19508         this.height = h;
19509         this.width = w;
19510         // resize cols..
19511         this.innerCt.setWidth(this.width);
19512         this.innerCt.setHeight(this.height-20);
19513         
19514         // headers...
19515         var cols = this.columns, c;
19516         var totalWidth = 0;
19517         var expEl = false;
19518         var len = cols.length;
19519         for(var i = 0; i < len; i++){
19520             c = cols[i];
19521             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19522                 // it's the expander..
19523                 expEl  = this.headEls[i];
19524                 continue;
19525             }
19526             totalWidth += c.width;
19527             
19528         }
19529         if (expEl) {
19530             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19531         }
19532         this.headers.setWidth(w-20);
19533
19534         
19535         
19536         
19537     }
19538 });
19539 /*
19540  * Based on:
19541  * Ext JS Library 1.1.1
19542  * Copyright(c) 2006-2007, Ext JS, LLC.
19543  *
19544  * Originally Released Under LGPL - original licence link has changed is not relivant.
19545  *
19546  * Fork - LGPL
19547  * <script type="text/javascript">
19548  */
19549  
19550 /**
19551  * @class Roo.menu.Menu
19552  * @extends Roo.util.Observable
19553  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19554  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19555  * @constructor
19556  * Creates a new Menu
19557  * @param {Object} config Configuration options
19558  */
19559 Roo.menu.Menu = function(config){
19560     Roo.apply(this, config);
19561     this.id = this.id || Roo.id();
19562     this.addEvents({
19563         /**
19564          * @event beforeshow
19565          * Fires before this menu is displayed
19566          * @param {Roo.menu.Menu} this
19567          */
19568         beforeshow : true,
19569         /**
19570          * @event beforehide
19571          * Fires before this menu is hidden
19572          * @param {Roo.menu.Menu} this
19573          */
19574         beforehide : true,
19575         /**
19576          * @event show
19577          * Fires after this menu is displayed
19578          * @param {Roo.menu.Menu} this
19579          */
19580         show : true,
19581         /**
19582          * @event hide
19583          * Fires after this menu is hidden
19584          * @param {Roo.menu.Menu} this
19585          */
19586         hide : true,
19587         /**
19588          * @event click
19589          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19590          * @param {Roo.menu.Menu} this
19591          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19592          * @param {Roo.EventObject} e
19593          */
19594         click : true,
19595         /**
19596          * @event mouseover
19597          * Fires when the mouse is hovering over this menu
19598          * @param {Roo.menu.Menu} this
19599          * @param {Roo.EventObject} e
19600          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19601          */
19602         mouseover : true,
19603         /**
19604          * @event mouseout
19605          * Fires when the mouse exits this menu
19606          * @param {Roo.menu.Menu} this
19607          * @param {Roo.EventObject} e
19608          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19609          */
19610         mouseout : true,
19611         /**
19612          * @event itemclick
19613          * Fires when a menu item contained in this menu is clicked
19614          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19615          * @param {Roo.EventObject} e
19616          */
19617         itemclick: true
19618     });
19619     if (this.registerMenu) {
19620         Roo.menu.MenuMgr.register(this);
19621     }
19622     
19623     var mis = this.items;
19624     this.items = new Roo.util.MixedCollection();
19625     if(mis){
19626         this.add.apply(this, mis);
19627     }
19628 };
19629
19630 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19631     /**
19632      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19633      */
19634     minWidth : 120,
19635     /**
19636      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19637      * for bottom-right shadow (defaults to "sides")
19638      */
19639     shadow : "sides",
19640     /**
19641      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19642      * this menu (defaults to "tl-tr?")
19643      */
19644     subMenuAlign : "tl-tr?",
19645     /**
19646      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19647      * relative to its element of origin (defaults to "tl-bl?")
19648      */
19649     defaultAlign : "tl-bl?",
19650     /**
19651      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19652      */
19653     allowOtherMenus : false,
19654     /**
19655      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19656      */
19657     registerMenu : true,
19658
19659     hidden:true,
19660
19661     // private
19662     render : function(){
19663         if(this.el){
19664             return;
19665         }
19666         var el = this.el = new Roo.Layer({
19667             cls: "x-menu",
19668             shadow:this.shadow,
19669             constrain: false,
19670             parentEl: this.parentEl || document.body,
19671             zindex:15000
19672         });
19673
19674         this.keyNav = new Roo.menu.MenuNav(this);
19675
19676         if(this.plain){
19677             el.addClass("x-menu-plain");
19678         }
19679         if(this.cls){
19680             el.addClass(this.cls);
19681         }
19682         // generic focus element
19683         this.focusEl = el.createChild({
19684             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19685         });
19686         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19687         ul.on("click", this.onClick, this);
19688         ul.on("mouseover", this.onMouseOver, this);
19689         ul.on("mouseout", this.onMouseOut, this);
19690         this.items.each(function(item){
19691             var li = document.createElement("li");
19692             li.className = "x-menu-list-item";
19693             ul.dom.appendChild(li);
19694             item.render(li, this);
19695         }, this);
19696         this.ul = ul;
19697         this.autoWidth();
19698     },
19699
19700     // private
19701     autoWidth : function(){
19702         var el = this.el, ul = this.ul;
19703         if(!el){
19704             return;
19705         }
19706         var w = this.width;
19707         if(w){
19708             el.setWidth(w);
19709         }else if(Roo.isIE){
19710             el.setWidth(this.minWidth);
19711             var t = el.dom.offsetWidth; // force recalc
19712             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19713         }
19714     },
19715
19716     // private
19717     delayAutoWidth : function(){
19718         if(this.rendered){
19719             if(!this.awTask){
19720                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19721             }
19722             this.awTask.delay(20);
19723         }
19724     },
19725
19726     // private
19727     findTargetItem : function(e){
19728         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19729         if(t && t.menuItemId){
19730             return this.items.get(t.menuItemId);
19731         }
19732     },
19733
19734     // private
19735     onClick : function(e){
19736         var t;
19737         if(t = this.findTargetItem(e)){
19738             t.onClick(e);
19739             this.fireEvent("click", this, t, e);
19740         }
19741     },
19742
19743     // private
19744     setActiveItem : function(item, autoExpand){
19745         if(item != this.activeItem){
19746             if(this.activeItem){
19747                 this.activeItem.deactivate();
19748             }
19749             this.activeItem = item;
19750             item.activate(autoExpand);
19751         }else if(autoExpand){
19752             item.expandMenu();
19753         }
19754     },
19755
19756     // private
19757     tryActivate : function(start, step){
19758         var items = this.items;
19759         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19760             var item = items.get(i);
19761             if(!item.disabled && item.canActivate){
19762                 this.setActiveItem(item, false);
19763                 return item;
19764             }
19765         }
19766         return false;
19767     },
19768
19769     // private
19770     onMouseOver : function(e){
19771         var t;
19772         if(t = this.findTargetItem(e)){
19773             if(t.canActivate && !t.disabled){
19774                 this.setActiveItem(t, true);
19775             }
19776         }
19777         this.fireEvent("mouseover", this, e, t);
19778     },
19779
19780     // private
19781     onMouseOut : function(e){
19782         var t;
19783         if(t = this.findTargetItem(e)){
19784             if(t == this.activeItem && t.shouldDeactivate(e)){
19785                 this.activeItem.deactivate();
19786                 delete this.activeItem;
19787             }
19788         }
19789         this.fireEvent("mouseout", this, e, t);
19790     },
19791
19792     /**
19793      * Read-only.  Returns true if the menu is currently displayed, else false.
19794      * @type Boolean
19795      */
19796     isVisible : function(){
19797         return this.el && !this.hidden;
19798     },
19799
19800     /**
19801      * Displays this menu relative to another element
19802      * @param {String/HTMLElement/Roo.Element} element The element to align to
19803      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19804      * the element (defaults to this.defaultAlign)
19805      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19806      */
19807     show : function(el, pos, parentMenu){
19808         this.parentMenu = parentMenu;
19809         if(!this.el){
19810             this.render();
19811         }
19812         this.fireEvent("beforeshow", this);
19813         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19814     },
19815
19816     /**
19817      * Displays this menu at a specific xy position
19818      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19819      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19820      */
19821     showAt : function(xy, parentMenu, /* private: */_e){
19822         this.parentMenu = parentMenu;
19823         if(!this.el){
19824             this.render();
19825         }
19826         if(_e !== false){
19827             this.fireEvent("beforeshow", this);
19828             xy = this.el.adjustForConstraints(xy);
19829         }
19830         this.el.setXY(xy);
19831         this.el.show();
19832         this.hidden = false;
19833         this.focus();
19834         this.fireEvent("show", this);
19835     },
19836
19837     focus : function(){
19838         if(!this.hidden){
19839             this.doFocus.defer(50, this);
19840         }
19841     },
19842
19843     doFocus : function(){
19844         if(!this.hidden){
19845             this.focusEl.focus();
19846         }
19847     },
19848
19849     /**
19850      * Hides this menu and optionally all parent menus
19851      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19852      */
19853     hide : function(deep){
19854         if(this.el && this.isVisible()){
19855             this.fireEvent("beforehide", this);
19856             if(this.activeItem){
19857                 this.activeItem.deactivate();
19858                 this.activeItem = null;
19859             }
19860             this.el.hide();
19861             this.hidden = true;
19862             this.fireEvent("hide", this);
19863         }
19864         if(deep === true && this.parentMenu){
19865             this.parentMenu.hide(true);
19866         }
19867     },
19868
19869     /**
19870      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19871      * Any of the following are valid:
19872      * <ul>
19873      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19874      * <li>An HTMLElement object which will be converted to a menu item</li>
19875      * <li>A menu item config object that will be created as a new menu item</li>
19876      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19877      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19878      * </ul>
19879      * Usage:
19880      * <pre><code>
19881 // Create the menu
19882 var menu = new Roo.menu.Menu();
19883
19884 // Create a menu item to add by reference
19885 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19886
19887 // Add a bunch of items at once using different methods.
19888 // Only the last item added will be returned.
19889 var item = menu.add(
19890     menuItem,                // add existing item by ref
19891     'Dynamic Item',          // new TextItem
19892     '-',                     // new separator
19893     { text: 'Config Item' }  // new item by config
19894 );
19895 </code></pre>
19896      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19897      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19898      */
19899     add : function(){
19900         var a = arguments, l = a.length, item;
19901         for(var i = 0; i < l; i++){
19902             var el = a[i];
19903             if ((typeof(el) == "object") && el.xtype && el.xns) {
19904                 el = Roo.factory(el, Roo.menu);
19905             }
19906             
19907             if(el.render){ // some kind of Item
19908                 item = this.addItem(el);
19909             }else if(typeof el == "string"){ // string
19910                 if(el == "separator" || el == "-"){
19911                     item = this.addSeparator();
19912                 }else{
19913                     item = this.addText(el);
19914                 }
19915             }else if(el.tagName || el.el){ // element
19916                 item = this.addElement(el);
19917             }else if(typeof el == "object"){ // must be menu item config?
19918                 item = this.addMenuItem(el);
19919             }
19920         }
19921         return item;
19922     },
19923
19924     /**
19925      * Returns this menu's underlying {@link Roo.Element} object
19926      * @return {Roo.Element} The element
19927      */
19928     getEl : function(){
19929         if(!this.el){
19930             this.render();
19931         }
19932         return this.el;
19933     },
19934
19935     /**
19936      * Adds a separator bar to the menu
19937      * @return {Roo.menu.Item} The menu item that was added
19938      */
19939     addSeparator : function(){
19940         return this.addItem(new Roo.menu.Separator());
19941     },
19942
19943     /**
19944      * Adds an {@link Roo.Element} object to the menu
19945      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19946      * @return {Roo.menu.Item} The menu item that was added
19947      */
19948     addElement : function(el){
19949         return this.addItem(new Roo.menu.BaseItem(el));
19950     },
19951
19952     /**
19953      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19954      * @param {Roo.menu.Item} item The menu item to add
19955      * @return {Roo.menu.Item} The menu item that was added
19956      */
19957     addItem : function(item){
19958         this.items.add(item);
19959         if(this.ul){
19960             var li = document.createElement("li");
19961             li.className = "x-menu-list-item";
19962             this.ul.dom.appendChild(li);
19963             item.render(li, this);
19964             this.delayAutoWidth();
19965         }
19966         return item;
19967     },
19968
19969     /**
19970      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19971      * @param {Object} config A MenuItem config object
19972      * @return {Roo.menu.Item} The menu item that was added
19973      */
19974     addMenuItem : function(config){
19975         if(!(config instanceof Roo.menu.Item)){
19976             if(typeof config.checked == "boolean"){ // must be check menu item config?
19977                 config = new Roo.menu.CheckItem(config);
19978             }else{
19979                 config = new Roo.menu.Item(config);
19980             }
19981         }
19982         return this.addItem(config);
19983     },
19984
19985     /**
19986      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19987      * @param {String} text The text to display in the menu item
19988      * @return {Roo.menu.Item} The menu item that was added
19989      */
19990     addText : function(text){
19991         return this.addItem(new Roo.menu.TextItem({ text : text }));
19992     },
19993
19994     /**
19995      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19996      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19997      * @param {Roo.menu.Item} item The menu item to add
19998      * @return {Roo.menu.Item} The menu item that was added
19999      */
20000     insert : function(index, item){
20001         this.items.insert(index, item);
20002         if(this.ul){
20003             var li = document.createElement("li");
20004             li.className = "x-menu-list-item";
20005             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20006             item.render(li, this);
20007             this.delayAutoWidth();
20008         }
20009         return item;
20010     },
20011
20012     /**
20013      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20014      * @param {Roo.menu.Item} item The menu item to remove
20015      */
20016     remove : function(item){
20017         this.items.removeKey(item.id);
20018         item.destroy();
20019     },
20020
20021     /**
20022      * Removes and destroys all items in the menu
20023      */
20024     removeAll : function(){
20025         var f;
20026         while(f = this.items.first()){
20027             this.remove(f);
20028         }
20029     }
20030 });
20031
20032 // MenuNav is a private utility class used internally by the Menu
20033 Roo.menu.MenuNav = function(menu){
20034     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20035     this.scope = this.menu = menu;
20036 };
20037
20038 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20039     doRelay : function(e, h){
20040         var k = e.getKey();
20041         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20042             this.menu.tryActivate(0, 1);
20043             return false;
20044         }
20045         return h.call(this.scope || this, e, this.menu);
20046     },
20047
20048     up : function(e, m){
20049         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20050             m.tryActivate(m.items.length-1, -1);
20051         }
20052     },
20053
20054     down : function(e, m){
20055         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20056             m.tryActivate(0, 1);
20057         }
20058     },
20059
20060     right : function(e, m){
20061         if(m.activeItem){
20062             m.activeItem.expandMenu(true);
20063         }
20064     },
20065
20066     left : function(e, m){
20067         m.hide();
20068         if(m.parentMenu && m.parentMenu.activeItem){
20069             m.parentMenu.activeItem.activate();
20070         }
20071     },
20072
20073     enter : function(e, m){
20074         if(m.activeItem){
20075             e.stopPropagation();
20076             m.activeItem.onClick(e);
20077             m.fireEvent("click", this, m.activeItem);
20078             return true;
20079         }
20080     }
20081 });/*
20082  * Based on:
20083  * Ext JS Library 1.1.1
20084  * Copyright(c) 2006-2007, Ext JS, LLC.
20085  *
20086  * Originally Released Under LGPL - original licence link has changed is not relivant.
20087  *
20088  * Fork - LGPL
20089  * <script type="text/javascript">
20090  */
20091  
20092 /**
20093  * @class Roo.menu.MenuMgr
20094  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20095  * @singleton
20096  */
20097 Roo.menu.MenuMgr = function(){
20098    var menus, active, groups = {}, attached = false, lastShow = new Date();
20099
20100    // private - called when first menu is created
20101    function init(){
20102        menus = {};
20103        active = new Roo.util.MixedCollection();
20104        Roo.get(document).addKeyListener(27, function(){
20105            if(active.length > 0){
20106                hideAll();
20107            }
20108        });
20109    }
20110
20111    // private
20112    function hideAll(){
20113        if(active && active.length > 0){
20114            var c = active.clone();
20115            c.each(function(m){
20116                m.hide();
20117            });
20118        }
20119    }
20120
20121    // private
20122    function onHide(m){
20123        active.remove(m);
20124        if(active.length < 1){
20125            Roo.get(document).un("mousedown", onMouseDown);
20126            attached = false;
20127        }
20128    }
20129
20130    // private
20131    function onShow(m){
20132        var last = active.last();
20133        lastShow = new Date();
20134        active.add(m);
20135        if(!attached){
20136            Roo.get(document).on("mousedown", onMouseDown);
20137            attached = true;
20138        }
20139        if(m.parentMenu){
20140           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20141           m.parentMenu.activeChild = m;
20142        }else if(last && last.isVisible()){
20143           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20144        }
20145    }
20146
20147    // private
20148    function onBeforeHide(m){
20149        if(m.activeChild){
20150            m.activeChild.hide();
20151        }
20152        if(m.autoHideTimer){
20153            clearTimeout(m.autoHideTimer);
20154            delete m.autoHideTimer;
20155        }
20156    }
20157
20158    // private
20159    function onBeforeShow(m){
20160        var pm = m.parentMenu;
20161        if(!pm && !m.allowOtherMenus){
20162            hideAll();
20163        }else if(pm && pm.activeChild && active != m){
20164            pm.activeChild.hide();
20165        }
20166    }
20167
20168    // private
20169    function onMouseDown(e){
20170        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20171            hideAll();
20172        }
20173    }
20174
20175    // private
20176    function onBeforeCheck(mi, state){
20177        if(state){
20178            var g = groups[mi.group];
20179            for(var i = 0, l = g.length; i < l; i++){
20180                if(g[i] != mi){
20181                    g[i].setChecked(false);
20182                }
20183            }
20184        }
20185    }
20186
20187    return {
20188
20189        /**
20190         * Hides all menus that are currently visible
20191         */
20192        hideAll : function(){
20193             hideAll();  
20194        },
20195
20196        // private
20197        register : function(menu){
20198            if(!menus){
20199                init();
20200            }
20201            menus[menu.id] = menu;
20202            menu.on("beforehide", onBeforeHide);
20203            menu.on("hide", onHide);
20204            menu.on("beforeshow", onBeforeShow);
20205            menu.on("show", onShow);
20206            var g = menu.group;
20207            if(g && menu.events["checkchange"]){
20208                if(!groups[g]){
20209                    groups[g] = [];
20210                }
20211                groups[g].push(menu);
20212                menu.on("checkchange", onCheck);
20213            }
20214        },
20215
20216         /**
20217          * Returns a {@link Roo.menu.Menu} object
20218          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20219          * be used to generate and return a new Menu instance.
20220          */
20221        get : function(menu){
20222            if(typeof menu == "string"){ // menu id
20223                return menus[menu];
20224            }else if(menu.events){  // menu instance
20225                return menu;
20226            }else if(typeof menu.length == 'number'){ // array of menu items?
20227                return new Roo.menu.Menu({items:menu});
20228            }else{ // otherwise, must be a config
20229                return new Roo.menu.Menu(menu);
20230            }
20231        },
20232
20233        // private
20234        unregister : function(menu){
20235            delete menus[menu.id];
20236            menu.un("beforehide", onBeforeHide);
20237            menu.un("hide", onHide);
20238            menu.un("beforeshow", onBeforeShow);
20239            menu.un("show", onShow);
20240            var g = menu.group;
20241            if(g && menu.events["checkchange"]){
20242                groups[g].remove(menu);
20243                menu.un("checkchange", onCheck);
20244            }
20245        },
20246
20247        // private
20248        registerCheckable : function(menuItem){
20249            var g = menuItem.group;
20250            if(g){
20251                if(!groups[g]){
20252                    groups[g] = [];
20253                }
20254                groups[g].push(menuItem);
20255                menuItem.on("beforecheckchange", onBeforeCheck);
20256            }
20257        },
20258
20259        // private
20260        unregisterCheckable : function(menuItem){
20261            var g = menuItem.group;
20262            if(g){
20263                groups[g].remove(menuItem);
20264                menuItem.un("beforecheckchange", onBeforeCheck);
20265            }
20266        }
20267    };
20268 }();/*
20269  * Based on:
20270  * Ext JS Library 1.1.1
20271  * Copyright(c) 2006-2007, Ext JS, LLC.
20272  *
20273  * Originally Released Under LGPL - original licence link has changed is not relivant.
20274  *
20275  * Fork - LGPL
20276  * <script type="text/javascript">
20277  */
20278  
20279
20280 /**
20281  * @class Roo.menu.BaseItem
20282  * @extends Roo.Component
20283  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20284  * management and base configuration options shared by all menu components.
20285  * @constructor
20286  * Creates a new BaseItem
20287  * @param {Object} config Configuration options
20288  */
20289 Roo.menu.BaseItem = function(config){
20290     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20291
20292     this.addEvents({
20293         /**
20294          * @event click
20295          * Fires when this item is clicked
20296          * @param {Roo.menu.BaseItem} this
20297          * @param {Roo.EventObject} e
20298          */
20299         click: true,
20300         /**
20301          * @event activate
20302          * Fires when this item is activated
20303          * @param {Roo.menu.BaseItem} this
20304          */
20305         activate : true,
20306         /**
20307          * @event deactivate
20308          * Fires when this item is deactivated
20309          * @param {Roo.menu.BaseItem} this
20310          */
20311         deactivate : true
20312     });
20313
20314     if(this.handler){
20315         this.on("click", this.handler, this.scope, true);
20316     }
20317 };
20318
20319 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20320     /**
20321      * @cfg {Function} handler
20322      * A function that will handle the click event of this menu item (defaults to undefined)
20323      */
20324     /**
20325      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20326      */
20327     canActivate : false,
20328     /**
20329      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20330      */
20331     activeClass : "x-menu-item-active",
20332     /**
20333      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20334      */
20335     hideOnClick : true,
20336     /**
20337      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20338      */
20339     hideDelay : 100,
20340
20341     // private
20342     ctype: "Roo.menu.BaseItem",
20343
20344     // private
20345     actionMode : "container",
20346
20347     // private
20348     render : function(container, parentMenu){
20349         this.parentMenu = parentMenu;
20350         Roo.menu.BaseItem.superclass.render.call(this, container);
20351         this.container.menuItemId = this.id;
20352     },
20353
20354     // private
20355     onRender : function(container, position){
20356         this.el = Roo.get(this.el);
20357         container.dom.appendChild(this.el.dom);
20358     },
20359
20360     // private
20361     onClick : function(e){
20362         if(!this.disabled && this.fireEvent("click", this, e) !== false
20363                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20364             this.handleClick(e);
20365         }else{
20366             e.stopEvent();
20367         }
20368     },
20369
20370     // private
20371     activate : function(){
20372         if(this.disabled){
20373             return false;
20374         }
20375         var li = this.container;
20376         li.addClass(this.activeClass);
20377         this.region = li.getRegion().adjust(2, 2, -2, -2);
20378         this.fireEvent("activate", this);
20379         return true;
20380     },
20381
20382     // private
20383     deactivate : function(){
20384         this.container.removeClass(this.activeClass);
20385         this.fireEvent("deactivate", this);
20386     },
20387
20388     // private
20389     shouldDeactivate : function(e){
20390         return !this.region || !this.region.contains(e.getPoint());
20391     },
20392
20393     // private
20394     handleClick : function(e){
20395         if(this.hideOnClick){
20396             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20397         }
20398     },
20399
20400     // private
20401     expandMenu : function(autoActivate){
20402         // do nothing
20403     },
20404
20405     // private
20406     hideMenu : function(){
20407         // do nothing
20408     }
20409 });/*
20410  * Based on:
20411  * Ext JS Library 1.1.1
20412  * Copyright(c) 2006-2007, Ext JS, LLC.
20413  *
20414  * Originally Released Under LGPL - original licence link has changed is not relivant.
20415  *
20416  * Fork - LGPL
20417  * <script type="text/javascript">
20418  */
20419  
20420 /**
20421  * @class Roo.menu.Adapter
20422  * @extends Roo.menu.BaseItem
20423  * 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.
20424  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20425  * @constructor
20426  * Creates a new Adapter
20427  * @param {Object} config Configuration options
20428  */
20429 Roo.menu.Adapter = function(component, config){
20430     Roo.menu.Adapter.superclass.constructor.call(this, config);
20431     this.component = component;
20432 };
20433 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20434     // private
20435     canActivate : true,
20436
20437     // private
20438     onRender : function(container, position){
20439         this.component.render(container);
20440         this.el = this.component.getEl();
20441     },
20442
20443     // private
20444     activate : function(){
20445         if(this.disabled){
20446             return false;
20447         }
20448         this.component.focus();
20449         this.fireEvent("activate", this);
20450         return true;
20451     },
20452
20453     // private
20454     deactivate : function(){
20455         this.fireEvent("deactivate", this);
20456     },
20457
20458     // private
20459     disable : function(){
20460         this.component.disable();
20461         Roo.menu.Adapter.superclass.disable.call(this);
20462     },
20463
20464     // private
20465     enable : function(){
20466         this.component.enable();
20467         Roo.menu.Adapter.superclass.enable.call(this);
20468     }
20469 });/*
20470  * Based on:
20471  * Ext JS Library 1.1.1
20472  * Copyright(c) 2006-2007, Ext JS, LLC.
20473  *
20474  * Originally Released Under LGPL - original licence link has changed is not relivant.
20475  *
20476  * Fork - LGPL
20477  * <script type="text/javascript">
20478  */
20479
20480 /**
20481  * @class Roo.menu.TextItem
20482  * @extends Roo.menu.BaseItem
20483  * Adds a static text string to a menu, usually used as either a heading or group separator.
20484  * Note: old style constructor with text is still supported.
20485  * 
20486  * @constructor
20487  * Creates a new TextItem
20488  * @param {Object} cfg Configuration
20489  */
20490 Roo.menu.TextItem = function(cfg){
20491     if (typeof(cfg) == 'string') {
20492         this.text = cfg;
20493     } else {
20494         Roo.apply(this,cfg);
20495     }
20496     
20497     Roo.menu.TextItem.superclass.constructor.call(this);
20498 };
20499
20500 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20501     /**
20502      * @cfg {Boolean} text Text to show on item.
20503      */
20504     text : '',
20505     
20506     /**
20507      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20508      */
20509     hideOnClick : false,
20510     /**
20511      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20512      */
20513     itemCls : "x-menu-text",
20514
20515     // private
20516     onRender : function(){
20517         var s = document.createElement("span");
20518         s.className = this.itemCls;
20519         s.innerHTML = this.text;
20520         this.el = s;
20521         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20522     }
20523 });/*
20524  * Based on:
20525  * Ext JS Library 1.1.1
20526  * Copyright(c) 2006-2007, Ext JS, LLC.
20527  *
20528  * Originally Released Under LGPL - original licence link has changed is not relivant.
20529  *
20530  * Fork - LGPL
20531  * <script type="text/javascript">
20532  */
20533
20534 /**
20535  * @class Roo.menu.Separator
20536  * @extends Roo.menu.BaseItem
20537  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20538  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20539  * @constructor
20540  * @param {Object} config Configuration options
20541  */
20542 Roo.menu.Separator = function(config){
20543     Roo.menu.Separator.superclass.constructor.call(this, config);
20544 };
20545
20546 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20547     /**
20548      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20549      */
20550     itemCls : "x-menu-sep",
20551     /**
20552      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20553      */
20554     hideOnClick : false,
20555
20556     // private
20557     onRender : function(li){
20558         var s = document.createElement("span");
20559         s.className = this.itemCls;
20560         s.innerHTML = "&#160;";
20561         this.el = s;
20562         li.addClass("x-menu-sep-li");
20563         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20564     }
20565 });/*
20566  * Based on:
20567  * Ext JS Library 1.1.1
20568  * Copyright(c) 2006-2007, Ext JS, LLC.
20569  *
20570  * Originally Released Under LGPL - original licence link has changed is not relivant.
20571  *
20572  * Fork - LGPL
20573  * <script type="text/javascript">
20574  */
20575 /**
20576  * @class Roo.menu.Item
20577  * @extends Roo.menu.BaseItem
20578  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20579  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20580  * activation and click handling.
20581  * @constructor
20582  * Creates a new Item
20583  * @param {Object} config Configuration options
20584  */
20585 Roo.menu.Item = function(config){
20586     Roo.menu.Item.superclass.constructor.call(this, config);
20587     if(this.menu){
20588         this.menu = Roo.menu.MenuMgr.get(this.menu);
20589     }
20590 };
20591 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20592     
20593     /**
20594      * @cfg {String} text
20595      * The text to show on the menu item.
20596      */
20597     text: '',
20598      /**
20599      * @cfg {String} HTML to render in menu
20600      * The text to show on the menu item (HTML version).
20601      */
20602     html: '',
20603     /**
20604      * @cfg {String} icon
20605      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20606      */
20607     icon: undefined,
20608     /**
20609      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20610      */
20611     itemCls : "x-menu-item",
20612     /**
20613      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20614      */
20615     canActivate : true,
20616     /**
20617      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20618      */
20619     showDelay: 200,
20620     // doc'd in BaseItem
20621     hideDelay: 200,
20622
20623     // private
20624     ctype: "Roo.menu.Item",
20625     
20626     // private
20627     onRender : function(container, position){
20628         var el = document.createElement("a");
20629         el.hideFocus = true;
20630         el.unselectable = "on";
20631         el.href = this.href || "#";
20632         if(this.hrefTarget){
20633             el.target = this.hrefTarget;
20634         }
20635         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20636         
20637         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20638         
20639         el.innerHTML = String.format(
20640                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20641                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20642         this.el = el;
20643         Roo.menu.Item.superclass.onRender.call(this, container, position);
20644     },
20645
20646     /**
20647      * Sets the text to display in this menu item
20648      * @param {String} text The text to display
20649      * @param {Boolean} isHTML true to indicate text is pure html.
20650      */
20651     setText : function(text, isHTML){
20652         if (isHTML) {
20653             this.html = text;
20654         } else {
20655             this.text = text;
20656             this.html = '';
20657         }
20658         if(this.rendered){
20659             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20660      
20661             this.el.update(String.format(
20662                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20663                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20664             this.parentMenu.autoWidth();
20665         }
20666     },
20667
20668     // private
20669     handleClick : function(e){
20670         if(!this.href){ // if no link defined, stop the event automatically
20671             e.stopEvent();
20672         }
20673         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20674     },
20675
20676     // private
20677     activate : function(autoExpand){
20678         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20679             this.focus();
20680             if(autoExpand){
20681                 this.expandMenu();
20682             }
20683         }
20684         return true;
20685     },
20686
20687     // private
20688     shouldDeactivate : function(e){
20689         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20690             if(this.menu && this.menu.isVisible()){
20691                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20692             }
20693             return true;
20694         }
20695         return false;
20696     },
20697
20698     // private
20699     deactivate : function(){
20700         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20701         this.hideMenu();
20702     },
20703
20704     // private
20705     expandMenu : function(autoActivate){
20706         if(!this.disabled && this.menu){
20707             clearTimeout(this.hideTimer);
20708             delete this.hideTimer;
20709             if(!this.menu.isVisible() && !this.showTimer){
20710                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20711             }else if (this.menu.isVisible() && autoActivate){
20712                 this.menu.tryActivate(0, 1);
20713             }
20714         }
20715     },
20716
20717     // private
20718     deferExpand : function(autoActivate){
20719         delete this.showTimer;
20720         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20721         if(autoActivate){
20722             this.menu.tryActivate(0, 1);
20723         }
20724     },
20725
20726     // private
20727     hideMenu : function(){
20728         clearTimeout(this.showTimer);
20729         delete this.showTimer;
20730         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20731             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20732         }
20733     },
20734
20735     // private
20736     deferHide : function(){
20737         delete this.hideTimer;
20738         this.menu.hide();
20739     }
20740 });/*
20741  * Based on:
20742  * Ext JS Library 1.1.1
20743  * Copyright(c) 2006-2007, Ext JS, LLC.
20744  *
20745  * Originally Released Under LGPL - original licence link has changed is not relivant.
20746  *
20747  * Fork - LGPL
20748  * <script type="text/javascript">
20749  */
20750  
20751 /**
20752  * @class Roo.menu.CheckItem
20753  * @extends Roo.menu.Item
20754  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20755  * @constructor
20756  * Creates a new CheckItem
20757  * @param {Object} config Configuration options
20758  */
20759 Roo.menu.CheckItem = function(config){
20760     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20761     this.addEvents({
20762         /**
20763          * @event beforecheckchange
20764          * Fires before the checked value is set, providing an opportunity to cancel if needed
20765          * @param {Roo.menu.CheckItem} this
20766          * @param {Boolean} checked The new checked value that will be set
20767          */
20768         "beforecheckchange" : true,
20769         /**
20770          * @event checkchange
20771          * Fires after the checked value has been set
20772          * @param {Roo.menu.CheckItem} this
20773          * @param {Boolean} checked The checked value that was set
20774          */
20775         "checkchange" : true
20776     });
20777     if(this.checkHandler){
20778         this.on('checkchange', this.checkHandler, this.scope);
20779     }
20780 };
20781 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20782     /**
20783      * @cfg {String} group
20784      * All check items with the same group name will automatically be grouped into a single-select
20785      * radio button group (defaults to '')
20786      */
20787     /**
20788      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20789      */
20790     itemCls : "x-menu-item x-menu-check-item",
20791     /**
20792      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20793      */
20794     groupClass : "x-menu-group-item",
20795
20796     /**
20797      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20798      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20799      * initialized with checked = true will be rendered as checked.
20800      */
20801     checked: false,
20802
20803     // private
20804     ctype: "Roo.menu.CheckItem",
20805
20806     // private
20807     onRender : function(c){
20808         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20809         if(this.group){
20810             this.el.addClass(this.groupClass);
20811         }
20812         Roo.menu.MenuMgr.registerCheckable(this);
20813         if(this.checked){
20814             this.checked = false;
20815             this.setChecked(true, true);
20816         }
20817     },
20818
20819     // private
20820     destroy : function(){
20821         if(this.rendered){
20822             Roo.menu.MenuMgr.unregisterCheckable(this);
20823         }
20824         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20825     },
20826
20827     /**
20828      * Set the checked state of this item
20829      * @param {Boolean} checked The new checked value
20830      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20831      */
20832     setChecked : function(state, suppressEvent){
20833         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20834             if(this.container){
20835                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20836             }
20837             this.checked = state;
20838             if(suppressEvent !== true){
20839                 this.fireEvent("checkchange", this, state);
20840             }
20841         }
20842     },
20843
20844     // private
20845     handleClick : function(e){
20846        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20847            this.setChecked(!this.checked);
20848        }
20849        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20850     }
20851 });/*
20852  * Based on:
20853  * Ext JS Library 1.1.1
20854  * Copyright(c) 2006-2007, Ext JS, LLC.
20855  *
20856  * Originally Released Under LGPL - original licence link has changed is not relivant.
20857  *
20858  * Fork - LGPL
20859  * <script type="text/javascript">
20860  */
20861  
20862 /**
20863  * @class Roo.menu.DateItem
20864  * @extends Roo.menu.Adapter
20865  * A menu item that wraps the {@link Roo.DatPicker} component.
20866  * @constructor
20867  * Creates a new DateItem
20868  * @param {Object} config Configuration options
20869  */
20870 Roo.menu.DateItem = function(config){
20871     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20872     /** The Roo.DatePicker object @type Roo.DatePicker */
20873     this.picker = this.component;
20874     this.addEvents({select: true});
20875     
20876     this.picker.on("render", function(picker){
20877         picker.getEl().swallowEvent("click");
20878         picker.container.addClass("x-menu-date-item");
20879     });
20880
20881     this.picker.on("select", this.onSelect, this);
20882 };
20883
20884 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20885     // private
20886     onSelect : function(picker, date){
20887         this.fireEvent("select", this, date, picker);
20888         Roo.menu.DateItem.superclass.handleClick.call(this);
20889     }
20890 });/*
20891  * Based on:
20892  * Ext JS Library 1.1.1
20893  * Copyright(c) 2006-2007, Ext JS, LLC.
20894  *
20895  * Originally Released Under LGPL - original licence link has changed is not relivant.
20896  *
20897  * Fork - LGPL
20898  * <script type="text/javascript">
20899  */
20900  
20901 /**
20902  * @class Roo.menu.ColorItem
20903  * @extends Roo.menu.Adapter
20904  * A menu item that wraps the {@link Roo.ColorPalette} component.
20905  * @constructor
20906  * Creates a new ColorItem
20907  * @param {Object} config Configuration options
20908  */
20909 Roo.menu.ColorItem = function(config){
20910     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20911     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20912     this.palette = this.component;
20913     this.relayEvents(this.palette, ["select"]);
20914     if(this.selectHandler){
20915         this.on('select', this.selectHandler, this.scope);
20916     }
20917 };
20918 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20919  * Based on:
20920  * Ext JS Library 1.1.1
20921  * Copyright(c) 2006-2007, Ext JS, LLC.
20922  *
20923  * Originally Released Under LGPL - original licence link has changed is not relivant.
20924  *
20925  * Fork - LGPL
20926  * <script type="text/javascript">
20927  */
20928  
20929
20930 /**
20931  * @class Roo.menu.DateMenu
20932  * @extends Roo.menu.Menu
20933  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20934  * @constructor
20935  * Creates a new DateMenu
20936  * @param {Object} config Configuration options
20937  */
20938 Roo.menu.DateMenu = function(config){
20939     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20940     this.plain = true;
20941     var di = new Roo.menu.DateItem(config);
20942     this.add(di);
20943     /**
20944      * The {@link Roo.DatePicker} instance for this DateMenu
20945      * @type DatePicker
20946      */
20947     this.picker = di.picker;
20948     /**
20949      * @event select
20950      * @param {DatePicker} picker
20951      * @param {Date} date
20952      */
20953     this.relayEvents(di, ["select"]);
20954
20955     this.on('beforeshow', function(){
20956         if(this.picker){
20957             this.picker.hideMonthPicker(true);
20958         }
20959     }, this);
20960 };
20961 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20962     cls:'x-date-menu'
20963 });/*
20964  * Based on:
20965  * Ext JS Library 1.1.1
20966  * Copyright(c) 2006-2007, Ext JS, LLC.
20967  *
20968  * Originally Released Under LGPL - original licence link has changed is not relivant.
20969  *
20970  * Fork - LGPL
20971  * <script type="text/javascript">
20972  */
20973  
20974
20975 /**
20976  * @class Roo.menu.ColorMenu
20977  * @extends Roo.menu.Menu
20978  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20979  * @constructor
20980  * Creates a new ColorMenu
20981  * @param {Object} config Configuration options
20982  */
20983 Roo.menu.ColorMenu = function(config){
20984     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20985     this.plain = true;
20986     var ci = new Roo.menu.ColorItem(config);
20987     this.add(ci);
20988     /**
20989      * The {@link Roo.ColorPalette} instance for this ColorMenu
20990      * @type ColorPalette
20991      */
20992     this.palette = ci.palette;
20993     /**
20994      * @event select
20995      * @param {ColorPalette} palette
20996      * @param {String} color
20997      */
20998     this.relayEvents(ci, ["select"]);
20999 };
21000 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21001  * Based on:
21002  * Ext JS Library 1.1.1
21003  * Copyright(c) 2006-2007, Ext JS, LLC.
21004  *
21005  * Originally Released Under LGPL - original licence link has changed is not relivant.
21006  *
21007  * Fork - LGPL
21008  * <script type="text/javascript">
21009  */
21010  
21011 /**
21012  * @class Roo.form.Field
21013  * @extends Roo.BoxComponent
21014  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21015  * @constructor
21016  * Creates a new Field
21017  * @param {Object} config Configuration options
21018  */
21019 Roo.form.Field = function(config){
21020     Roo.form.Field.superclass.constructor.call(this, config);
21021 };
21022
21023 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21024     /**
21025      * @cfg {String} fieldLabel Label to use when rendering a form.
21026      */
21027        /**
21028      * @cfg {String} qtip Mouse over tip
21029      */
21030      
21031     /**
21032      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21033      */
21034     invalidClass : "x-form-invalid",
21035     /**
21036      * @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")
21037      */
21038     invalidText : "The value in this field is invalid",
21039     /**
21040      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21041      */
21042     focusClass : "x-form-focus",
21043     /**
21044      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21045       automatic validation (defaults to "keyup").
21046      */
21047     validationEvent : "keyup",
21048     /**
21049      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21050      */
21051     validateOnBlur : true,
21052     /**
21053      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21054      */
21055     validationDelay : 250,
21056     /**
21057      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21058      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21059      */
21060     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21061     /**
21062      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21063      */
21064     fieldClass : "x-form-field",
21065     /**
21066      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21067      *<pre>
21068 Value         Description
21069 -----------   ----------------------------------------------------------------------
21070 qtip          Display a quick tip when the user hovers over the field
21071 title         Display a default browser title attribute popup
21072 under         Add a block div beneath the field containing the error text
21073 side          Add an error icon to the right of the field with a popup on hover
21074 [element id]  Add the error text directly to the innerHTML of the specified element
21075 </pre>
21076      */
21077     msgTarget : 'qtip',
21078     /**
21079      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21080      */
21081     msgFx : 'normal',
21082
21083     /**
21084      * @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.
21085      */
21086     readOnly : false,
21087
21088     /**
21089      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21090      */
21091     disabled : false,
21092
21093     /**
21094      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21095      */
21096     inputType : undefined,
21097     
21098     /**
21099      * @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).
21100          */
21101         tabIndex : undefined,
21102         
21103     // private
21104     isFormField : true,
21105
21106     // private
21107     hasFocus : false,
21108     /**
21109      * @property {Roo.Element} fieldEl
21110      * Element Containing the rendered Field (with label etc.)
21111      */
21112     /**
21113      * @cfg {Mixed} value A value to initialize this field with.
21114      */
21115     value : undefined,
21116
21117     /**
21118      * @cfg {String} name The field's HTML name attribute.
21119      */
21120     /**
21121      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21122      */
21123
21124         // private ??
21125         initComponent : function(){
21126         Roo.form.Field.superclass.initComponent.call(this);
21127         this.addEvents({
21128             /**
21129              * @event focus
21130              * Fires when this field receives input focus.
21131              * @param {Roo.form.Field} this
21132              */
21133             focus : true,
21134             /**
21135              * @event blur
21136              * Fires when this field loses input focus.
21137              * @param {Roo.form.Field} this
21138              */
21139             blur : true,
21140             /**
21141              * @event specialkey
21142              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21143              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21144              * @param {Roo.form.Field} this
21145              * @param {Roo.EventObject} e The event object
21146              */
21147             specialkey : true,
21148             /**
21149              * @event change
21150              * Fires just before the field blurs if the field value has changed.
21151              * @param {Roo.form.Field} this
21152              * @param {Mixed} newValue The new value
21153              * @param {Mixed} oldValue The original value
21154              */
21155             change : true,
21156             /**
21157              * @event invalid
21158              * Fires after the field has been marked as invalid.
21159              * @param {Roo.form.Field} this
21160              * @param {String} msg The validation message
21161              */
21162             invalid : true,
21163             /**
21164              * @event valid
21165              * Fires after the field has been validated with no errors.
21166              * @param {Roo.form.Field} this
21167              */
21168             valid : true,
21169              /**
21170              * @event keyup
21171              * Fires after the key up
21172              * @param {Roo.form.Field} this
21173              * @param {Roo.EventObject}  e The event Object
21174              */
21175             keyup : true
21176         });
21177     },
21178
21179     /**
21180      * Returns the name attribute of the field if available
21181      * @return {String} name The field name
21182      */
21183     getName: function(){
21184          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21185     },
21186
21187     // private
21188     onRender : function(ct, position){
21189         Roo.form.Field.superclass.onRender.call(this, ct, position);
21190         if(!this.el){
21191             var cfg = this.getAutoCreate();
21192             if(!cfg.name){
21193                 cfg.name = this.name || this.id;
21194             }
21195             if(this.inputType){
21196                 cfg.type = this.inputType;
21197             }
21198             this.el = ct.createChild(cfg, position);
21199         }
21200         var type = this.el.dom.type;
21201         if(type){
21202             if(type == 'password'){
21203                 type = 'text';
21204             }
21205             this.el.addClass('x-form-'+type);
21206         }
21207         if(this.readOnly){
21208             this.el.dom.readOnly = true;
21209         }
21210         if(this.tabIndex !== undefined){
21211             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21212         }
21213
21214         this.el.addClass([this.fieldClass, this.cls]);
21215         this.initValue();
21216     },
21217
21218     /**
21219      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21220      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21221      * @return {Roo.form.Field} this
21222      */
21223     applyTo : function(target){
21224         this.allowDomMove = false;
21225         this.el = Roo.get(target);
21226         this.render(this.el.dom.parentNode);
21227         return this;
21228     },
21229
21230     // private
21231     initValue : function(){
21232         if(this.value !== undefined){
21233             this.setValue(this.value);
21234         }else if(this.el.dom.value.length > 0){
21235             this.setValue(this.el.dom.value);
21236         }
21237     },
21238
21239     /**
21240      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21241      */
21242     isDirty : function() {
21243         if(this.disabled) {
21244             return false;
21245         }
21246         return String(this.getValue()) !== String(this.originalValue);
21247     },
21248
21249     // private
21250     afterRender : function(){
21251         Roo.form.Field.superclass.afterRender.call(this);
21252         this.initEvents();
21253     },
21254
21255     // private
21256     fireKey : function(e){
21257         //Roo.log('field ' + e.getKey());
21258         if(e.isNavKeyPress()){
21259             this.fireEvent("specialkey", this, e);
21260         }
21261     },
21262
21263     /**
21264      * Resets the current field value to the originally loaded value and clears any validation messages
21265      */
21266     reset : function(){
21267         this.setValue(this.originalValue);
21268         this.clearInvalid();
21269     },
21270
21271     // private
21272     initEvents : function(){
21273         // safari killled keypress - so keydown is now used..
21274         this.el.on("keydown" , this.fireKey,  this);
21275         this.el.on("focus", this.onFocus,  this);
21276         this.el.on("blur", this.onBlur,  this);
21277         this.el.relayEvent('keyup', this);
21278
21279         // reference to original value for reset
21280         this.originalValue = this.getValue();
21281     },
21282
21283     // private
21284     onFocus : function(){
21285         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21286             this.el.addClass(this.focusClass);
21287         }
21288         if(!this.hasFocus){
21289             this.hasFocus = true;
21290             this.startValue = this.getValue();
21291             this.fireEvent("focus", this);
21292         }
21293     },
21294
21295     beforeBlur : Roo.emptyFn,
21296
21297     // private
21298     onBlur : function(){
21299         this.beforeBlur();
21300         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21301             this.el.removeClass(this.focusClass);
21302         }
21303         this.hasFocus = false;
21304         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21305             this.validate();
21306         }
21307         var v = this.getValue();
21308         if(String(v) !== String(this.startValue)){
21309             this.fireEvent('change', this, v, this.startValue);
21310         }
21311         this.fireEvent("blur", this);
21312     },
21313
21314     /**
21315      * Returns whether or not the field value is currently valid
21316      * @param {Boolean} preventMark True to disable marking the field invalid
21317      * @return {Boolean} True if the value is valid, else false
21318      */
21319     isValid : function(preventMark){
21320         if(this.disabled){
21321             return true;
21322         }
21323         var restore = this.preventMark;
21324         this.preventMark = preventMark === true;
21325         var v = this.validateValue(this.processValue(this.getRawValue()));
21326         this.preventMark = restore;
21327         return v;
21328     },
21329
21330     /**
21331      * Validates the field value
21332      * @return {Boolean} True if the value is valid, else false
21333      */
21334     validate : function(){
21335         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21336             this.clearInvalid();
21337             return true;
21338         }
21339         return false;
21340     },
21341
21342     processValue : function(value){
21343         return value;
21344     },
21345
21346     // private
21347     // Subclasses should provide the validation implementation by overriding this
21348     validateValue : function(value){
21349         return true;
21350     },
21351
21352     /**
21353      * Mark this field as invalid
21354      * @param {String} msg The validation message
21355      */
21356     markInvalid : function(msg){
21357         if(!this.rendered || this.preventMark){ // not rendered
21358             return;
21359         }
21360         this.el.addClass(this.invalidClass);
21361         msg = msg || this.invalidText;
21362         switch(this.msgTarget){
21363             case 'qtip':
21364                 this.el.dom.qtip = msg;
21365                 this.el.dom.qclass = 'x-form-invalid-tip';
21366                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21367                     Roo.QuickTips.enable();
21368                 }
21369                 break;
21370             case 'title':
21371                 this.el.dom.title = msg;
21372                 break;
21373             case 'under':
21374                 if(!this.errorEl){
21375                     var elp = this.el.findParent('.x-form-element', 5, true);
21376                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21377                     this.errorEl.setWidth(elp.getWidth(true)-20);
21378                 }
21379                 this.errorEl.update(msg);
21380                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21381                 break;
21382             case 'side':
21383                 if(!this.errorIcon){
21384                     var elp = this.el.findParent('.x-form-element', 5, true);
21385                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21386                 }
21387                 this.alignErrorIcon();
21388                 this.errorIcon.dom.qtip = msg;
21389                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21390                 this.errorIcon.show();
21391                 this.on('resize', this.alignErrorIcon, this);
21392                 break;
21393             default:
21394                 var t = Roo.getDom(this.msgTarget);
21395                 t.innerHTML = msg;
21396                 t.style.display = this.msgDisplay;
21397                 break;
21398         }
21399         this.fireEvent('invalid', this, msg);
21400     },
21401
21402     // private
21403     alignErrorIcon : function(){
21404         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21405     },
21406
21407     /**
21408      * Clear any invalid styles/messages for this field
21409      */
21410     clearInvalid : function(){
21411         if(!this.rendered || this.preventMark){ // not rendered
21412             return;
21413         }
21414         this.el.removeClass(this.invalidClass);
21415         switch(this.msgTarget){
21416             case 'qtip':
21417                 this.el.dom.qtip = '';
21418                 break;
21419             case 'title':
21420                 this.el.dom.title = '';
21421                 break;
21422             case 'under':
21423                 if(this.errorEl){
21424                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21425                 }
21426                 break;
21427             case 'side':
21428                 if(this.errorIcon){
21429                     this.errorIcon.dom.qtip = '';
21430                     this.errorIcon.hide();
21431                     this.un('resize', this.alignErrorIcon, this);
21432                 }
21433                 break;
21434             default:
21435                 var t = Roo.getDom(this.msgTarget);
21436                 t.innerHTML = '';
21437                 t.style.display = 'none';
21438                 break;
21439         }
21440         this.fireEvent('valid', this);
21441     },
21442
21443     /**
21444      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21445      * @return {Mixed} value The field value
21446      */
21447     getRawValue : function(){
21448         var v = this.el.getValue();
21449         if(v === this.emptyText){
21450             v = '';
21451         }
21452         return v;
21453     },
21454
21455     /**
21456      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21457      * @return {Mixed} value The field value
21458      */
21459     getValue : function(){
21460         var v = this.el.getValue();
21461         if(v === this.emptyText || v === undefined){
21462             v = '';
21463         }
21464         return v;
21465     },
21466
21467     /**
21468      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21469      * @param {Mixed} value The value to set
21470      */
21471     setRawValue : function(v){
21472         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21473     },
21474
21475     /**
21476      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21477      * @param {Mixed} value The value to set
21478      */
21479     setValue : function(v){
21480         this.value = v;
21481         if(this.rendered){
21482             this.el.dom.value = (v === null || v === undefined ? '' : v);
21483              this.validate();
21484         }
21485     },
21486
21487     adjustSize : function(w, h){
21488         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21489         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21490         return s;
21491     },
21492
21493     adjustWidth : function(tag, w){
21494         tag = tag.toLowerCase();
21495         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21496             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21497                 if(tag == 'input'){
21498                     return w + 2;
21499                 }
21500                 if(tag = 'textarea'){
21501                     return w-2;
21502                 }
21503             }else if(Roo.isOpera){
21504                 if(tag == 'input'){
21505                     return w + 2;
21506                 }
21507                 if(tag = 'textarea'){
21508                     return w-2;
21509                 }
21510             }
21511         }
21512         return w;
21513     }
21514 });
21515
21516
21517 // anything other than normal should be considered experimental
21518 Roo.form.Field.msgFx = {
21519     normal : {
21520         show: function(msgEl, f){
21521             msgEl.setDisplayed('block');
21522         },
21523
21524         hide : function(msgEl, f){
21525             msgEl.setDisplayed(false).update('');
21526         }
21527     },
21528
21529     slide : {
21530         show: function(msgEl, f){
21531             msgEl.slideIn('t', {stopFx:true});
21532         },
21533
21534         hide : function(msgEl, f){
21535             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21536         }
21537     },
21538
21539     slideRight : {
21540         show: function(msgEl, f){
21541             msgEl.fixDisplay();
21542             msgEl.alignTo(f.el, 'tl-tr');
21543             msgEl.slideIn('l', {stopFx:true});
21544         },
21545
21546         hide : function(msgEl, f){
21547             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21548         }
21549     }
21550 };/*
21551  * Based on:
21552  * Ext JS Library 1.1.1
21553  * Copyright(c) 2006-2007, Ext JS, LLC.
21554  *
21555  * Originally Released Under LGPL - original licence link has changed is not relivant.
21556  *
21557  * Fork - LGPL
21558  * <script type="text/javascript">
21559  */
21560  
21561
21562 /**
21563  * @class Roo.form.TextField
21564  * @extends Roo.form.Field
21565  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21566  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21567  * @constructor
21568  * Creates a new TextField
21569  * @param {Object} config Configuration options
21570  */
21571 Roo.form.TextField = function(config){
21572     Roo.form.TextField.superclass.constructor.call(this, config);
21573     this.addEvents({
21574         /**
21575          * @event autosize
21576          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21577          * according to the default logic, but this event provides a hook for the developer to apply additional
21578          * logic at runtime to resize the field if needed.
21579              * @param {Roo.form.Field} this This text field
21580              * @param {Number} width The new field width
21581              */
21582         autosize : true
21583     });
21584 };
21585
21586 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21587     /**
21588      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21589      */
21590     grow : false,
21591     /**
21592      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21593      */
21594     growMin : 30,
21595     /**
21596      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21597      */
21598     growMax : 800,
21599     /**
21600      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21601      */
21602     vtype : null,
21603     /**
21604      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21605      */
21606     maskRe : null,
21607     /**
21608      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21609      */
21610     disableKeyFilter : false,
21611     /**
21612      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21613      */
21614     allowBlank : true,
21615     /**
21616      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21617      */
21618     minLength : 0,
21619     /**
21620      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21621      */
21622     maxLength : Number.MAX_VALUE,
21623     /**
21624      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21625      */
21626     minLengthText : "The minimum length for this field is {0}",
21627     /**
21628      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21629      */
21630     maxLengthText : "The maximum length for this field is {0}",
21631     /**
21632      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21633      */
21634     selectOnFocus : false,
21635     /**
21636      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21637      */
21638     blankText : "This field is required",
21639     /**
21640      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21641      * If available, this function will be called only after the basic validators all return true, and will be passed the
21642      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21643      */
21644     validator : null,
21645     /**
21646      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21647      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21648      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21649      */
21650     regex : null,
21651     /**
21652      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21653      */
21654     regexText : "",
21655     /**
21656      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21657      */
21658     emptyText : null,
21659     /**
21660      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21661      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21662      */
21663     emptyClass : 'x-form-empty-field',
21664
21665     // private
21666     initEvents : function(){
21667         Roo.form.TextField.superclass.initEvents.call(this);
21668         if(this.validationEvent == 'keyup'){
21669             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21670             this.el.on('keyup', this.filterValidation, this);
21671         }
21672         else if(this.validationEvent !== false){
21673             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21674         }
21675         if(this.selectOnFocus || this.emptyText){
21676             this.on("focus", this.preFocus, this);
21677             if(this.emptyText){
21678                 this.on('blur', this.postBlur, this);
21679                 this.applyEmptyText();
21680             }
21681         }
21682         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21683             this.el.on("keypress", this.filterKeys, this);
21684         }
21685         if(this.grow){
21686             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21687             this.el.on("click", this.autoSize,  this);
21688         }
21689     },
21690
21691     processValue : function(value){
21692         if(this.stripCharsRe){
21693             var newValue = value.replace(this.stripCharsRe, '');
21694             if(newValue !== value){
21695                 this.setRawValue(newValue);
21696                 return newValue;
21697             }
21698         }
21699         return value;
21700     },
21701
21702     filterValidation : function(e){
21703         if(!e.isNavKeyPress()){
21704             this.validationTask.delay(this.validationDelay);
21705         }
21706     },
21707
21708     // private
21709     onKeyUp : function(e){
21710         if(!e.isNavKeyPress()){
21711             this.autoSize();
21712         }
21713     },
21714
21715     /**
21716      * Resets the current field value to the originally-loaded value and clears any validation messages.
21717      * Also adds emptyText and emptyClass if the original value was blank.
21718      */
21719     reset : function(){
21720         Roo.form.TextField.superclass.reset.call(this);
21721         this.applyEmptyText();
21722     },
21723
21724     applyEmptyText : function(){
21725         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21726             this.setRawValue(this.emptyText);
21727             this.el.addClass(this.emptyClass);
21728         }
21729     },
21730
21731     // private
21732     preFocus : function(){
21733         if(this.emptyText){
21734             if(this.el.dom.value == this.emptyText){
21735                 this.setRawValue('');
21736             }
21737             this.el.removeClass(this.emptyClass);
21738         }
21739         if(this.selectOnFocus){
21740             this.el.dom.select();
21741         }
21742     },
21743
21744     // private
21745     postBlur : function(){
21746         this.applyEmptyText();
21747     },
21748
21749     // private
21750     filterKeys : function(e){
21751         var k = e.getKey();
21752         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21753             return;
21754         }
21755         var c = e.getCharCode(), cc = String.fromCharCode(c);
21756         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21757             return;
21758         }
21759         if(!this.maskRe.test(cc)){
21760             e.stopEvent();
21761         }
21762     },
21763
21764     setValue : function(v){
21765         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21766             this.el.removeClass(this.emptyClass);
21767         }
21768         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21769         this.applyEmptyText();
21770         this.autoSize();
21771     },
21772
21773     /**
21774      * Validates a value according to the field's validation rules and marks the field as invalid
21775      * if the validation fails
21776      * @param {Mixed} value The value to validate
21777      * @return {Boolean} True if the value is valid, else false
21778      */
21779     validateValue : function(value){
21780         if(value.length < 1 || value === this.emptyText){ // if it's blank
21781              if(this.allowBlank){
21782                 this.clearInvalid();
21783                 return true;
21784              }else{
21785                 this.markInvalid(this.blankText);
21786                 return false;
21787              }
21788         }
21789         if(value.length < this.minLength){
21790             this.markInvalid(String.format(this.minLengthText, this.minLength));
21791             return false;
21792         }
21793         if(value.length > this.maxLength){
21794             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21795             return false;
21796         }
21797         if(this.vtype){
21798             var vt = Roo.form.VTypes;
21799             if(!vt[this.vtype](value, this)){
21800                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21801                 return false;
21802             }
21803         }
21804         if(typeof this.validator == "function"){
21805             var msg = this.validator(value);
21806             if(msg !== true){
21807                 this.markInvalid(msg);
21808                 return false;
21809             }
21810         }
21811         if(this.regex && !this.regex.test(value)){
21812             this.markInvalid(this.regexText);
21813             return false;
21814         }
21815         return true;
21816     },
21817
21818     /**
21819      * Selects text in this field
21820      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21821      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21822      */
21823     selectText : function(start, end){
21824         var v = this.getRawValue();
21825         if(v.length > 0){
21826             start = start === undefined ? 0 : start;
21827             end = end === undefined ? v.length : end;
21828             var d = this.el.dom;
21829             if(d.setSelectionRange){
21830                 d.setSelectionRange(start, end);
21831             }else if(d.createTextRange){
21832                 var range = d.createTextRange();
21833                 range.moveStart("character", start);
21834                 range.moveEnd("character", v.length-end);
21835                 range.select();
21836             }
21837         }
21838     },
21839
21840     /**
21841      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21842      * This only takes effect if grow = true, and fires the autosize event.
21843      */
21844     autoSize : function(){
21845         if(!this.grow || !this.rendered){
21846             return;
21847         }
21848         if(!this.metrics){
21849             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21850         }
21851         var el = this.el;
21852         var v = el.dom.value;
21853         var d = document.createElement('div');
21854         d.appendChild(document.createTextNode(v));
21855         v = d.innerHTML;
21856         d = null;
21857         v += "&#160;";
21858         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21859         this.el.setWidth(w);
21860         this.fireEvent("autosize", this, w);
21861     }
21862 });/*
21863  * Based on:
21864  * Ext JS Library 1.1.1
21865  * Copyright(c) 2006-2007, Ext JS, LLC.
21866  *
21867  * Originally Released Under LGPL - original licence link has changed is not relivant.
21868  *
21869  * Fork - LGPL
21870  * <script type="text/javascript">
21871  */
21872  
21873 /**
21874  * @class Roo.form.Hidden
21875  * @extends Roo.form.TextField
21876  * Simple Hidden element used on forms 
21877  * 
21878  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21879  * 
21880  * @constructor
21881  * Creates a new Hidden form element.
21882  * @param {Object} config Configuration options
21883  */
21884
21885
21886
21887 // easy hidden field...
21888 Roo.form.Hidden = function(config){
21889     Roo.form.Hidden.superclass.constructor.call(this, config);
21890 };
21891   
21892 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21893     fieldLabel:      '',
21894     inputType:      'hidden',
21895     width:          50,
21896     allowBlank:     true,
21897     labelSeparator: '',
21898     hidden:         true,
21899     itemCls :       'x-form-item-display-none'
21900
21901
21902 });
21903
21904
21905 /*
21906  * Based on:
21907  * Ext JS Library 1.1.1
21908  * Copyright(c) 2006-2007, Ext JS, LLC.
21909  *
21910  * Originally Released Under LGPL - original licence link has changed is not relivant.
21911  *
21912  * Fork - LGPL
21913  * <script type="text/javascript">
21914  */
21915  
21916 /**
21917  * @class Roo.form.TriggerField
21918  * @extends Roo.form.TextField
21919  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21920  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21921  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21922  * for which you can provide a custom implementation.  For example:
21923  * <pre><code>
21924 var trigger = new Roo.form.TriggerField();
21925 trigger.onTriggerClick = myTriggerFn;
21926 trigger.applyTo('my-field');
21927 </code></pre>
21928  *
21929  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21930  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21931  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21932  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21933  * @constructor
21934  * Create a new TriggerField.
21935  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21936  * to the base TextField)
21937  */
21938 Roo.form.TriggerField = function(config){
21939     this.mimicing = false;
21940     Roo.form.TriggerField.superclass.constructor.call(this, config);
21941 };
21942
21943 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21944     /**
21945      * @cfg {String} triggerClass A CSS class to apply to the trigger
21946      */
21947     /**
21948      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21949      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21950      */
21951     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21952     /**
21953      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21954      */
21955     hideTrigger:false,
21956
21957     /** @cfg {Boolean} grow @hide */
21958     /** @cfg {Number} growMin @hide */
21959     /** @cfg {Number} growMax @hide */
21960
21961     /**
21962      * @hide 
21963      * @method
21964      */
21965     autoSize: Roo.emptyFn,
21966     // private
21967     monitorTab : true,
21968     // private
21969     deferHeight : true,
21970
21971     
21972     actionMode : 'wrap',
21973     // private
21974     onResize : function(w, h){
21975         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21976         if(typeof w == 'number'){
21977             var x = w - this.trigger.getWidth();
21978             this.el.setWidth(this.adjustWidth('input', x));
21979             this.trigger.setStyle('left', x+'px');
21980         }
21981     },
21982
21983     // private
21984     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21985
21986     // private
21987     getResizeEl : function(){
21988         return this.wrap;
21989     },
21990
21991     // private
21992     getPositionEl : function(){
21993         return this.wrap;
21994     },
21995
21996     // private
21997     alignErrorIcon : function(){
21998         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21999     },
22000
22001     // private
22002     onRender : function(ct, position){
22003         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22004         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22005         this.trigger = this.wrap.createChild(this.triggerConfig ||
22006                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22007         if(this.hideTrigger){
22008             this.trigger.setDisplayed(false);
22009         }
22010         this.initTrigger();
22011         if(!this.width){
22012             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22013         }
22014     },
22015
22016     // private
22017     initTrigger : function(){
22018         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22019         this.trigger.addClassOnOver('x-form-trigger-over');
22020         this.trigger.addClassOnClick('x-form-trigger-click');
22021     },
22022
22023     // private
22024     onDestroy : function(){
22025         if(this.trigger){
22026             this.trigger.removeAllListeners();
22027             this.trigger.remove();
22028         }
22029         if(this.wrap){
22030             this.wrap.remove();
22031         }
22032         Roo.form.TriggerField.superclass.onDestroy.call(this);
22033     },
22034
22035     // private
22036     onFocus : function(){
22037         Roo.form.TriggerField.superclass.onFocus.call(this);
22038         if(!this.mimicing){
22039             this.wrap.addClass('x-trigger-wrap-focus');
22040             this.mimicing = true;
22041             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22042             if(this.monitorTab){
22043                 this.el.on("keydown", this.checkTab, this);
22044             }
22045         }
22046     },
22047
22048     // private
22049     checkTab : function(e){
22050         if(e.getKey() == e.TAB){
22051             this.triggerBlur();
22052         }
22053     },
22054
22055     // private
22056     onBlur : function(){
22057         // do nothing
22058     },
22059
22060     // private
22061     mimicBlur : function(e, t){
22062         if(!this.wrap.contains(t) && this.validateBlur()){
22063             this.triggerBlur();
22064         }
22065     },
22066
22067     // private
22068     triggerBlur : function(){
22069         this.mimicing = false;
22070         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22071         if(this.monitorTab){
22072             this.el.un("keydown", this.checkTab, this);
22073         }
22074         this.wrap.removeClass('x-trigger-wrap-focus');
22075         Roo.form.TriggerField.superclass.onBlur.call(this);
22076     },
22077
22078     // private
22079     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22080     validateBlur : function(e, t){
22081         return true;
22082     },
22083
22084     // private
22085     onDisable : function(){
22086         Roo.form.TriggerField.superclass.onDisable.call(this);
22087         if(this.wrap){
22088             this.wrap.addClass('x-item-disabled');
22089         }
22090     },
22091
22092     // private
22093     onEnable : function(){
22094         Roo.form.TriggerField.superclass.onEnable.call(this);
22095         if(this.wrap){
22096             this.wrap.removeClass('x-item-disabled');
22097         }
22098     },
22099
22100     // private
22101     onShow : function(){
22102         var ae = this.getActionEl();
22103         
22104         if(ae){
22105             ae.dom.style.display = '';
22106             ae.dom.style.visibility = 'visible';
22107         }
22108     },
22109
22110     // private
22111     
22112     onHide : function(){
22113         var ae = this.getActionEl();
22114         ae.dom.style.display = 'none';
22115     },
22116
22117     /**
22118      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22119      * by an implementing function.
22120      * @method
22121      * @param {EventObject} e
22122      */
22123     onTriggerClick : Roo.emptyFn
22124 });
22125
22126 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22127 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22128 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22129 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22130     initComponent : function(){
22131         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22132
22133         this.triggerConfig = {
22134             tag:'span', cls:'x-form-twin-triggers', cn:[
22135             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22136             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22137         ]};
22138     },
22139
22140     getTrigger : function(index){
22141         return this.triggers[index];
22142     },
22143
22144     initTrigger : function(){
22145         var ts = this.trigger.select('.x-form-trigger', true);
22146         this.wrap.setStyle('overflow', 'hidden');
22147         var triggerField = this;
22148         ts.each(function(t, all, index){
22149             t.hide = function(){
22150                 var w = triggerField.wrap.getWidth();
22151                 this.dom.style.display = 'none';
22152                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22153             };
22154             t.show = function(){
22155                 var w = triggerField.wrap.getWidth();
22156                 this.dom.style.display = '';
22157                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22158             };
22159             var triggerIndex = 'Trigger'+(index+1);
22160
22161             if(this['hide'+triggerIndex]){
22162                 t.dom.style.display = 'none';
22163             }
22164             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22165             t.addClassOnOver('x-form-trigger-over');
22166             t.addClassOnClick('x-form-trigger-click');
22167         }, this);
22168         this.triggers = ts.elements;
22169     },
22170
22171     onTrigger1Click : Roo.emptyFn,
22172     onTrigger2Click : Roo.emptyFn
22173 });/*
22174  * Based on:
22175  * Ext JS Library 1.1.1
22176  * Copyright(c) 2006-2007, Ext JS, LLC.
22177  *
22178  * Originally Released Under LGPL - original licence link has changed is not relivant.
22179  *
22180  * Fork - LGPL
22181  * <script type="text/javascript">
22182  */
22183  
22184 /**
22185  * @class Roo.form.TextArea
22186  * @extends Roo.form.TextField
22187  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22188  * support for auto-sizing.
22189  * @constructor
22190  * Creates a new TextArea
22191  * @param {Object} config Configuration options
22192  */
22193 Roo.form.TextArea = function(config){
22194     Roo.form.TextArea.superclass.constructor.call(this, config);
22195     // these are provided exchanges for backwards compat
22196     // minHeight/maxHeight were replaced by growMin/growMax to be
22197     // compatible with TextField growing config values
22198     if(this.minHeight !== undefined){
22199         this.growMin = this.minHeight;
22200     }
22201     if(this.maxHeight !== undefined){
22202         this.growMax = this.maxHeight;
22203     }
22204 };
22205
22206 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22207     /**
22208      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22209      */
22210     growMin : 60,
22211     /**
22212      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22213      */
22214     growMax: 1000,
22215     /**
22216      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22217      * in the field (equivalent to setting overflow: hidden, defaults to false)
22218      */
22219     preventScrollbars: false,
22220     /**
22221      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22222      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22223      */
22224
22225     // private
22226     onRender : function(ct, position){
22227         if(!this.el){
22228             this.defaultAutoCreate = {
22229                 tag: "textarea",
22230                 style:"width:300px;height:60px;",
22231                 autocomplete: "off"
22232             };
22233         }
22234         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22235         if(this.grow){
22236             this.textSizeEl = Roo.DomHelper.append(document.body, {
22237                 tag: "pre", cls: "x-form-grow-sizer"
22238             });
22239             if(this.preventScrollbars){
22240                 this.el.setStyle("overflow", "hidden");
22241             }
22242             this.el.setHeight(this.growMin);
22243         }
22244     },
22245
22246     onDestroy : function(){
22247         if(this.textSizeEl){
22248             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22249         }
22250         Roo.form.TextArea.superclass.onDestroy.call(this);
22251     },
22252
22253     // private
22254     onKeyUp : function(e){
22255         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22256             this.autoSize();
22257         }
22258     },
22259
22260     /**
22261      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22262      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22263      */
22264     autoSize : function(){
22265         if(!this.grow || !this.textSizeEl){
22266             return;
22267         }
22268         var el = this.el;
22269         var v = el.dom.value;
22270         var ts = this.textSizeEl;
22271
22272         ts.innerHTML = '';
22273         ts.appendChild(document.createTextNode(v));
22274         v = ts.innerHTML;
22275
22276         Roo.fly(ts).setWidth(this.el.getWidth());
22277         if(v.length < 1){
22278             v = "&#160;&#160;";
22279         }else{
22280             if(Roo.isIE){
22281                 v = v.replace(/\n/g, '<p>&#160;</p>');
22282             }
22283             v += "&#160;\n&#160;";
22284         }
22285         ts.innerHTML = v;
22286         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22287         if(h != this.lastHeight){
22288             this.lastHeight = h;
22289             this.el.setHeight(h);
22290             this.fireEvent("autosize", this, h);
22291         }
22292     }
22293 });/*
22294  * Based on:
22295  * Ext JS Library 1.1.1
22296  * Copyright(c) 2006-2007, Ext JS, LLC.
22297  *
22298  * Originally Released Under LGPL - original licence link has changed is not relivant.
22299  *
22300  * Fork - LGPL
22301  * <script type="text/javascript">
22302  */
22303  
22304
22305 /**
22306  * @class Roo.form.NumberField
22307  * @extends Roo.form.TextField
22308  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22309  * @constructor
22310  * Creates a new NumberField
22311  * @param {Object} config Configuration options
22312  */
22313 Roo.form.NumberField = function(config){
22314     Roo.form.NumberField.superclass.constructor.call(this, config);
22315 };
22316
22317 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22318     /**
22319      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22320      */
22321     fieldClass: "x-form-field x-form-num-field",
22322     /**
22323      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22324      */
22325     allowDecimals : true,
22326     /**
22327      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22328      */
22329     decimalSeparator : ".",
22330     /**
22331      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22332      */
22333     decimalPrecision : 2,
22334     /**
22335      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22336      */
22337     allowNegative : true,
22338     /**
22339      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22340      */
22341     minValue : Number.NEGATIVE_INFINITY,
22342     /**
22343      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22344      */
22345     maxValue : Number.MAX_VALUE,
22346     /**
22347      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22348      */
22349     minText : "The minimum value for this field is {0}",
22350     /**
22351      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22352      */
22353     maxText : "The maximum value for this field is {0}",
22354     /**
22355      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22356      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22357      */
22358     nanText : "{0} is not a valid number",
22359
22360     // private
22361     initEvents : function(){
22362         Roo.form.NumberField.superclass.initEvents.call(this);
22363         var allowed = "0123456789";
22364         if(this.allowDecimals){
22365             allowed += this.decimalSeparator;
22366         }
22367         if(this.allowNegative){
22368             allowed += "-";
22369         }
22370         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22371         var keyPress = function(e){
22372             var k = e.getKey();
22373             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22374                 return;
22375             }
22376             var c = e.getCharCode();
22377             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22378                 e.stopEvent();
22379             }
22380         };
22381         this.el.on("keypress", keyPress, this);
22382     },
22383
22384     // private
22385     validateValue : function(value){
22386         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22387             return false;
22388         }
22389         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22390              return true;
22391         }
22392         var num = this.parseValue(value);
22393         if(isNaN(num)){
22394             this.markInvalid(String.format(this.nanText, value));
22395             return false;
22396         }
22397         if(num < this.minValue){
22398             this.markInvalid(String.format(this.minText, this.minValue));
22399             return false;
22400         }
22401         if(num > this.maxValue){
22402             this.markInvalid(String.format(this.maxText, this.maxValue));
22403             return false;
22404         }
22405         return true;
22406     },
22407
22408     getValue : function(){
22409         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22410     },
22411
22412     // private
22413     parseValue : function(value){
22414         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22415         return isNaN(value) ? '' : value;
22416     },
22417
22418     // private
22419     fixPrecision : function(value){
22420         var nan = isNaN(value);
22421         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22422             return nan ? '' : value;
22423         }
22424         return parseFloat(value).toFixed(this.decimalPrecision);
22425     },
22426
22427     setValue : function(v){
22428         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22429     },
22430
22431     // private
22432     decimalPrecisionFcn : function(v){
22433         return Math.floor(v);
22434     },
22435
22436     beforeBlur : function(){
22437         var v = this.parseValue(this.getRawValue());
22438         if(v){
22439             this.setValue(this.fixPrecision(v));
22440         }
22441     }
22442 });/*
22443  * Based on:
22444  * Ext JS Library 1.1.1
22445  * Copyright(c) 2006-2007, Ext JS, LLC.
22446  *
22447  * Originally Released Under LGPL - original licence link has changed is not relivant.
22448  *
22449  * Fork - LGPL
22450  * <script type="text/javascript">
22451  */
22452  
22453 /**
22454  * @class Roo.form.DateField
22455  * @extends Roo.form.TriggerField
22456  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22457 * @constructor
22458 * Create a new DateField
22459 * @param {Object} config
22460  */
22461 Roo.form.DateField = function(config){
22462     Roo.form.DateField.superclass.constructor.call(this, config);
22463     
22464       this.addEvents({
22465          
22466         /**
22467          * @event select
22468          * Fires when a date is selected
22469              * @param {Roo.form.DateField} combo This combo box
22470              * @param {Date} date The date selected
22471              */
22472         'select' : true
22473          
22474     });
22475     
22476     
22477     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22478     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22479     this.ddMatch = null;
22480     if(this.disabledDates){
22481         var dd = this.disabledDates;
22482         var re = "(?:";
22483         for(var i = 0; i < dd.length; i++){
22484             re += dd[i];
22485             if(i != dd.length-1) re += "|";
22486         }
22487         this.ddMatch = new RegExp(re + ")");
22488     }
22489 };
22490
22491 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22492     /**
22493      * @cfg {String} format
22494      * The default date format string which can be overriden for localization support.  The format must be
22495      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22496      */
22497     format : "m/d/y",
22498     /**
22499      * @cfg {String} altFormats
22500      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22501      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22502      */
22503     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22504     /**
22505      * @cfg {Array} disabledDays
22506      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22507      */
22508     disabledDays : null,
22509     /**
22510      * @cfg {String} disabledDaysText
22511      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22512      */
22513     disabledDaysText : "Disabled",
22514     /**
22515      * @cfg {Array} disabledDates
22516      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22517      * expression so they are very powerful. Some examples:
22518      * <ul>
22519      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22520      * <li>["03/08", "09/16"] would disable those days for every year</li>
22521      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22522      * <li>["03/../2006"] would disable every day in March 2006</li>
22523      * <li>["^03"] would disable every day in every March</li>
22524      * </ul>
22525      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22526      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22527      */
22528     disabledDates : null,
22529     /**
22530      * @cfg {String} disabledDatesText
22531      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22532      */
22533     disabledDatesText : "Disabled",
22534     /**
22535      * @cfg {Date/String} minValue
22536      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22537      * valid format (defaults to null).
22538      */
22539     minValue : null,
22540     /**
22541      * @cfg {Date/String} maxValue
22542      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22543      * valid format (defaults to null).
22544      */
22545     maxValue : null,
22546     /**
22547      * @cfg {String} minText
22548      * The error text to display when the date in the cell is before minValue (defaults to
22549      * 'The date in this field must be after {minValue}').
22550      */
22551     minText : "The date in this field must be equal to or after {0}",
22552     /**
22553      * @cfg {String} maxText
22554      * The error text to display when the date in the cell is after maxValue (defaults to
22555      * 'The date in this field must be before {maxValue}').
22556      */
22557     maxText : "The date in this field must be equal to or before {0}",
22558     /**
22559      * @cfg {String} invalidText
22560      * The error text to display when the date in the field is invalid (defaults to
22561      * '{value} is not a valid date - it must be in the format {format}').
22562      */
22563     invalidText : "{0} is not a valid date - it must be in the format {1}",
22564     /**
22565      * @cfg {String} triggerClass
22566      * An additional CSS class used to style the trigger button.  The trigger will always get the
22567      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22568      * which displays a calendar icon).
22569      */
22570     triggerClass : 'x-form-date-trigger',
22571     
22572
22573     /**
22574      * @cfg {bool} useIso
22575      * if enabled, then the date field will use a hidden field to store the 
22576      * real value as iso formated date. default (false)
22577      */ 
22578     useIso : false,
22579     /**
22580      * @cfg {String/Object} autoCreate
22581      * A DomHelper element spec, or true for a default element spec (defaults to
22582      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22583      */ 
22584     // private
22585     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22586     
22587     // private
22588     hiddenField: false,
22589     
22590     onRender : function(ct, position)
22591     {
22592         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22593         if (this.useIso) {
22594             this.el.dom.removeAttribute('name'); 
22595             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22596                     'before', true);
22597             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22598             // prevent input submission
22599             this.hiddenName = this.name;
22600         }
22601             
22602             
22603     },
22604     
22605     // private
22606     validateValue : function(value)
22607     {
22608         value = this.formatDate(value);
22609         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22610             return false;
22611         }
22612         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22613              return true;
22614         }
22615         var svalue = value;
22616         value = this.parseDate(value);
22617         if(!value){
22618             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22619             return false;
22620         }
22621         var time = value.getTime();
22622         if(this.minValue && time < this.minValue.getTime()){
22623             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22624             return false;
22625         }
22626         if(this.maxValue && time > this.maxValue.getTime()){
22627             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22628             return false;
22629         }
22630         if(this.disabledDays){
22631             var day = value.getDay();
22632             for(var i = 0; i < this.disabledDays.length; i++) {
22633                 if(day === this.disabledDays[i]){
22634                     this.markInvalid(this.disabledDaysText);
22635                     return false;
22636                 }
22637             }
22638         }
22639         var fvalue = this.formatDate(value);
22640         if(this.ddMatch && this.ddMatch.test(fvalue)){
22641             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22642             return false;
22643         }
22644         return true;
22645     },
22646
22647     // private
22648     // Provides logic to override the default TriggerField.validateBlur which just returns true
22649     validateBlur : function(){
22650         return !this.menu || !this.menu.isVisible();
22651     },
22652
22653     /**
22654      * Returns the current date value of the date field.
22655      * @return {Date} The date value
22656      */
22657     getValue : function(){
22658         
22659         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22660     },
22661
22662     /**
22663      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22664      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22665      * (the default format used is "m/d/y").
22666      * <br />Usage:
22667      * <pre><code>
22668 //All of these calls set the same date value (May 4, 2006)
22669
22670 //Pass a date object:
22671 var dt = new Date('5/4/06');
22672 dateField.setValue(dt);
22673
22674 //Pass a date string (default format):
22675 dateField.setValue('5/4/06');
22676
22677 //Pass a date string (custom format):
22678 dateField.format = 'Y-m-d';
22679 dateField.setValue('2006-5-4');
22680 </code></pre>
22681      * @param {String/Date} date The date or valid date string
22682      */
22683     setValue : function(date){
22684         if (this.hiddenField) {
22685             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22686         }
22687         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22688     },
22689
22690     // private
22691     parseDate : function(value){
22692         if(!value || value instanceof Date){
22693             return value;
22694         }
22695         var v = Date.parseDate(value, this.format);
22696         if(!v && this.altFormats){
22697             if(!this.altFormatsArray){
22698                 this.altFormatsArray = this.altFormats.split("|");
22699             }
22700             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22701                 v = Date.parseDate(value, this.altFormatsArray[i]);
22702             }
22703         }
22704         return v;
22705     },
22706
22707     // private
22708     formatDate : function(date, fmt){
22709         return (!date || !(date instanceof Date)) ?
22710                date : date.dateFormat(fmt || this.format);
22711     },
22712
22713     // private
22714     menuListeners : {
22715         select: function(m, d){
22716             this.setValue(d);
22717             this.fireEvent('select', this, d);
22718         },
22719         show : function(){ // retain focus styling
22720             this.onFocus();
22721         },
22722         hide : function(){
22723             this.focus.defer(10, this);
22724             var ml = this.menuListeners;
22725             this.menu.un("select", ml.select,  this);
22726             this.menu.un("show", ml.show,  this);
22727             this.menu.un("hide", ml.hide,  this);
22728         }
22729     },
22730
22731     // private
22732     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22733     onTriggerClick : function(){
22734         if(this.disabled){
22735             return;
22736         }
22737         if(this.menu == null){
22738             this.menu = new Roo.menu.DateMenu();
22739         }
22740         Roo.apply(this.menu.picker,  {
22741             showClear: this.allowBlank,
22742             minDate : this.minValue,
22743             maxDate : this.maxValue,
22744             disabledDatesRE : this.ddMatch,
22745             disabledDatesText : this.disabledDatesText,
22746             disabledDays : this.disabledDays,
22747             disabledDaysText : this.disabledDaysText,
22748             format : this.format,
22749             minText : String.format(this.minText, this.formatDate(this.minValue)),
22750             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22751         });
22752         this.menu.on(Roo.apply({}, this.menuListeners, {
22753             scope:this
22754         }));
22755         this.menu.picker.setValue(this.getValue() || new Date());
22756         this.menu.show(this.el, "tl-bl?");
22757     },
22758
22759     beforeBlur : function(){
22760         var v = this.parseDate(this.getRawValue());
22761         if(v){
22762             this.setValue(v);
22763         }
22764     }
22765
22766     /** @cfg {Boolean} grow @hide */
22767     /** @cfg {Number} growMin @hide */
22768     /** @cfg {Number} growMax @hide */
22769     /**
22770      * @hide
22771      * @method autoSize
22772      */
22773 });/*
22774  * Based on:
22775  * Ext JS Library 1.1.1
22776  * Copyright(c) 2006-2007, Ext JS, LLC.
22777  *
22778  * Originally Released Under LGPL - original licence link has changed is not relivant.
22779  *
22780  * Fork - LGPL
22781  * <script type="text/javascript">
22782  */
22783  
22784
22785 /**
22786  * @class Roo.form.ComboBox
22787  * @extends Roo.form.TriggerField
22788  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22789  * @constructor
22790  * Create a new ComboBox.
22791  * @param {Object} config Configuration options
22792  */
22793 Roo.form.ComboBox = function(config){
22794     Roo.form.ComboBox.superclass.constructor.call(this, config);
22795     this.addEvents({
22796         /**
22797          * @event expand
22798          * Fires when the dropdown list is expanded
22799              * @param {Roo.form.ComboBox} combo This combo box
22800              */
22801         'expand' : true,
22802         /**
22803          * @event collapse
22804          * Fires when the dropdown list is collapsed
22805              * @param {Roo.form.ComboBox} combo This combo box
22806              */
22807         'collapse' : true,
22808         /**
22809          * @event beforeselect
22810          * Fires before a list item is selected. Return false to cancel the selection.
22811              * @param {Roo.form.ComboBox} combo This combo box
22812              * @param {Roo.data.Record} record The data record returned from the underlying store
22813              * @param {Number} index The index of the selected item in the dropdown list
22814              */
22815         'beforeselect' : true,
22816         /**
22817          * @event select
22818          * Fires when a list item is selected
22819              * @param {Roo.form.ComboBox} combo This combo box
22820              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22821              * @param {Number} index The index of the selected item in the dropdown list
22822              */
22823         'select' : true,
22824         /**
22825          * @event beforequery
22826          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22827          * The event object passed has these properties:
22828              * @param {Roo.form.ComboBox} combo This combo box
22829              * @param {String} query The query
22830              * @param {Boolean} forceAll true to force "all" query
22831              * @param {Boolean} cancel true to cancel the query
22832              * @param {Object} e The query event object
22833              */
22834         'beforequery': true,
22835          /**
22836          * @event add
22837          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22838              * @param {Roo.form.ComboBox} combo This combo box
22839              */
22840         'add' : true,
22841         /**
22842          * @event edit
22843          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22844              * @param {Roo.form.ComboBox} combo This combo box
22845              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22846              */
22847         'edit' : true
22848         
22849         
22850     });
22851     if(this.transform){
22852         this.allowDomMove = false;
22853         var s = Roo.getDom(this.transform);
22854         if(!this.hiddenName){
22855             this.hiddenName = s.name;
22856         }
22857         if(!this.store){
22858             this.mode = 'local';
22859             var d = [], opts = s.options;
22860             for(var i = 0, len = opts.length;i < len; i++){
22861                 var o = opts[i];
22862                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22863                 if(o.selected) {
22864                     this.value = value;
22865                 }
22866                 d.push([value, o.text]);
22867             }
22868             this.store = new Roo.data.SimpleStore({
22869                 'id': 0,
22870                 fields: ['value', 'text'],
22871                 data : d
22872             });
22873             this.valueField = 'value';
22874             this.displayField = 'text';
22875         }
22876         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22877         if(!this.lazyRender){
22878             this.target = true;
22879             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22880             s.parentNode.removeChild(s); // remove it
22881             this.render(this.el.parentNode);
22882         }else{
22883             s.parentNode.removeChild(s); // remove it
22884         }
22885
22886     }
22887     if (this.store) {
22888         this.store = Roo.factory(this.store, Roo.data);
22889     }
22890     
22891     this.selectedIndex = -1;
22892     if(this.mode == 'local'){
22893         if(config.queryDelay === undefined){
22894             this.queryDelay = 10;
22895         }
22896         if(config.minChars === undefined){
22897             this.minChars = 0;
22898         }
22899     }
22900 };
22901
22902 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22903     /**
22904      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22905      */
22906     /**
22907      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22908      * rendering into an Roo.Editor, defaults to false)
22909      */
22910     /**
22911      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22912      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22913      */
22914     /**
22915      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22916      */
22917     /**
22918      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22919      * the dropdown list (defaults to undefined, with no header element)
22920      */
22921
22922      /**
22923      * @cfg {String/Roo.Template} tpl The template to use to render the output
22924      */
22925      
22926     // private
22927     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22928     /**
22929      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22930      */
22931     listWidth: undefined,
22932     /**
22933      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22934      * mode = 'remote' or 'text' if mode = 'local')
22935      */
22936     displayField: undefined,
22937     /**
22938      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22939      * mode = 'remote' or 'value' if mode = 'local'). 
22940      * Note: use of a valueField requires the user make a selection
22941      * in order for a value to be mapped.
22942      */
22943     valueField: undefined,
22944     
22945     
22946     /**
22947      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22948      * field's data value (defaults to the underlying DOM element's name)
22949      */
22950     hiddenName: undefined,
22951     /**
22952      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22953      */
22954     listClass: '',
22955     /**
22956      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22957      */
22958     selectedClass: 'x-combo-selected',
22959     /**
22960      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22961      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22962      * which displays a downward arrow icon).
22963      */
22964     triggerClass : 'x-form-arrow-trigger',
22965     /**
22966      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22967      */
22968     shadow:'sides',
22969     /**
22970      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22971      * anchor positions (defaults to 'tl-bl')
22972      */
22973     listAlign: 'tl-bl?',
22974     /**
22975      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22976      */
22977     maxHeight: 300,
22978     /**
22979      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22980      * query specified by the allQuery config option (defaults to 'query')
22981      */
22982     triggerAction: 'query',
22983     /**
22984      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22985      * (defaults to 4, does not apply if editable = false)
22986      */
22987     minChars : 4,
22988     /**
22989      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22990      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22991      */
22992     typeAhead: false,
22993     /**
22994      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22995      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22996      */
22997     queryDelay: 500,
22998     /**
22999      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23000      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23001      */
23002     pageSize: 0,
23003     /**
23004      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23005      * when editable = true (defaults to false)
23006      */
23007     selectOnFocus:false,
23008     /**
23009      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23010      */
23011     queryParam: 'query',
23012     /**
23013      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23014      * when mode = 'remote' (defaults to 'Loading...')
23015      */
23016     loadingText: 'Loading...',
23017     /**
23018      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23019      */
23020     resizable: false,
23021     /**
23022      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23023      */
23024     handleHeight : 8,
23025     /**
23026      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23027      * traditional select (defaults to true)
23028      */
23029     editable: true,
23030     /**
23031      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23032      */
23033     allQuery: '',
23034     /**
23035      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23036      */
23037     mode: 'remote',
23038     /**
23039      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23040      * listWidth has a higher value)
23041      */
23042     minListWidth : 70,
23043     /**
23044      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23045      * allow the user to set arbitrary text into the field (defaults to false)
23046      */
23047     forceSelection:false,
23048     /**
23049      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23050      * if typeAhead = true (defaults to 250)
23051      */
23052     typeAheadDelay : 250,
23053     /**
23054      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23055      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23056      */
23057     valueNotFoundText : undefined,
23058     /**
23059      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23060      */
23061     blockFocus : false,
23062     
23063     /**
23064      * @cfg {Boolean} disableClear Disable showing of clear button.
23065      */
23066     disableClear : false,
23067     /**
23068      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23069      */
23070     alwaysQuery : false,
23071     
23072     //private
23073     addicon : false,
23074     editicon: false,
23075     
23076     // element that contains real text value.. (when hidden is used..)
23077      
23078     // private
23079     onRender : function(ct, position){
23080         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23081         if(this.hiddenName){
23082             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23083                     'before', true);
23084             this.hiddenField.value =
23085                 this.hiddenValue !== undefined ? this.hiddenValue :
23086                 this.value !== undefined ? this.value : '';
23087
23088             // prevent input submission
23089             this.el.dom.removeAttribute('name');
23090              
23091              
23092         }
23093         if(Roo.isGecko){
23094             this.el.dom.setAttribute('autocomplete', 'off');
23095         }
23096
23097         var cls = 'x-combo-list';
23098
23099         this.list = new Roo.Layer({
23100             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23101         });
23102
23103         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23104         this.list.setWidth(lw);
23105         this.list.swallowEvent('mousewheel');
23106         this.assetHeight = 0;
23107
23108         if(this.title){
23109             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23110             this.assetHeight += this.header.getHeight();
23111         }
23112
23113         this.innerList = this.list.createChild({cls:cls+'-inner'});
23114         this.innerList.on('mouseover', this.onViewOver, this);
23115         this.innerList.on('mousemove', this.onViewMove, this);
23116         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23117         
23118         if(this.allowBlank && !this.pageSize && !this.disableClear){
23119             this.footer = this.list.createChild({cls:cls+'-ft'});
23120             this.pageTb = new Roo.Toolbar(this.footer);
23121            
23122         }
23123         if(this.pageSize){
23124             this.footer = this.list.createChild({cls:cls+'-ft'});
23125             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23126                     {pageSize: this.pageSize});
23127             
23128         }
23129         
23130         if (this.pageTb && this.allowBlank && !this.disableClear) {
23131             var _this = this;
23132             this.pageTb.add(new Roo.Toolbar.Fill(), {
23133                 cls: 'x-btn-icon x-btn-clear',
23134                 text: '&#160;',
23135                 handler: function()
23136                 {
23137                     _this.collapse();
23138                     _this.clearValue();
23139                     _this.onSelect(false, -1);
23140                 }
23141             });
23142         }
23143         if (this.footer) {
23144             this.assetHeight += this.footer.getHeight();
23145         }
23146         
23147
23148         if(!this.tpl){
23149             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23150         }
23151
23152         this.view = new Roo.View(this.innerList, this.tpl, {
23153             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23154         });
23155
23156         this.view.on('click', this.onViewClick, this);
23157
23158         this.store.on('beforeload', this.onBeforeLoad, this);
23159         this.store.on('load', this.onLoad, this);
23160         this.store.on('loadexception', this.onLoadException, this);
23161
23162         if(this.resizable){
23163             this.resizer = new Roo.Resizable(this.list,  {
23164                pinned:true, handles:'se'
23165             });
23166             this.resizer.on('resize', function(r, w, h){
23167                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23168                 this.listWidth = w;
23169                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23170                 this.restrictHeight();
23171             }, this);
23172             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23173         }
23174         if(!this.editable){
23175             this.editable = true;
23176             this.setEditable(false);
23177         }  
23178         
23179         
23180         if (typeof(this.events.add.listeners) != 'undefined') {
23181             
23182             this.addicon = this.wrap.createChild(
23183                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23184        
23185             this.addicon.on('click', function(e) {
23186                 this.fireEvent('add', this);
23187             }, this);
23188         }
23189         if (typeof(this.events.edit.listeners) != 'undefined') {
23190             
23191             this.editicon = this.wrap.createChild(
23192                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23193             if (this.addicon) {
23194                 this.editicon.setStyle('margin-left', '40px');
23195             }
23196             this.editicon.on('click', function(e) {
23197                 
23198                 // we fire even  if inothing is selected..
23199                 this.fireEvent('edit', this, this.lastData );
23200                 
23201             }, this);
23202         }
23203         
23204         
23205         
23206     },
23207
23208     // private
23209     initEvents : function(){
23210         Roo.form.ComboBox.superclass.initEvents.call(this);
23211
23212         this.keyNav = new Roo.KeyNav(this.el, {
23213             "up" : function(e){
23214                 this.inKeyMode = true;
23215                 this.selectPrev();
23216             },
23217
23218             "down" : function(e){
23219                 if(!this.isExpanded()){
23220                     this.onTriggerClick();
23221                 }else{
23222                     this.inKeyMode = true;
23223                     this.selectNext();
23224                 }
23225             },
23226
23227             "enter" : function(e){
23228                 this.onViewClick();
23229                 //return true;
23230             },
23231
23232             "esc" : function(e){
23233                 this.collapse();
23234             },
23235
23236             "tab" : function(e){
23237                 this.onViewClick(false);
23238                 this.fireEvent("specialkey", this, e);
23239                 return true;
23240             },
23241
23242             scope : this,
23243
23244             doRelay : function(foo, bar, hname){
23245                 if(hname == 'down' || this.scope.isExpanded()){
23246                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23247                 }
23248                 return true;
23249             },
23250
23251             forceKeyDown: true
23252         });
23253         this.queryDelay = Math.max(this.queryDelay || 10,
23254                 this.mode == 'local' ? 10 : 250);
23255         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23256         if(this.typeAhead){
23257             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23258         }
23259         if(this.editable !== false){
23260             this.el.on("keyup", this.onKeyUp, this);
23261         }
23262         if(this.forceSelection){
23263             this.on('blur', this.doForce, this);
23264         }
23265     },
23266
23267     onDestroy : function(){
23268         if(this.view){
23269             this.view.setStore(null);
23270             this.view.el.removeAllListeners();
23271             this.view.el.remove();
23272             this.view.purgeListeners();
23273         }
23274         if(this.list){
23275             this.list.destroy();
23276         }
23277         if(this.store){
23278             this.store.un('beforeload', this.onBeforeLoad, this);
23279             this.store.un('load', this.onLoad, this);
23280             this.store.un('loadexception', this.onLoadException, this);
23281         }
23282         Roo.form.ComboBox.superclass.onDestroy.call(this);
23283     },
23284
23285     // private
23286     fireKey : function(e){
23287         if(e.isNavKeyPress() && !this.list.isVisible()){
23288             this.fireEvent("specialkey", this, e);
23289         }
23290     },
23291
23292     // private
23293     onResize: function(w, h){
23294         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23295         
23296         if(typeof w != 'number'){
23297             // we do not handle it!?!?
23298             return;
23299         }
23300         var tw = this.trigger.getWidth();
23301         tw += this.addicon ? this.addicon.getWidth() : 0;
23302         tw += this.editicon ? this.editicon.getWidth() : 0;
23303         var x = w - tw;
23304         this.el.setWidth( this.adjustWidth('input', x));
23305             
23306         this.trigger.setStyle('left', x+'px');
23307         
23308         if(this.list && this.listWidth === undefined){
23309             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23310             this.list.setWidth(lw);
23311             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23312         }
23313         
23314     
23315         
23316     },
23317
23318     /**
23319      * Allow or prevent the user from directly editing the field text.  If false is passed,
23320      * the user will only be able to select from the items defined in the dropdown list.  This method
23321      * is the runtime equivalent of setting the 'editable' config option at config time.
23322      * @param {Boolean} value True to allow the user to directly edit the field text
23323      */
23324     setEditable : function(value){
23325         if(value == this.editable){
23326             return;
23327         }
23328         this.editable = value;
23329         if(!value){
23330             this.el.dom.setAttribute('readOnly', true);
23331             this.el.on('mousedown', this.onTriggerClick,  this);
23332             this.el.addClass('x-combo-noedit');
23333         }else{
23334             this.el.dom.setAttribute('readOnly', false);
23335             this.el.un('mousedown', this.onTriggerClick,  this);
23336             this.el.removeClass('x-combo-noedit');
23337         }
23338     },
23339
23340     // private
23341     onBeforeLoad : function(){
23342         if(!this.hasFocus){
23343             return;
23344         }
23345         this.innerList.update(this.loadingText ?
23346                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23347         this.restrictHeight();
23348         this.selectedIndex = -1;
23349     },
23350
23351     // private
23352     onLoad : function(){
23353         if(!this.hasFocus){
23354             return;
23355         }
23356         if(this.store.getCount() > 0){
23357             this.expand();
23358             this.restrictHeight();
23359             if(this.lastQuery == this.allQuery){
23360                 if(this.editable){
23361                     this.el.dom.select();
23362                 }
23363                 if(!this.selectByValue(this.value, true)){
23364                     this.select(0, true);
23365                 }
23366             }else{
23367                 this.selectNext();
23368                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23369                     this.taTask.delay(this.typeAheadDelay);
23370                 }
23371             }
23372         }else{
23373             this.onEmptyResults();
23374         }
23375         //this.el.focus();
23376     },
23377     // private
23378     onLoadException : function()
23379     {
23380         this.collapse();
23381         Roo.log(this.store.reader.jsonData);
23382         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23383             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23384         }
23385         
23386         
23387     },
23388     // private
23389     onTypeAhead : function(){
23390         if(this.store.getCount() > 0){
23391             var r = this.store.getAt(0);
23392             var newValue = r.data[this.displayField];
23393             var len = newValue.length;
23394             var selStart = this.getRawValue().length;
23395             if(selStart != len){
23396                 this.setRawValue(newValue);
23397                 this.selectText(selStart, newValue.length);
23398             }
23399         }
23400     },
23401
23402     // private
23403     onSelect : function(record, index){
23404         if(this.fireEvent('beforeselect', this, record, index) !== false){
23405             this.setFromData(index > -1 ? record.data : false);
23406             this.collapse();
23407             this.fireEvent('select', this, record, index);
23408         }
23409     },
23410
23411     /**
23412      * Returns the currently selected field value or empty string if no value is set.
23413      * @return {String} value The selected value
23414      */
23415     getValue : function(){
23416         if(this.valueField){
23417             return typeof this.value != 'undefined' ? this.value : '';
23418         }else{
23419             return Roo.form.ComboBox.superclass.getValue.call(this);
23420         }
23421     },
23422
23423     /**
23424      * Clears any text/value currently set in the field
23425      */
23426     clearValue : function(){
23427         if(this.hiddenField){
23428             this.hiddenField.value = '';
23429         }
23430         this.value = '';
23431         this.setRawValue('');
23432         this.lastSelectionText = '';
23433         this.applyEmptyText();
23434     },
23435
23436     /**
23437      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23438      * will be displayed in the field.  If the value does not match the data value of an existing item,
23439      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23440      * Otherwise the field will be blank (although the value will still be set).
23441      * @param {String} value The value to match
23442      */
23443     setValue : function(v){
23444         var text = v;
23445         if(this.valueField){
23446             var r = this.findRecord(this.valueField, v);
23447             if(r){
23448                 text = r.data[this.displayField];
23449             }else if(this.valueNotFoundText !== undefined){
23450                 text = this.valueNotFoundText;
23451             }
23452         }
23453         this.lastSelectionText = text;
23454         if(this.hiddenField){
23455             this.hiddenField.value = v;
23456         }
23457         Roo.form.ComboBox.superclass.setValue.call(this, text);
23458         this.value = v;
23459     },
23460     /**
23461      * @property {Object} the last set data for the element
23462      */
23463     
23464     lastData : false,
23465     /**
23466      * Sets the value of the field based on a object which is related to the record format for the store.
23467      * @param {Object} value the value to set as. or false on reset?
23468      */
23469     setFromData : function(o){
23470         var dv = ''; // display value
23471         var vv = ''; // value value..
23472         this.lastData = o;
23473         if (this.displayField) {
23474             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23475         } else {
23476             // this is an error condition!!!
23477             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23478         }
23479         
23480         if(this.valueField){
23481             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23482         }
23483         if(this.hiddenField){
23484             this.hiddenField.value = vv;
23485             
23486             this.lastSelectionText = dv;
23487             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23488             this.value = vv;
23489             return;
23490         }
23491         // no hidden field.. - we store the value in 'value', but still display
23492         // display field!!!!
23493         this.lastSelectionText = dv;
23494         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23495         this.value = vv;
23496         
23497         
23498     },
23499     // private
23500     reset : function(){
23501         // overridden so that last data is reset..
23502         this.setValue(this.originalValue);
23503         this.clearInvalid();
23504         this.lastData = false;
23505     },
23506     // private
23507     findRecord : function(prop, value){
23508         var record;
23509         if(this.store.getCount() > 0){
23510             this.store.each(function(r){
23511                 if(r.data[prop] == value){
23512                     record = r;
23513                     return false;
23514                 }
23515                 return true;
23516             });
23517         }
23518         return record;
23519     },
23520     
23521     getName: function()
23522     {
23523         // returns hidden if it's set..
23524         if (!this.rendered) {return ''};
23525         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23526         
23527     },
23528     // private
23529     onViewMove : function(e, t){
23530         this.inKeyMode = false;
23531     },
23532
23533     // private
23534     onViewOver : function(e, t){
23535         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23536             return;
23537         }
23538         var item = this.view.findItemFromChild(t);
23539         if(item){
23540             var index = this.view.indexOf(item);
23541             this.select(index, false);
23542         }
23543     },
23544
23545     // private
23546     onViewClick : function(doFocus)
23547     {
23548         var index = this.view.getSelectedIndexes()[0];
23549         var r = this.store.getAt(index);
23550         if(r){
23551             this.onSelect(r, index);
23552         }
23553         if(doFocus !== false && !this.blockFocus){
23554             this.el.focus();
23555         }
23556     },
23557
23558     // private
23559     restrictHeight : function(){
23560         this.innerList.dom.style.height = '';
23561         var inner = this.innerList.dom;
23562         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23563         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23564         this.list.beginUpdate();
23565         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23566         this.list.alignTo(this.el, this.listAlign);
23567         this.list.endUpdate();
23568     },
23569
23570     // private
23571     onEmptyResults : function(){
23572         this.collapse();
23573     },
23574
23575     /**
23576      * Returns true if the dropdown list is expanded, else false.
23577      */
23578     isExpanded : function(){
23579         return this.list.isVisible();
23580     },
23581
23582     /**
23583      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23584      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23585      * @param {String} value The data value of the item to select
23586      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23587      * selected item if it is not currently in view (defaults to true)
23588      * @return {Boolean} True if the value matched an item in the list, else false
23589      */
23590     selectByValue : function(v, scrollIntoView){
23591         if(v !== undefined && v !== null){
23592             var r = this.findRecord(this.valueField || this.displayField, v);
23593             if(r){
23594                 this.select(this.store.indexOf(r), scrollIntoView);
23595                 return true;
23596             }
23597         }
23598         return false;
23599     },
23600
23601     /**
23602      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23603      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23604      * @param {Number} index The zero-based index of the list item to select
23605      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23606      * selected item if it is not currently in view (defaults to true)
23607      */
23608     select : function(index, scrollIntoView){
23609         this.selectedIndex = index;
23610         this.view.select(index);
23611         if(scrollIntoView !== false){
23612             var el = this.view.getNode(index);
23613             if(el){
23614                 this.innerList.scrollChildIntoView(el, false);
23615             }
23616         }
23617     },
23618
23619     // private
23620     selectNext : function(){
23621         var ct = this.store.getCount();
23622         if(ct > 0){
23623             if(this.selectedIndex == -1){
23624                 this.select(0);
23625             }else if(this.selectedIndex < ct-1){
23626                 this.select(this.selectedIndex+1);
23627             }
23628         }
23629     },
23630
23631     // private
23632     selectPrev : function(){
23633         var ct = this.store.getCount();
23634         if(ct > 0){
23635             if(this.selectedIndex == -1){
23636                 this.select(0);
23637             }else if(this.selectedIndex != 0){
23638                 this.select(this.selectedIndex-1);
23639             }
23640         }
23641     },
23642
23643     // private
23644     onKeyUp : function(e){
23645         if(this.editable !== false && !e.isSpecialKey()){
23646             this.lastKey = e.getKey();
23647             this.dqTask.delay(this.queryDelay);
23648         }
23649     },
23650
23651     // private
23652     validateBlur : function(){
23653         return !this.list || !this.list.isVisible();   
23654     },
23655
23656     // private
23657     initQuery : function(){
23658         this.doQuery(this.getRawValue());
23659     },
23660
23661     // private
23662     doForce : function(){
23663         if(this.el.dom.value.length > 0){
23664             this.el.dom.value =
23665                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23666             this.applyEmptyText();
23667         }
23668     },
23669
23670     /**
23671      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23672      * query allowing the query action to be canceled if needed.
23673      * @param {String} query The SQL query to execute
23674      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23675      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23676      * saved in the current store (defaults to false)
23677      */
23678     doQuery : function(q, forceAll){
23679         if(q === undefined || q === null){
23680             q = '';
23681         }
23682         var qe = {
23683             query: q,
23684             forceAll: forceAll,
23685             combo: this,
23686             cancel:false
23687         };
23688         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23689             return false;
23690         }
23691         q = qe.query;
23692         forceAll = qe.forceAll;
23693         if(forceAll === true || (q.length >= this.minChars)){
23694             if(this.lastQuery != q || this.alwaysQuery){
23695                 this.lastQuery = q;
23696                 if(this.mode == 'local'){
23697                     this.selectedIndex = -1;
23698                     if(forceAll){
23699                         this.store.clearFilter();
23700                     }else{
23701                         this.store.filter(this.displayField, q);
23702                     }
23703                     this.onLoad();
23704                 }else{
23705                     this.store.baseParams[this.queryParam] = q;
23706                     this.store.load({
23707                         params: this.getParams(q)
23708                     });
23709                     this.expand();
23710                 }
23711             }else{
23712                 this.selectedIndex = -1;
23713                 this.onLoad();   
23714             }
23715         }
23716     },
23717
23718     // private
23719     getParams : function(q){
23720         var p = {};
23721         //p[this.queryParam] = q;
23722         if(this.pageSize){
23723             p.start = 0;
23724             p.limit = this.pageSize;
23725         }
23726         return p;
23727     },
23728
23729     /**
23730      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23731      */
23732     collapse : function(){
23733         if(!this.isExpanded()){
23734             return;
23735         }
23736         this.list.hide();
23737         Roo.get(document).un('mousedown', this.collapseIf, this);
23738         Roo.get(document).un('mousewheel', this.collapseIf, this);
23739         if (!this.editable) {
23740             Roo.get(document).un('keydown', this.listKeyPress, this);
23741         }
23742         this.fireEvent('collapse', this);
23743     },
23744
23745     // private
23746     collapseIf : function(e){
23747         if(!e.within(this.wrap) && !e.within(this.list)){
23748             this.collapse();
23749         }
23750     },
23751
23752     /**
23753      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23754      */
23755     expand : function(){
23756         if(this.isExpanded() || !this.hasFocus){
23757             return;
23758         }
23759         this.list.alignTo(this.el, this.listAlign);
23760         this.list.show();
23761         Roo.get(document).on('mousedown', this.collapseIf, this);
23762         Roo.get(document).on('mousewheel', this.collapseIf, this);
23763         if (!this.editable) {
23764             Roo.get(document).on('keydown', this.listKeyPress, this);
23765         }
23766         
23767         this.fireEvent('expand', this);
23768     },
23769
23770     // private
23771     // Implements the default empty TriggerField.onTriggerClick function
23772     onTriggerClick : function(){
23773         if(this.disabled){
23774             return;
23775         }
23776         if(this.isExpanded()){
23777             this.collapse();
23778             if (!this.blockFocus) {
23779                 this.el.focus();
23780             }
23781             
23782         }else {
23783             this.hasFocus = true;
23784             if(this.triggerAction == 'all') {
23785                 this.doQuery(this.allQuery, true);
23786             } else {
23787                 this.doQuery(this.getRawValue());
23788             }
23789             if (!this.blockFocus) {
23790                 this.el.focus();
23791             }
23792         }
23793     },
23794     listKeyPress : function(e)
23795     {
23796         //Roo.log('listkeypress');
23797         // scroll to first matching element based on key pres..
23798         if (e.isSpecialKey()) {
23799             return false;
23800         }
23801         var k = String.fromCharCode(e.getKey()).toUpperCase();
23802         //Roo.log(k);
23803         var match  = false;
23804         var csel = this.view.getSelectedNodes();
23805         var cselitem = false;
23806         if (csel.length) {
23807             var ix = this.view.indexOf(csel[0]);
23808             cselitem  = this.store.getAt(ix);
23809             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23810                 cselitem = false;
23811             }
23812             
23813         }
23814         
23815         this.store.each(function(v) { 
23816             if (cselitem) {
23817                 // start at existing selection.
23818                 if (cselitem.id == v.id) {
23819                     cselitem = false;
23820                 }
23821                 return;
23822             }
23823                 
23824             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23825                 match = this.store.indexOf(v);
23826                 return false;
23827             }
23828         }, this);
23829         
23830         if (match === false) {
23831             return true; // no more action?
23832         }
23833         // scroll to?
23834         this.view.select(match);
23835         var sn = Roo.get(this.view.getSelectedNodes()[0])
23836         sn.scrollIntoView(sn.dom.parentNode, false);
23837     }
23838
23839     /** 
23840     * @cfg {Boolean} grow 
23841     * @hide 
23842     */
23843     /** 
23844     * @cfg {Number} growMin 
23845     * @hide 
23846     */
23847     /** 
23848     * @cfg {Number} growMax 
23849     * @hide 
23850     */
23851     /**
23852      * @hide
23853      * @method autoSize
23854      */
23855 });/*
23856  * Based on:
23857  * Ext JS Library 1.1.1
23858  * Copyright(c) 2006-2007, Ext JS, LLC.
23859  *
23860  * Originally Released Under LGPL - original licence link has changed is not relivant.
23861  *
23862  * Fork - LGPL
23863  * <script type="text/javascript">
23864  */
23865 /**
23866  * @class Roo.form.Checkbox
23867  * @extends Roo.form.Field
23868  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23869  * @constructor
23870  * Creates a new Checkbox
23871  * @param {Object} config Configuration options
23872  */
23873 Roo.form.Checkbox = function(config){
23874     Roo.form.Checkbox.superclass.constructor.call(this, config);
23875     this.addEvents({
23876         /**
23877          * @event check
23878          * Fires when the checkbox is checked or unchecked.
23879              * @param {Roo.form.Checkbox} this This checkbox
23880              * @param {Boolean} checked The new checked value
23881              */
23882         check : true
23883     });
23884 };
23885
23886 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23887     /**
23888      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23889      */
23890     focusClass : undefined,
23891     /**
23892      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23893      */
23894     fieldClass: "x-form-field",
23895     /**
23896      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23897      */
23898     checked: false,
23899     /**
23900      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23901      * {tag: "input", type: "checkbox", autocomplete: "off"})
23902      */
23903     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23904     /**
23905      * @cfg {String} boxLabel The text that appears beside the checkbox
23906      */
23907     boxLabel : "",
23908     /**
23909      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23910      */  
23911     inputValue : '1',
23912     /**
23913      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23914      */
23915      valueOff: '0', // value when not checked..
23916
23917     actionMode : 'viewEl', 
23918     //
23919     // private
23920     itemCls : 'x-menu-check-item x-form-item',
23921     groupClass : 'x-menu-group-item',
23922     inputType : 'hidden',
23923     
23924     
23925     inSetChecked: false, // check that we are not calling self...
23926     
23927     inputElement: false, // real input element?
23928     basedOn: false, // ????
23929     
23930     isFormField: true, // not sure where this is needed!!!!
23931
23932     onResize : function(){
23933         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23934         if(!this.boxLabel){
23935             this.el.alignTo(this.wrap, 'c-c');
23936         }
23937     },
23938
23939     initEvents : function(){
23940         Roo.form.Checkbox.superclass.initEvents.call(this);
23941         this.el.on("click", this.onClick,  this);
23942         this.el.on("change", this.onClick,  this);
23943     },
23944
23945
23946     getResizeEl : function(){
23947         return this.wrap;
23948     },
23949
23950     getPositionEl : function(){
23951         return this.wrap;
23952     },
23953
23954     // private
23955     onRender : function(ct, position){
23956         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23957         /*
23958         if(this.inputValue !== undefined){
23959             this.el.dom.value = this.inputValue;
23960         }
23961         */
23962         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23963         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23964         var viewEl = this.wrap.createChild({ 
23965             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23966         this.viewEl = viewEl;   
23967         this.wrap.on('click', this.onClick,  this); 
23968         
23969         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23970         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23971         
23972         
23973         
23974         if(this.boxLabel){
23975             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23976         //    viewEl.on('click', this.onClick,  this); 
23977         }
23978         //if(this.checked){
23979             this.setChecked(this.checked);
23980         //}else{
23981             //this.checked = this.el.dom;
23982         //}
23983
23984     },
23985
23986     // private
23987     initValue : Roo.emptyFn,
23988
23989     /**
23990      * Returns the checked state of the checkbox.
23991      * @return {Boolean} True if checked, else false
23992      */
23993     getValue : function(){
23994         if(this.el){
23995             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23996         }
23997         return this.valueOff;
23998         
23999     },
24000
24001         // private
24002     onClick : function(){ 
24003         this.setChecked(!this.checked);
24004
24005         //if(this.el.dom.checked != this.checked){
24006         //    this.setValue(this.el.dom.checked);
24007        // }
24008     },
24009
24010     /**
24011      * Sets the checked state of the checkbox.
24012      * On is always based on a string comparison between inputValue and the param.
24013      * @param {Boolean/String} value - the value to set 
24014      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24015      */
24016     setValue : function(v,suppressEvent){
24017         
24018         
24019         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24020         //if(this.el && this.el.dom){
24021         //    this.el.dom.checked = this.checked;
24022         //    this.el.dom.defaultChecked = this.checked;
24023         //}
24024         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24025         //this.fireEvent("check", this, this.checked);
24026     },
24027     // private..
24028     setChecked : function(state,suppressEvent)
24029     {
24030         if (this.inSetChecked) {
24031             this.checked = state;
24032             return;
24033         }
24034         
24035     
24036         if(this.wrap){
24037             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24038         }
24039         this.checked = state;
24040         if(suppressEvent !== true){
24041             this.fireEvent('check', this, state);
24042         }
24043         this.inSetChecked = true;
24044         this.el.dom.value = state ? this.inputValue : this.valueOff;
24045         this.inSetChecked = false;
24046         
24047     },
24048     // handle setting of hidden value by some other method!!?!?
24049     setFromHidden: function()
24050     {
24051         if(!this.el){
24052             return;
24053         }
24054         //console.log("SET FROM HIDDEN");
24055         //alert('setFrom hidden');
24056         this.setValue(this.el.dom.value);
24057     },
24058     
24059     onDestroy : function()
24060     {
24061         if(this.viewEl){
24062             Roo.get(this.viewEl).remove();
24063         }
24064          
24065         Roo.form.Checkbox.superclass.onDestroy.call(this);
24066     }
24067
24068 });/*
24069  * Based on:
24070  * Ext JS Library 1.1.1
24071  * Copyright(c) 2006-2007, Ext JS, LLC.
24072  *
24073  * Originally Released Under LGPL - original licence link has changed is not relivant.
24074  *
24075  * Fork - LGPL
24076  * <script type="text/javascript">
24077  */
24078  
24079 /**
24080  * @class Roo.form.Radio
24081  * @extends Roo.form.Checkbox
24082  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24083  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24084  * @constructor
24085  * Creates a new Radio
24086  * @param {Object} config Configuration options
24087  */
24088 Roo.form.Radio = function(){
24089     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24090 };
24091 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24092     inputType: 'radio',
24093
24094     /**
24095      * If this radio is part of a group, it will return the selected value
24096      * @return {String}
24097      */
24098     getGroupValue : function(){
24099         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24100     }
24101 });//<script type="text/javascript">
24102
24103 /*
24104  * Ext JS Library 1.1.1
24105  * Copyright(c) 2006-2007, Ext JS, LLC.
24106  * licensing@extjs.com
24107  * 
24108  * http://www.extjs.com/license
24109  */
24110  
24111  /*
24112   * 
24113   * Known bugs:
24114   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24115   * - IE ? - no idea how much works there.
24116   * 
24117   * 
24118   * 
24119   */
24120  
24121
24122 /**
24123  * @class Ext.form.HtmlEditor
24124  * @extends Ext.form.Field
24125  * Provides a lightweight HTML Editor component.
24126  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
24127  * 
24128  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24129  * supported by this editor.</b><br/><br/>
24130  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24131  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24132  */
24133 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24134       /**
24135      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24136      */
24137     toolbars : false,
24138     /**
24139      * @cfg {String} createLinkText The default text for the create link prompt
24140      */
24141     createLinkText : 'Please enter the URL for the link:',
24142     /**
24143      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24144      */
24145     defaultLinkValue : 'http:/'+'/',
24146    
24147      /**
24148      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24149      *                        Roo.resizable.
24150      */
24151     resizable : false,
24152      /**
24153      * @cfg {Number} height (in pixels)
24154      */   
24155     height: 300,
24156    /**
24157      * @cfg {Number} width (in pixels)
24158      */   
24159     width: 500,
24160     
24161     /**
24162      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24163      * 
24164      */
24165     stylesheets: false,
24166     
24167     // id of frame..
24168     frameId: false,
24169     
24170     // private properties
24171     validationEvent : false,
24172     deferHeight: true,
24173     initialized : false,
24174     activated : false,
24175     sourceEditMode : false,
24176     onFocus : Roo.emptyFn,
24177     iframePad:3,
24178     hideMode:'offsets',
24179     
24180     defaultAutoCreate : { // modified by initCompnoent..
24181         tag: "textarea",
24182         style:"width:500px;height:300px;",
24183         autocomplete: "off"
24184     },
24185
24186     // private
24187     initComponent : function(){
24188         this.addEvents({
24189             /**
24190              * @event initialize
24191              * Fires when the editor is fully initialized (including the iframe)
24192              * @param {HtmlEditor} this
24193              */
24194             initialize: true,
24195             /**
24196              * @event activate
24197              * Fires when the editor is first receives the focus. Any insertion must wait
24198              * until after this event.
24199              * @param {HtmlEditor} this
24200              */
24201             activate: true,
24202              /**
24203              * @event beforesync
24204              * Fires before the textarea is updated with content from the editor iframe. Return false
24205              * to cancel the sync.
24206              * @param {HtmlEditor} this
24207              * @param {String} html
24208              */
24209             beforesync: true,
24210              /**
24211              * @event beforepush
24212              * Fires before the iframe editor is updated with content from the textarea. Return false
24213              * to cancel the push.
24214              * @param {HtmlEditor} this
24215              * @param {String} html
24216              */
24217             beforepush: true,
24218              /**
24219              * @event sync
24220              * Fires when the textarea is updated with content from the editor iframe.
24221              * @param {HtmlEditor} this
24222              * @param {String} html
24223              */
24224             sync: true,
24225              /**
24226              * @event push
24227              * Fires when the iframe editor is updated with content from the textarea.
24228              * @param {HtmlEditor} this
24229              * @param {String} html
24230              */
24231             push: true,
24232              /**
24233              * @event editmodechange
24234              * Fires when the editor switches edit modes
24235              * @param {HtmlEditor} this
24236              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24237              */
24238             editmodechange: true,
24239             /**
24240              * @event editorevent
24241              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24242              * @param {HtmlEditor} this
24243              */
24244             editorevent: true
24245         });
24246         this.defaultAutoCreate =  {
24247             tag: "textarea",
24248             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24249             autocomplete: "off"
24250         };
24251     },
24252
24253     /**
24254      * Protected method that will not generally be called directly. It
24255      * is called when the editor creates its toolbar. Override this method if you need to
24256      * add custom toolbar buttons.
24257      * @param {HtmlEditor} editor
24258      */
24259     createToolbar : function(editor){
24260         if (!editor.toolbars || !editor.toolbars.length) {
24261             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24262         }
24263         
24264         for (var i =0 ; i < editor.toolbars.length;i++) {
24265             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24266             editor.toolbars[i].init(editor);
24267         }
24268          
24269         
24270     },
24271
24272     /**
24273      * Protected method that will not generally be called directly. It
24274      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24275      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24276      */
24277     getDocMarkup : function(){
24278         // body styles..
24279         var st = '';
24280         if (this.stylesheets === false) {
24281             
24282             Roo.get(document.head).select('style').each(function(node) {
24283                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24284             });
24285             
24286             Roo.get(document.head).select('link').each(function(node) { 
24287                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24288             });
24289             
24290         } else if (!this.stylesheets.length) {
24291                 // simple..
24292                 st = '<style type="text/css">' +
24293                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24294                    '</style>';
24295         } else {
24296             Roo.each(this.stylesheets, function(s) {
24297                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24298             });
24299             
24300         }
24301         
24302         return '<html><head>' + st  +
24303             //<style type="text/css">' +
24304             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24305             //'</style>' +
24306             ' </head><body></body></html>';
24307     },
24308
24309     // private
24310     onRender : function(ct, position)
24311     {
24312         var _t = this;
24313         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24314         this.el.dom.style.border = '0 none';
24315         this.el.dom.setAttribute('tabIndex', -1);
24316         this.el.addClass('x-hidden');
24317         if(Roo.isIE){ // fix IE 1px bogus margin
24318             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24319         }
24320         this.wrap = this.el.wrap({
24321             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24322         });
24323         
24324         if (this.resizable) {
24325             this.resizeEl = new Roo.Resizable(this.wrap, {
24326                 pinned : true,
24327                 wrap: true,
24328                 dynamic : true,
24329                 minHeight : this.height,
24330                 height: this.height,
24331                 handles : this.resizable,
24332                 width: this.width,
24333                 listeners : {
24334                     resize : function(r, w, h) {
24335                         _t.onResize(w,h); // -something
24336                     }
24337                 }
24338             });
24339             
24340         }
24341
24342         this.frameId = Roo.id();
24343         
24344         this.createToolbar(this);
24345         
24346       
24347         
24348         var iframe = this.wrap.createChild({
24349             tag: 'iframe',
24350             id: this.frameId,
24351             name: this.frameId,
24352             frameBorder : 'no',
24353             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24354         }, this.el
24355         );
24356         
24357        // console.log(iframe);
24358         //this.wrap.dom.appendChild(iframe);
24359
24360         this.iframe = iframe.dom;
24361
24362          this.assignDocWin();
24363         
24364         this.doc.designMode = 'on';
24365        
24366         this.doc.open();
24367         this.doc.write(this.getDocMarkup());
24368         this.doc.close();
24369
24370         
24371         var task = { // must defer to wait for browser to be ready
24372             run : function(){
24373                 //console.log("run task?" + this.doc.readyState);
24374                 this.assignDocWin();
24375                 if(this.doc.body || this.doc.readyState == 'complete'){
24376                     try {
24377                         this.doc.designMode="on";
24378                     } catch (e) {
24379                         return;
24380                     }
24381                     Roo.TaskMgr.stop(task);
24382                     this.initEditor.defer(10, this);
24383                 }
24384             },
24385             interval : 10,
24386             duration:10000,
24387             scope: this
24388         };
24389         Roo.TaskMgr.start(task);
24390
24391         if(!this.width){
24392             this.setSize(this.wrap.getSize());
24393         }
24394         if (this.resizeEl) {
24395             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24396             // should trigger onReize..
24397         }
24398     },
24399
24400     // private
24401     onResize : function(w, h)
24402     {
24403         //Roo.log('resize: ' +w + ',' + h );
24404         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24405         if(this.el && this.iframe){
24406             if(typeof w == 'number'){
24407                 var aw = w - this.wrap.getFrameWidth('lr');
24408                 this.el.setWidth(this.adjustWidth('textarea', aw));
24409                 this.iframe.style.width = aw + 'px';
24410             }
24411             if(typeof h == 'number'){
24412                 var tbh = 0;
24413                 for (var i =0; i < this.toolbars.length;i++) {
24414                     // fixme - ask toolbars for heights?
24415                     tbh += this.toolbars[i].tb.el.getHeight();
24416                     if (this.toolbars[i].footer) {
24417                         tbh += this.toolbars[i].footer.el.getHeight();
24418                     }
24419                 }
24420                 
24421                 
24422                 
24423                 
24424                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24425                 ah -= 5; // knock a few pixes off for look..
24426                 this.el.setHeight(this.adjustWidth('textarea', ah));
24427                 this.iframe.style.height = ah + 'px';
24428                 if(this.doc){
24429                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24430                 }
24431             }
24432         }
24433     },
24434
24435     /**
24436      * Toggles the editor between standard and source edit mode.
24437      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24438      */
24439     toggleSourceEdit : function(sourceEditMode){
24440         
24441         this.sourceEditMode = sourceEditMode === true;
24442         
24443         if(this.sourceEditMode){
24444           
24445             this.syncValue();
24446             this.iframe.className = 'x-hidden';
24447             this.el.removeClass('x-hidden');
24448             this.el.dom.removeAttribute('tabIndex');
24449             this.el.focus();
24450         }else{
24451              
24452             this.pushValue();
24453             this.iframe.className = '';
24454             this.el.addClass('x-hidden');
24455             this.el.dom.setAttribute('tabIndex', -1);
24456             this.deferFocus();
24457         }
24458         this.setSize(this.wrap.getSize());
24459         this.fireEvent('editmodechange', this, this.sourceEditMode);
24460     },
24461
24462     // private used internally
24463     createLink : function(){
24464         var url = prompt(this.createLinkText, this.defaultLinkValue);
24465         if(url && url != 'http:/'+'/'){
24466             this.relayCmd('createlink', url);
24467         }
24468     },
24469
24470     // private (for BoxComponent)
24471     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24472
24473     // private (for BoxComponent)
24474     getResizeEl : function(){
24475         return this.wrap;
24476     },
24477
24478     // private (for BoxComponent)
24479     getPositionEl : function(){
24480         return this.wrap;
24481     },
24482
24483     // private
24484     initEvents : function(){
24485         this.originalValue = this.getValue();
24486     },
24487
24488     /**
24489      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24490      * @method
24491      */
24492     markInvalid : Roo.emptyFn,
24493     /**
24494      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24495      * @method
24496      */
24497     clearInvalid : Roo.emptyFn,
24498
24499     setValue : function(v){
24500         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24501         this.pushValue();
24502     },
24503
24504     /**
24505      * Protected method that will not generally be called directly. If you need/want
24506      * custom HTML cleanup, this is the method you should override.
24507      * @param {String} html The HTML to be cleaned
24508      * return {String} The cleaned HTML
24509      */
24510     cleanHtml : function(html){
24511         html = String(html);
24512         if(html.length > 5){
24513             if(Roo.isSafari){ // strip safari nonsense
24514                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24515             }
24516         }
24517         if(html == '&nbsp;'){
24518             html = '';
24519         }
24520         return html;
24521     },
24522
24523     /**
24524      * Protected method that will not generally be called directly. Syncs the contents
24525      * of the editor iframe with the textarea.
24526      */
24527     syncValue : function(){
24528         if(this.initialized){
24529             var bd = (this.doc.body || this.doc.documentElement);
24530             //this.cleanUpPaste();
24531             var html = bd.innerHTML;
24532             if(Roo.isSafari){
24533                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24534                 var m = bs.match(/text-align:(.*?);/i);
24535                 if(m && m[1]){
24536                     html = '<div style="'+m[0]+'">' + html + '</div>';
24537                 }
24538             }
24539             html = this.cleanHtml(html);
24540             if(this.fireEvent('beforesync', this, html) !== false){
24541                 this.el.dom.value = html;
24542                 this.fireEvent('sync', this, html);
24543             }
24544         }
24545     },
24546
24547     /**
24548      * Protected method that will not generally be called directly. Pushes the value of the textarea
24549      * into the iframe editor.
24550      */
24551     pushValue : function(){
24552         if(this.initialized){
24553             var v = this.el.dom.value;
24554             if(v.length < 1){
24555                 v = '&#160;';
24556             }
24557             
24558             if(this.fireEvent('beforepush', this, v) !== false){
24559                 var d = (this.doc.body || this.doc.documentElement);
24560                 d.innerHTML = v;
24561                 this.cleanUpPaste();
24562                 this.el.dom.value = d.innerHTML;
24563                 this.fireEvent('push', this, v);
24564             }
24565         }
24566     },
24567
24568     // private
24569     deferFocus : function(){
24570         this.focus.defer(10, this);
24571     },
24572
24573     // doc'ed in Field
24574     focus : function(){
24575         if(this.win && !this.sourceEditMode){
24576             this.win.focus();
24577         }else{
24578             this.el.focus();
24579         }
24580     },
24581     
24582     assignDocWin: function()
24583     {
24584         var iframe = this.iframe;
24585         
24586          if(Roo.isIE){
24587             this.doc = iframe.contentWindow.document;
24588             this.win = iframe.contentWindow;
24589         } else {
24590             if (!Roo.get(this.frameId)) {
24591                 return;
24592             }
24593             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24594             this.win = Roo.get(this.frameId).dom.contentWindow;
24595         }
24596     },
24597     
24598     // private
24599     initEditor : function(){
24600         //console.log("INIT EDITOR");
24601         this.assignDocWin();
24602         
24603         
24604         
24605         this.doc.designMode="on";
24606         this.doc.open();
24607         this.doc.write(this.getDocMarkup());
24608         this.doc.close();
24609         
24610         var dbody = (this.doc.body || this.doc.documentElement);
24611         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24612         // this copies styles from the containing element into thsi one..
24613         // not sure why we need all of this..
24614         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24615         ss['background-attachment'] = 'fixed'; // w3c
24616         dbody.bgProperties = 'fixed'; // ie
24617         Roo.DomHelper.applyStyles(dbody, ss);
24618         Roo.EventManager.on(this.doc, {
24619             //'mousedown': this.onEditorEvent,
24620             'mouseup': this.onEditorEvent,
24621             'dblclick': this.onEditorEvent,
24622             'click': this.onEditorEvent,
24623             'keyup': this.onEditorEvent,
24624             buffer:100,
24625             scope: this
24626         });
24627         if(Roo.isGecko){
24628             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24629         }
24630         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24631             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24632         }
24633         this.initialized = true;
24634
24635         this.fireEvent('initialize', this);
24636         this.pushValue();
24637     },
24638
24639     // private
24640     onDestroy : function(){
24641         
24642         
24643         
24644         if(this.rendered){
24645             
24646             for (var i =0; i < this.toolbars.length;i++) {
24647                 // fixme - ask toolbars for heights?
24648                 this.toolbars[i].onDestroy();
24649             }
24650             
24651             this.wrap.dom.innerHTML = '';
24652             this.wrap.remove();
24653         }
24654     },
24655
24656     // private
24657     onFirstFocus : function(){
24658         
24659         this.assignDocWin();
24660         
24661         
24662         this.activated = true;
24663         for (var i =0; i < this.toolbars.length;i++) {
24664             this.toolbars[i].onFirstFocus();
24665         }
24666        
24667         if(Roo.isGecko){ // prevent silly gecko errors
24668             this.win.focus();
24669             var s = this.win.getSelection();
24670             if(!s.focusNode || s.focusNode.nodeType != 3){
24671                 var r = s.getRangeAt(0);
24672                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24673                 r.collapse(true);
24674                 this.deferFocus();
24675             }
24676             try{
24677                 this.execCmd('useCSS', true);
24678                 this.execCmd('styleWithCSS', false);
24679             }catch(e){}
24680         }
24681         this.fireEvent('activate', this);
24682     },
24683
24684     // private
24685     adjustFont: function(btn){
24686         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24687         //if(Roo.isSafari){ // safari
24688         //    adjust *= 2;
24689        // }
24690         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24691         if(Roo.isSafari){ // safari
24692             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24693             v =  (v < 10) ? 10 : v;
24694             v =  (v > 48) ? 48 : v;
24695             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24696             
24697         }
24698         
24699         
24700         v = Math.max(1, v+adjust);
24701         
24702         this.execCmd('FontSize', v  );
24703     },
24704
24705     onEditorEvent : function(e){
24706         this.fireEvent('editorevent', this, e);
24707       //  this.updateToolbar();
24708         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24709     },
24710
24711     insertTag : function(tg)
24712     {
24713         // could be a bit smarter... -> wrap the current selected tRoo..
24714         
24715         this.execCmd("formatblock",   tg);
24716         
24717     },
24718     
24719     insertText : function(txt)
24720     {
24721         
24722         
24723         range = this.createRange();
24724         range.deleteContents();
24725                //alert(Sender.getAttribute('label'));
24726                
24727         range.insertNode(this.doc.createTextNode(txt));
24728     } ,
24729     
24730     // private
24731     relayBtnCmd : function(btn){
24732         this.relayCmd(btn.cmd);
24733     },
24734
24735     /**
24736      * Executes a Midas editor command on the editor document and performs necessary focus and
24737      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24738      * @param {String} cmd The Midas command
24739      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24740      */
24741     relayCmd : function(cmd, value){
24742         this.win.focus();
24743         this.execCmd(cmd, value);
24744         this.fireEvent('editorevent', this);
24745         //this.updateToolbar();
24746         this.deferFocus();
24747     },
24748
24749     /**
24750      * Executes a Midas editor command directly on the editor document.
24751      * For visual commands, you should use {@link #relayCmd} instead.
24752      * <b>This should only be called after the editor is initialized.</b>
24753      * @param {String} cmd The Midas command
24754      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24755      */
24756     execCmd : function(cmd, value){
24757         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24758         this.syncValue();
24759     },
24760
24761    
24762     /**
24763      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24764      * to insert tRoo.
24765      * @param {String} text
24766      */
24767     insertAtCursor : function(text){
24768         if(!this.activated){
24769             return;
24770         }
24771         if(Roo.isIE){
24772             this.win.focus();
24773             var r = this.doc.selection.createRange();
24774             if(r){
24775                 r.collapse(true);
24776                 r.pasteHTML(text);
24777                 this.syncValue();
24778                 this.deferFocus();
24779             }
24780         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24781             this.win.focus();
24782             this.execCmd('InsertHTML', text);
24783             this.deferFocus();
24784         }
24785     },
24786  // private
24787     mozKeyPress : function(e){
24788         if(e.ctrlKey){
24789             var c = e.getCharCode(), cmd;
24790           
24791             if(c > 0){
24792                 c = String.fromCharCode(c).toLowerCase();
24793                 switch(c){
24794                     case 'b':
24795                         cmd = 'bold';
24796                     break;
24797                     case 'i':
24798                         cmd = 'italic';
24799                     break;
24800                     case 'u':
24801                         cmd = 'underline';
24802                         break;
24803                     case 'v':
24804                         this.cleanUpPaste.defer(100, this);
24805                         return;
24806                     break;
24807                 }
24808                 if(cmd){
24809                     this.win.focus();
24810                     this.execCmd(cmd);
24811                     this.deferFocus();
24812                     e.preventDefault();
24813                 }
24814                 
24815             }
24816         }
24817     },
24818
24819     // private
24820     fixKeys : function(){ // load time branching for fastest keydown performance
24821         if(Roo.isIE){
24822             return function(e){
24823                 var k = e.getKey(), r;
24824                 if(k == e.TAB){
24825                     e.stopEvent();
24826                     r = this.doc.selection.createRange();
24827                     if(r){
24828                         r.collapse(true);
24829                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24830                         this.deferFocus();
24831                     }
24832                     return;
24833                 }
24834                 
24835                 if(k == e.ENTER){
24836                     r = this.doc.selection.createRange();
24837                     if(r){
24838                         var target = r.parentElement();
24839                         if(!target || target.tagName.toLowerCase() != 'li'){
24840                             e.stopEvent();
24841                             r.pasteHTML('<br />');
24842                             r.collapse(false);
24843                             r.select();
24844                         }
24845                     }
24846                 }
24847                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24848                     this.cleanUpPaste.defer(100, this);
24849                     return;
24850                 }
24851                 
24852                 
24853             };
24854         }else if(Roo.isOpera){
24855             return function(e){
24856                 var k = e.getKey();
24857                 if(k == e.TAB){
24858                     e.stopEvent();
24859                     this.win.focus();
24860                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24861                     this.deferFocus();
24862                 }
24863                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24864                     this.cleanUpPaste.defer(100, this);
24865                     return;
24866                 }
24867                 
24868             };
24869         }else if(Roo.isSafari){
24870             return function(e){
24871                 var k = e.getKey();
24872                 
24873                 if(k == e.TAB){
24874                     e.stopEvent();
24875                     this.execCmd('InsertText','\t');
24876                     this.deferFocus();
24877                     return;
24878                 }
24879                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24880                     this.cleanUpPaste.defer(100, this);
24881                     return;
24882                 }
24883                 
24884              };
24885         }
24886     }(),
24887     
24888     getAllAncestors: function()
24889     {
24890         var p = this.getSelectedNode();
24891         var a = [];
24892         if (!p) {
24893             a.push(p); // push blank onto stack..
24894             p = this.getParentElement();
24895         }
24896         
24897         
24898         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24899             a.push(p);
24900             p = p.parentNode;
24901         }
24902         a.push(this.doc.body);
24903         return a;
24904     },
24905     lastSel : false,
24906     lastSelNode : false,
24907     
24908     
24909     getSelection : function() 
24910     {
24911         this.assignDocWin();
24912         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24913     },
24914     
24915     getSelectedNode: function() 
24916     {
24917         // this may only work on Gecko!!!
24918         
24919         // should we cache this!!!!
24920         
24921         
24922         
24923          
24924         var range = this.createRange(this.getSelection()).cloneRange();
24925         
24926         if (Roo.isIE) {
24927             var parent = range.parentElement();
24928             while (true) {
24929                 var testRange = range.duplicate();
24930                 testRange.moveToElementText(parent);
24931                 if (testRange.inRange(range)) {
24932                     break;
24933                 }
24934                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24935                     break;
24936                 }
24937                 parent = parent.parentElement;
24938             }
24939             return parent;
24940         }
24941         
24942         // is ancestor a text element.
24943         var ac =  range.commonAncestorContainer;
24944         if (ac.nodeType == 3) {
24945             ac = ac.parentNode;
24946         }
24947         
24948         var ar = ac.childNodes;
24949          
24950         var nodes = [];
24951         var other_nodes = [];
24952         var has_other_nodes = false;
24953         for (var i=0;i<ar.length;i++) {
24954             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24955                 continue;
24956             }
24957             // fullly contained node.
24958             
24959             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24960                 nodes.push(ar[i]);
24961                 continue;
24962             }
24963             
24964             // probably selected..
24965             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24966                 other_nodes.push(ar[i]);
24967                 continue;
24968             }
24969             // outer..
24970             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24971                 continue;
24972             }
24973             
24974             
24975             has_other_nodes = true;
24976         }
24977         if (!nodes.length && other_nodes.length) {
24978             nodes= other_nodes;
24979         }
24980         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24981             return false;
24982         }
24983         
24984         return nodes[0];
24985     },
24986     createRange: function(sel)
24987     {
24988         // this has strange effects when using with 
24989         // top toolbar - not sure if it's a great idea.
24990         //this.editor.contentWindow.focus();
24991         if (typeof sel != "undefined") {
24992             try {
24993                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24994             } catch(e) {
24995                 return this.doc.createRange();
24996             }
24997         } else {
24998             return this.doc.createRange();
24999         }
25000     },
25001     getParentElement: function()
25002     {
25003         
25004         this.assignDocWin();
25005         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25006         
25007         var range = this.createRange(sel);
25008          
25009         try {
25010             var p = range.commonAncestorContainer;
25011             while (p.nodeType == 3) { // text node
25012                 p = p.parentNode;
25013             }
25014             return p;
25015         } catch (e) {
25016             return null;
25017         }
25018     
25019     },
25020     /***
25021      *
25022      * Range intersection.. the hard stuff...
25023      *  '-1' = before
25024      *  '0' = hits..
25025      *  '1' = after.
25026      *         [ -- selected range --- ]
25027      *   [fail]                        [fail]
25028      *
25029      *    basically..
25030      *      if end is before start or  hits it. fail.
25031      *      if start is after end or hits it fail.
25032      *
25033      *   if either hits (but other is outside. - then it's not 
25034      *   
25035      *    
25036      **/
25037     
25038     
25039     // @see http://www.thismuchiknow.co.uk/?p=64.
25040     rangeIntersectsNode : function(range, node)
25041     {
25042         var nodeRange = node.ownerDocument.createRange();
25043         try {
25044             nodeRange.selectNode(node);
25045         } catch (e) {
25046             nodeRange.selectNodeContents(node);
25047         }
25048     
25049         var rangeStartRange = range.cloneRange();
25050         rangeStartRange.collapse(true);
25051     
25052         var rangeEndRange = range.cloneRange();
25053         rangeEndRange.collapse(false);
25054     
25055         var nodeStartRange = nodeRange.cloneRange();
25056         nodeStartRange.collapse(true);
25057     
25058         var nodeEndRange = nodeRange.cloneRange();
25059         nodeEndRange.collapse(false);
25060     
25061         return rangeStartRange.compareBoundaryPoints(
25062                  Range.START_TO_START, nodeEndRange) == -1 &&
25063                rangeEndRange.compareBoundaryPoints(
25064                  Range.START_TO_START, nodeStartRange) == 1;
25065         
25066          
25067     },
25068     rangeCompareNode : function(range, node)
25069     {
25070         var nodeRange = node.ownerDocument.createRange();
25071         try {
25072             nodeRange.selectNode(node);
25073         } catch (e) {
25074             nodeRange.selectNodeContents(node);
25075         }
25076         
25077         
25078         range.collapse(true);
25079     
25080         nodeRange.collapse(true);
25081      
25082         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25083         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25084          
25085         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25086         
25087         var nodeIsBefore   =  ss == 1;
25088         var nodeIsAfter    = ee == -1;
25089         
25090         if (nodeIsBefore && nodeIsAfter)
25091             return 0; // outer
25092         if (!nodeIsBefore && nodeIsAfter)
25093             return 1; //right trailed.
25094         
25095         if (nodeIsBefore && !nodeIsAfter)
25096             return 2;  // left trailed.
25097         // fully contined.
25098         return 3;
25099     },
25100
25101     // private? - in a new class?
25102     cleanUpPaste :  function()
25103     {
25104         // cleans up the whole document..
25105          Roo.log('cleanuppaste');
25106         this.cleanUpChildren(this.doc.body);
25107         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25108         if (clean != this.doc.body.innerHTML) {
25109             this.doc.body.innerHTML = clean;
25110         }
25111         
25112     },
25113     
25114     cleanWordChars : function(input) {
25115         var he = Roo.form.HtmlEditor;
25116     
25117         var output = input;
25118         Roo.each(he.swapCodes, function(sw) { 
25119         
25120             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25121             output = output.replace(swapper, sw[1]);
25122         });
25123         return output;
25124     },
25125     
25126     
25127     cleanUpChildren : function (n)
25128     {
25129         if (!n.childNodes.length) {
25130             return;
25131         }
25132         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25133            this.cleanUpChild(n.childNodes[i]);
25134         }
25135     },
25136     
25137     
25138         
25139     
25140     cleanUpChild : function (node)
25141     {
25142         //console.log(node);
25143         if (node.nodeName == "#text") {
25144             // clean up silly Windows -- stuff?
25145             return; 
25146         }
25147         if (node.nodeName == "#comment") {
25148             node.parentNode.removeChild(node);
25149             // clean up silly Windows -- stuff?
25150             return; 
25151         }
25152         
25153         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25154             // remove node.
25155             node.parentNode.removeChild(node);
25156             return;
25157             
25158         }
25159         
25160         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25161         
25162         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25163         
25164         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25165             remove_keep_children = true;
25166         }
25167         
25168         if (remove_keep_children) {
25169             this.cleanUpChildren(node);
25170             // inserts everything just before this node...
25171             while (node.childNodes.length) {
25172                 var cn = node.childNodes[0];
25173                 node.removeChild(cn);
25174                 node.parentNode.insertBefore(cn, node);
25175             }
25176             node.parentNode.removeChild(node);
25177             return;
25178         }
25179         
25180         if (!node.attributes || !node.attributes.length) {
25181             this.cleanUpChildren(node);
25182             return;
25183         }
25184         
25185         function cleanAttr(n,v)
25186         {
25187             
25188             if (v.match(/^\./) || v.match(/^\//)) {
25189                 return;
25190             }
25191             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25192                 return;
25193             }
25194             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25195             node.removeAttribute(n);
25196             
25197         }
25198         
25199         function cleanStyle(n,v)
25200         {
25201             if (v.match(/expression/)) { //XSS?? should we even bother..
25202                 node.removeAttribute(n);
25203                 return;
25204             }
25205             
25206             
25207             var parts = v.split(/;/);
25208             Roo.each(parts, function(p) {
25209                 p = p.replace(/\s+/g,'');
25210                 if (!p.length) {
25211                     return true;
25212                 }
25213                 var l = p.split(':').shift().replace(/\s+/g,'');
25214                 
25215                 // only allow 'c whitelisted system attributes'
25216                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25217                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25218                     node.removeAttribute(n);
25219                     return false;
25220                 }
25221                 return true;
25222             });
25223             
25224             
25225         }
25226         
25227         
25228         for (var i = node.attributes.length-1; i > -1 ; i--) {
25229             var a = node.attributes[i];
25230             //console.log(a);
25231             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25232                 node.removeAttribute(a.name);
25233                 return;
25234             }
25235             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25236                 cleanAttr(a.name,a.value); // fixme..
25237                 return;
25238             }
25239             if (a.name == 'style') {
25240                 cleanStyle(a.name,a.value);
25241             }
25242             /// clean up MS crap..
25243             // tecnically this should be a list of valid class'es..
25244             
25245             
25246             if (a.name == 'class') {
25247                 if (a.value.match(/^Mso/)) {
25248                     node.className = '';
25249                 }
25250                 
25251                 if (a.value.match(/body/)) {
25252                     node.className = '';
25253                 }
25254             }
25255             
25256             // style cleanup!?
25257             // class cleanup?
25258             
25259         }
25260         
25261         
25262         this.cleanUpChildren(node);
25263         
25264         
25265     }
25266     
25267     
25268     // hide stuff that is not compatible
25269     /**
25270      * @event blur
25271      * @hide
25272      */
25273     /**
25274      * @event change
25275      * @hide
25276      */
25277     /**
25278      * @event focus
25279      * @hide
25280      */
25281     /**
25282      * @event specialkey
25283      * @hide
25284      */
25285     /**
25286      * @cfg {String} fieldClass @hide
25287      */
25288     /**
25289      * @cfg {String} focusClass @hide
25290      */
25291     /**
25292      * @cfg {String} autoCreate @hide
25293      */
25294     /**
25295      * @cfg {String} inputType @hide
25296      */
25297     /**
25298      * @cfg {String} invalidClass @hide
25299      */
25300     /**
25301      * @cfg {String} invalidText @hide
25302      */
25303     /**
25304      * @cfg {String} msgFx @hide
25305      */
25306     /**
25307      * @cfg {String} validateOnBlur @hide
25308      */
25309 });
25310
25311 Roo.form.HtmlEditor.white = [
25312         'area', 'br', 'img', 'input', 'hr', 'wbr',
25313         
25314        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25315        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25316        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25317        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25318        'table',   'ul',         'xmp', 
25319        
25320        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25321       'thead',   'tr', 
25322      
25323       'dir', 'menu', 'ol', 'ul', 'dl',
25324        
25325       'embed',  'object'
25326 ];
25327
25328
25329 Roo.form.HtmlEditor.black = [
25330     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25331         'applet', // 
25332         'base',   'basefont', 'bgsound', 'blink',  'body', 
25333         'frame',  'frameset', 'head',    'html',   'ilayer', 
25334         'iframe', 'layer',  'link',     'meta',    'object',   
25335         'script', 'style' ,'title',  'xml' // clean later..
25336 ];
25337 Roo.form.HtmlEditor.clean = [
25338     'script', 'style', 'title', 'xml'
25339 ];
25340 Roo.form.HtmlEditor.remove = [
25341     'font'
25342 ];
25343 // attributes..
25344
25345 Roo.form.HtmlEditor.ablack = [
25346     'on'
25347 ];
25348     
25349 Roo.form.HtmlEditor.aclean = [ 
25350     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25351 ];
25352
25353 // protocols..
25354 Roo.form.HtmlEditor.pwhite= [
25355         'http',  'https',  'mailto'
25356 ];
25357
25358 // white listed style attributes.
25359 Roo.form.HtmlEditor.cwhite= [
25360         'text-align',
25361         'font-size'
25362 ];
25363
25364
25365 Roo.form.HtmlEditor.swapCodes   =[ 
25366     [    8211, "--" ], 
25367     [    8212, "--" ], 
25368     [    8216,  "'" ],  
25369     [    8217, "'" ],  
25370     [    8220, '"' ],  
25371     [    8221, '"' ],  
25372     [    8226, "*" ],  
25373     [    8230, "..." ]
25374 ]; 
25375
25376     // <script type="text/javascript">
25377 /*
25378  * Based on
25379  * Ext JS Library 1.1.1
25380  * Copyright(c) 2006-2007, Ext JS, LLC.
25381  *  
25382  
25383  */
25384
25385 /**
25386  * @class Roo.form.HtmlEditorToolbar1
25387  * Basic Toolbar
25388  * 
25389  * Usage:
25390  *
25391  new Roo.form.HtmlEditor({
25392     ....
25393     toolbars : [
25394         new Roo.form.HtmlEditorToolbar1({
25395             disable : { fonts: 1 , format: 1, ..., ... , ...],
25396             btns : [ .... ]
25397         })
25398     }
25399      
25400  * 
25401  * @cfg {Object} disable List of elements to disable..
25402  * @cfg {Array} btns List of additional buttons.
25403  * 
25404  * 
25405  * NEEDS Extra CSS? 
25406  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25407  */
25408  
25409 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25410 {
25411     
25412     Roo.apply(this, config);
25413     
25414     // default disabled, based on 'good practice'..
25415     this.disable = this.disable || {};
25416     Roo.applyIf(this.disable, {
25417         fontSize : true,
25418         colors : true,
25419         specialElements : true
25420     });
25421     
25422     
25423     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25424     // dont call parent... till later.
25425 }
25426
25427 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25428     
25429     tb: false,
25430     
25431     rendered: false,
25432     
25433     editor : false,
25434     /**
25435      * @cfg {Object} disable  List of toolbar elements to disable
25436          
25437      */
25438     disable : false,
25439       /**
25440      * @cfg {Array} fontFamilies An array of available font families
25441      */
25442     fontFamilies : [
25443         'Arial',
25444         'Courier New',
25445         'Tahoma',
25446         'Times New Roman',
25447         'Verdana'
25448     ],
25449     
25450     specialChars : [
25451            "&#169;",
25452           "&#174;",     
25453           "&#8482;",    
25454           "&#163;" ,    
25455          // "&#8212;",    
25456           "&#8230;",    
25457           "&#247;" ,    
25458         //  "&#225;" ,     ?? a acute?
25459            "&#8364;"    , //Euro
25460        //   "&#8220;"    ,
25461         //  "&#8221;"    ,
25462         //  "&#8226;"    ,
25463           "&#176;"  //   , // degrees
25464
25465          // "&#233;"     , // e ecute
25466          // "&#250;"     , // u ecute?
25467     ],
25468     
25469     specialElements : [
25470         {
25471             text: "Insert Table",
25472             xtype: 'MenuItem',
25473             xns : Roo.Menu,
25474             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
25475                 
25476         },
25477         {    
25478             text: "Insert Image",
25479             xtype: 'MenuItem',
25480             xns : Roo.Menu,
25481             ihtml : '<img src="about:blank"/>'
25482             
25483         }
25484         
25485          
25486     ],
25487     
25488     
25489     inputElements : [ 
25490             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25491             "input:submit", "input:button", "select", "textarea", "label" ],
25492     formats : [
25493         ["p"] ,  
25494         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25495         ["pre"],[ "code"], 
25496         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25497     ],
25498      /**
25499      * @cfg {String} defaultFont default font to use.
25500      */
25501     defaultFont: 'tahoma',
25502    
25503     fontSelect : false,
25504     
25505     
25506     formatCombo : false,
25507     
25508     init : function(editor)
25509     {
25510         this.editor = editor;
25511         
25512         
25513         var fid = editor.frameId;
25514         var etb = this;
25515         function btn(id, toggle, handler){
25516             var xid = fid + '-'+ id ;
25517             return {
25518                 id : xid,
25519                 cmd : id,
25520                 cls : 'x-btn-icon x-edit-'+id,
25521                 enableToggle:toggle !== false,
25522                 scope: editor, // was editor...
25523                 handler:handler||editor.relayBtnCmd,
25524                 clickEvent:'mousedown',
25525                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25526                 tabIndex:-1
25527             };
25528         }
25529         
25530         
25531         
25532         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25533         this.tb = tb;
25534          // stop form submits
25535         tb.el.on('click', function(e){
25536             e.preventDefault(); // what does this do?
25537         });
25538
25539         if(!this.disable.font && !Roo.isSafari){
25540             /* why no safari for fonts
25541             editor.fontSelect = tb.el.createChild({
25542                 tag:'select',
25543                 tabIndex: -1,
25544                 cls:'x-font-select',
25545                 html: editor.createFontOptions()
25546             });
25547             editor.fontSelect.on('change', function(){
25548                 var font = editor.fontSelect.dom.value;
25549                 editor.relayCmd('fontname', font);
25550                 editor.deferFocus();
25551             }, editor);
25552             tb.add(
25553                 editor.fontSelect.dom,
25554                 '-'
25555             );
25556             */
25557         };
25558         if(!this.disable.formats){
25559             this.formatCombo = new Roo.form.ComboBox({
25560                 store: new Roo.data.SimpleStore({
25561                     id : 'tag',
25562                     fields: ['tag'],
25563                     data : this.formats // from states.js
25564                 }),
25565                 blockFocus : true,
25566                 //autoCreate : {tag: "div",  size: "20"},
25567                 displayField:'tag',
25568                 typeAhead: false,
25569                 mode: 'local',
25570                 editable : false,
25571                 triggerAction: 'all',
25572                 emptyText:'Add tag',
25573                 selectOnFocus:true,
25574                 width:135,
25575                 listeners : {
25576                     'select': function(c, r, i) {
25577                         editor.insertTag(r.get('tag'));
25578                         editor.focus();
25579                     }
25580                 }
25581
25582             });
25583             tb.addField(this.formatCombo);
25584             
25585         }
25586         
25587         if(!this.disable.format){
25588             tb.add(
25589                 btn('bold'),
25590                 btn('italic'),
25591                 btn('underline')
25592             );
25593         };
25594         if(!this.disable.fontSize){
25595             tb.add(
25596                 '-',
25597                 
25598                 
25599                 btn('increasefontsize', false, editor.adjustFont),
25600                 btn('decreasefontsize', false, editor.adjustFont)
25601             );
25602         };
25603         
25604         
25605         if(!this.disable.colors){
25606             tb.add(
25607                 '-', {
25608                     id:editor.frameId +'-forecolor',
25609                     cls:'x-btn-icon x-edit-forecolor',
25610                     clickEvent:'mousedown',
25611                     tooltip: this.buttonTips['forecolor'] || undefined,
25612                     tabIndex:-1,
25613                     menu : new Roo.menu.ColorMenu({
25614                         allowReselect: true,
25615                         focus: Roo.emptyFn,
25616                         value:'000000',
25617                         plain:true,
25618                         selectHandler: function(cp, color){
25619                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25620                             editor.deferFocus();
25621                         },
25622                         scope: editor,
25623                         clickEvent:'mousedown'
25624                     })
25625                 }, {
25626                     id:editor.frameId +'backcolor',
25627                     cls:'x-btn-icon x-edit-backcolor',
25628                     clickEvent:'mousedown',
25629                     tooltip: this.buttonTips['backcolor'] || undefined,
25630                     tabIndex:-1,
25631                     menu : new Roo.menu.ColorMenu({
25632                         focus: Roo.emptyFn,
25633                         value:'FFFFFF',
25634                         plain:true,
25635                         allowReselect: true,
25636                         selectHandler: function(cp, color){
25637                             if(Roo.isGecko){
25638                                 editor.execCmd('useCSS', false);
25639                                 editor.execCmd('hilitecolor', color);
25640                                 editor.execCmd('useCSS', true);
25641                                 editor.deferFocus();
25642                             }else{
25643                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25644                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25645                                 editor.deferFocus();
25646                             }
25647                         },
25648                         scope:editor,
25649                         clickEvent:'mousedown'
25650                     })
25651                 }
25652             );
25653         };
25654         // now add all the items...
25655         
25656
25657         if(!this.disable.alignments){
25658             tb.add(
25659                 '-',
25660                 btn('justifyleft'),
25661                 btn('justifycenter'),
25662                 btn('justifyright')
25663             );
25664         };
25665
25666         //if(!Roo.isSafari){
25667             if(!this.disable.links){
25668                 tb.add(
25669                     '-',
25670                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25671                 );
25672             };
25673
25674             if(!this.disable.lists){
25675                 tb.add(
25676                     '-',
25677                     btn('insertorderedlist'),
25678                     btn('insertunorderedlist')
25679                 );
25680             }
25681             if(!this.disable.sourceEdit){
25682                 tb.add(
25683                     '-',
25684                     btn('sourceedit', true, function(btn){
25685                         this.toggleSourceEdit(btn.pressed);
25686                     })
25687                 );
25688             }
25689         //}
25690         
25691         var smenu = { };
25692         // special menu.. - needs to be tidied up..
25693         if (!this.disable.special) {
25694             smenu = {
25695                 text: "&#169;",
25696                 cls: 'x-edit-none',
25697                 
25698                 menu : {
25699                     items : []
25700                 }
25701             };
25702             for (var i =0; i < this.specialChars.length; i++) {
25703                 smenu.menu.items.push({
25704                     
25705                     html: this.specialChars[i],
25706                     handler: function(a,b) {
25707                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25708                         
25709                     },
25710                     tabIndex:-1
25711                 });
25712             }
25713             
25714             
25715             tb.add(smenu);
25716             
25717             
25718         }
25719          
25720         if (!this.disable.specialElements) {
25721             var semenu = {
25722                 text: "Other;",
25723                 cls: 'x-edit-none',
25724                 menu : {
25725                     items : []
25726                 }
25727             };
25728             for (var i =0; i < this.specialElements.length; i++) {
25729                 semenu.menu.items.push(
25730                     Roo.apply({ 
25731                         handler: function(a,b) {
25732                             editor.insertAtCursor(this.ihtml);
25733                         }
25734                     }, this.specialElements[i])
25735                 );
25736                     
25737             }
25738             
25739             tb.add(semenu);
25740             
25741             
25742         }
25743          
25744         
25745         if (this.btns) {
25746             for(var i =0; i< this.btns.length;i++) {
25747                 var b = this.btns[i];
25748                 b.cls =  'x-edit-none';
25749                 b.scope = editor;
25750                 tb.add(b);
25751             }
25752         
25753         }
25754         
25755         
25756         
25757         // disable everything...
25758         
25759         this.tb.items.each(function(item){
25760            if(item.id != editor.frameId+ '-sourceedit'){
25761                 item.disable();
25762             }
25763         });
25764         this.rendered = true;
25765         
25766         // the all the btns;
25767         editor.on('editorevent', this.updateToolbar, this);
25768         // other toolbars need to implement this..
25769         //editor.on('editmodechange', this.updateToolbar, this);
25770     },
25771     
25772     
25773     
25774     /**
25775      * Protected method that will not generally be called directly. It triggers
25776      * a toolbar update by reading the markup state of the current selection in the editor.
25777      */
25778     updateToolbar: function(){
25779
25780         if(!this.editor.activated){
25781             this.editor.onFirstFocus();
25782             return;
25783         }
25784
25785         var btns = this.tb.items.map, 
25786             doc = this.editor.doc,
25787             frameId = this.editor.frameId;
25788
25789         if(!this.disable.font && !Roo.isSafari){
25790             /*
25791             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25792             if(name != this.fontSelect.dom.value){
25793                 this.fontSelect.dom.value = name;
25794             }
25795             */
25796         }
25797         if(!this.disable.format){
25798             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25799             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25800             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25801         }
25802         if(!this.disable.alignments){
25803             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25804             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25805             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25806         }
25807         if(!Roo.isSafari && !this.disable.lists){
25808             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25809             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25810         }
25811         
25812         var ans = this.editor.getAllAncestors();
25813         if (this.formatCombo) {
25814             
25815             
25816             var store = this.formatCombo.store;
25817             this.formatCombo.setValue("");
25818             for (var i =0; i < ans.length;i++) {
25819                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25820                     // select it..
25821                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25822                     break;
25823                 }
25824             }
25825         }
25826         
25827         
25828         
25829         // hides menus... - so this cant be on a menu...
25830         Roo.menu.MenuMgr.hideAll();
25831
25832         //this.editorsyncValue();
25833     },
25834    
25835     
25836     createFontOptions : function(){
25837         var buf = [], fs = this.fontFamilies, ff, lc;
25838         for(var i = 0, len = fs.length; i< len; i++){
25839             ff = fs[i];
25840             lc = ff.toLowerCase();
25841             buf.push(
25842                 '<option value="',lc,'" style="font-family:',ff,';"',
25843                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25844                     ff,
25845                 '</option>'
25846             );
25847         }
25848         return buf.join('');
25849     },
25850     
25851     toggleSourceEdit : function(sourceEditMode){
25852         if(sourceEditMode === undefined){
25853             sourceEditMode = !this.sourceEditMode;
25854         }
25855         this.sourceEditMode = sourceEditMode === true;
25856         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25857         // just toggle the button?
25858         if(btn.pressed !== this.editor.sourceEditMode){
25859             btn.toggle(this.editor.sourceEditMode);
25860             return;
25861         }
25862         
25863         if(this.sourceEditMode){
25864             this.tb.items.each(function(item){
25865                 if(item.cmd != 'sourceedit'){
25866                     item.disable();
25867                 }
25868             });
25869           
25870         }else{
25871             if(this.initialized){
25872                 this.tb.items.each(function(item){
25873                     item.enable();
25874                 });
25875             }
25876             
25877         }
25878         // tell the editor that it's been pressed..
25879         this.editor.toggleSourceEdit(sourceEditMode);
25880        
25881     },
25882      /**
25883      * Object collection of toolbar tooltips for the buttons in the editor. The key
25884      * is the command id associated with that button and the value is a valid QuickTips object.
25885      * For example:
25886 <pre><code>
25887 {
25888     bold : {
25889         title: 'Bold (Ctrl+B)',
25890         text: 'Make the selected text bold.',
25891         cls: 'x-html-editor-tip'
25892     },
25893     italic : {
25894         title: 'Italic (Ctrl+I)',
25895         text: 'Make the selected text italic.',
25896         cls: 'x-html-editor-tip'
25897     },
25898     ...
25899 </code></pre>
25900     * @type Object
25901      */
25902     buttonTips : {
25903         bold : {
25904             title: 'Bold (Ctrl+B)',
25905             text: 'Make the selected text bold.',
25906             cls: 'x-html-editor-tip'
25907         },
25908         italic : {
25909             title: 'Italic (Ctrl+I)',
25910             text: 'Make the selected text italic.',
25911             cls: 'x-html-editor-tip'
25912         },
25913         underline : {
25914             title: 'Underline (Ctrl+U)',
25915             text: 'Underline the selected text.',
25916             cls: 'x-html-editor-tip'
25917         },
25918         increasefontsize : {
25919             title: 'Grow Text',
25920             text: 'Increase the font size.',
25921             cls: 'x-html-editor-tip'
25922         },
25923         decreasefontsize : {
25924             title: 'Shrink Text',
25925             text: 'Decrease the font size.',
25926             cls: 'x-html-editor-tip'
25927         },
25928         backcolor : {
25929             title: 'Text Highlight Color',
25930             text: 'Change the background color of the selected text.',
25931             cls: 'x-html-editor-tip'
25932         },
25933         forecolor : {
25934             title: 'Font Color',
25935             text: 'Change the color of the selected text.',
25936             cls: 'x-html-editor-tip'
25937         },
25938         justifyleft : {
25939             title: 'Align Text Left',
25940             text: 'Align text to the left.',
25941             cls: 'x-html-editor-tip'
25942         },
25943         justifycenter : {
25944             title: 'Center Text',
25945             text: 'Center text in the editor.',
25946             cls: 'x-html-editor-tip'
25947         },
25948         justifyright : {
25949             title: 'Align Text Right',
25950             text: 'Align text to the right.',
25951             cls: 'x-html-editor-tip'
25952         },
25953         insertunorderedlist : {
25954             title: 'Bullet List',
25955             text: 'Start a bulleted list.',
25956             cls: 'x-html-editor-tip'
25957         },
25958         insertorderedlist : {
25959             title: 'Numbered List',
25960             text: 'Start a numbered list.',
25961             cls: 'x-html-editor-tip'
25962         },
25963         createlink : {
25964             title: 'Hyperlink',
25965             text: 'Make the selected text a hyperlink.',
25966             cls: 'x-html-editor-tip'
25967         },
25968         sourceedit : {
25969             title: 'Source Edit',
25970             text: 'Switch to source editing mode.',
25971             cls: 'x-html-editor-tip'
25972         }
25973     },
25974     // private
25975     onDestroy : function(){
25976         if(this.rendered){
25977             
25978             this.tb.items.each(function(item){
25979                 if(item.menu){
25980                     item.menu.removeAll();
25981                     if(item.menu.el){
25982                         item.menu.el.destroy();
25983                     }
25984                 }
25985                 item.destroy();
25986             });
25987              
25988         }
25989     },
25990     onFirstFocus: function() {
25991         this.tb.items.each(function(item){
25992            item.enable();
25993         });
25994     }
25995 });
25996
25997
25998
25999
26000 // <script type="text/javascript">
26001 /*
26002  * Based on
26003  * Ext JS Library 1.1.1
26004  * Copyright(c) 2006-2007, Ext JS, LLC.
26005  *  
26006  
26007  */
26008
26009  
26010 /**
26011  * @class Roo.form.HtmlEditor.ToolbarContext
26012  * Context Toolbar
26013  * 
26014  * Usage:
26015  *
26016  new Roo.form.HtmlEditor({
26017     ....
26018     toolbars : [
26019         { xtype: 'ToolbarStandard', styles : {} }
26020         { xtype: 'ToolbarContext', disable : {} }
26021     ]
26022 })
26023
26024      
26025  * 
26026  * @config : {Object} disable List of elements to disable.. (not done yet.)
26027  * @config : {Object} styles  Map of styles available.
26028  * 
26029  */
26030
26031 Roo.form.HtmlEditor.ToolbarContext = function(config)
26032 {
26033     
26034     Roo.apply(this, config);
26035     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26036     // dont call parent... till later.
26037     this.styles = this.styles || {};
26038 }
26039 Roo.form.HtmlEditor.ToolbarContext.types = {
26040     'IMG' : {
26041         width : {
26042             title: "Width",
26043             width: 40
26044         },
26045         height:  {
26046             title: "Height",
26047             width: 40
26048         },
26049         align: {
26050             title: "Align",
26051             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26052             width : 80
26053             
26054         },
26055         border: {
26056             title: "Border",
26057             width: 40
26058         },
26059         alt: {
26060             title: "Alt",
26061             width: 120
26062         },
26063         src : {
26064             title: "Src",
26065             width: 220
26066         }
26067         
26068     },
26069     'A' : {
26070         name : {
26071             title: "Name",
26072             width: 50
26073         },
26074         href:  {
26075             title: "Href",
26076             width: 220
26077         } // border?
26078         
26079     },
26080     'TABLE' : {
26081         rows : {
26082             title: "Rows",
26083             width: 20
26084         },
26085         cols : {
26086             title: "Cols",
26087             width: 20
26088         },
26089         width : {
26090             title: "Width",
26091             width: 40
26092         },
26093         height : {
26094             title: "Height",
26095             width: 40
26096         },
26097         border : {
26098             title: "Border",
26099             width: 20
26100         }
26101     },
26102     'TD' : {
26103         width : {
26104             title: "Width",
26105             width: 40
26106         },
26107         height : {
26108             title: "Height",
26109             width: 40
26110         },   
26111         align: {
26112             title: "Align",
26113             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26114             width: 80
26115         },
26116         valign: {
26117             title: "Valign",
26118             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26119             width: 80
26120         },
26121         colspan: {
26122             title: "Colspan",
26123             width: 20
26124             
26125         }
26126     },
26127     'INPUT' : {
26128         name : {
26129             title: "name",
26130             width: 120
26131         },
26132         value : {
26133             title: "Value",
26134             width: 120
26135         },
26136         width : {
26137             title: "Width",
26138             width: 40
26139         }
26140     },
26141     'LABEL' : {
26142         'for' : {
26143             title: "For",
26144             width: 120
26145         }
26146     },
26147     'TEXTAREA' : {
26148           name : {
26149             title: "name",
26150             width: 120
26151         },
26152         rows : {
26153             title: "Rows",
26154             width: 20
26155         },
26156         cols : {
26157             title: "Cols",
26158             width: 20
26159         }
26160     },
26161     'SELECT' : {
26162         name : {
26163             title: "name",
26164             width: 120
26165         },
26166         selectoptions : {
26167             title: "Options",
26168             width: 200
26169         }
26170     },
26171     
26172     // should we really allow this??
26173     // should this just be 
26174     'BODY' : {
26175         title : {
26176             title: "title",
26177             width: 200,
26178             disabled : true
26179         }
26180     },
26181     '*' : {
26182         // empty..
26183     }
26184 };
26185
26186
26187
26188 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26189     
26190     tb: false,
26191     
26192     rendered: false,
26193     
26194     editor : false,
26195     /**
26196      * @cfg {Object} disable  List of toolbar elements to disable
26197          
26198      */
26199     disable : false,
26200     /**
26201      * @cfg {Object} styles List of styles 
26202      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26203      *
26204      * These must be defined in the page, so they get rendered correctly..
26205      * .headline { }
26206      * TD.underline { }
26207      * 
26208      */
26209     styles : false,
26210     
26211     
26212     
26213     toolbars : false,
26214     
26215     init : function(editor)
26216     {
26217         this.editor = editor;
26218         
26219         
26220         var fid = editor.frameId;
26221         var etb = this;
26222         function btn(id, toggle, handler){
26223             var xid = fid + '-'+ id ;
26224             return {
26225                 id : xid,
26226                 cmd : id,
26227                 cls : 'x-btn-icon x-edit-'+id,
26228                 enableToggle:toggle !== false,
26229                 scope: editor, // was editor...
26230                 handler:handler||editor.relayBtnCmd,
26231                 clickEvent:'mousedown',
26232                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26233                 tabIndex:-1
26234             };
26235         }
26236         // create a new element.
26237         var wdiv = editor.wrap.createChild({
26238                 tag: 'div'
26239             }, editor.wrap.dom.firstChild.nextSibling, true);
26240         
26241         // can we do this more than once??
26242         
26243          // stop form submits
26244       
26245  
26246         // disable everything...
26247         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26248         this.toolbars = {};
26249            
26250         for (var i in  ty) {
26251           
26252             this.toolbars[i] = this.buildToolbar(ty[i],i);
26253         }
26254         this.tb = this.toolbars.BODY;
26255         this.tb.el.show();
26256         this.buildFooter();
26257         this.footer.show();
26258          
26259         this.rendered = true;
26260         
26261         // the all the btns;
26262         editor.on('editorevent', this.updateToolbar, this);
26263         // other toolbars need to implement this..
26264         //editor.on('editmodechange', this.updateToolbar, this);
26265     },
26266     
26267     
26268     
26269     /**
26270      * Protected method that will not generally be called directly. It triggers
26271      * a toolbar update by reading the markup state of the current selection in the editor.
26272      */
26273     updateToolbar: function(ignore_a,ignore_b,sel){
26274
26275         
26276         if(!this.editor.activated){
26277              this.editor.onFirstFocus();
26278             return;
26279         }
26280         var updateFooter = sel ? false : true;
26281         
26282         
26283         var ans = this.editor.getAllAncestors();
26284         
26285         // pick
26286         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26287         
26288         if (!sel) { 
26289             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26290             sel = sel ? sel : this.editor.doc.body;
26291             sel = sel.tagName.length ? sel : this.editor.doc.body;
26292             
26293         }
26294         // pick a menu that exists..
26295         var tn = sel.tagName.toUpperCase();
26296         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26297         
26298         tn = sel.tagName.toUpperCase();
26299         
26300         var lastSel = this.tb.selectedNode
26301         
26302         this.tb.selectedNode = sel;
26303         
26304         // if current menu does not match..
26305         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26306                 
26307             this.tb.el.hide();
26308             ///console.log("show: " + tn);
26309             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26310             this.tb.el.show();
26311             // update name
26312             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26313             
26314             
26315             // update attributes
26316             if (this.tb.fields) {
26317                 this.tb.fields.each(function(e) {
26318                    e.setValue(sel.getAttribute(e.name));
26319                 });
26320             }
26321             
26322             // update styles
26323             var st = this.tb.fields.item(0);
26324             st.store.removeAll();
26325             var cn = sel.className.split(/\s+/);
26326             
26327             var avs = [];
26328             if (this.styles['*']) {
26329                 
26330                 Roo.each(this.styles['*'], function(v) {
26331                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26332                 });
26333             }
26334             if (this.styles[tn]) { 
26335                 Roo.each(this.styles[tn], function(v) {
26336                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26337                 });
26338             }
26339             
26340             st.store.loadData(avs);
26341             st.collapse();
26342             st.setValue(cn);
26343             
26344             // flag our selected Node.
26345             this.tb.selectedNode = sel;
26346            
26347            
26348             Roo.menu.MenuMgr.hideAll();
26349
26350         }
26351         
26352         if (!updateFooter) {
26353             return;
26354         }
26355         // update the footer
26356         //
26357         var html = '';
26358         
26359         this.footerEls = ans.reverse();
26360         Roo.each(this.footerEls, function(a,i) {
26361             if (!a) { return; }
26362             html += html.length ? ' &gt; '  :  '';
26363             
26364             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26365             
26366         });
26367        
26368         // 
26369         var sz = this.footDisp.up('td').getSize();
26370         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26371         this.footDisp.dom.style.marginLeft = '5px';
26372         
26373         this.footDisp.dom.style.overflow = 'hidden';
26374         
26375         this.footDisp.dom.innerHTML = html;
26376             
26377         //this.editorsyncValue();
26378     },
26379    
26380        
26381     // private
26382     onDestroy : function(){
26383         if(this.rendered){
26384             
26385             this.tb.items.each(function(item){
26386                 if(item.menu){
26387                     item.menu.removeAll();
26388                     if(item.menu.el){
26389                         item.menu.el.destroy();
26390                     }
26391                 }
26392                 item.destroy();
26393             });
26394              
26395         }
26396     },
26397     onFirstFocus: function() {
26398         // need to do this for all the toolbars..
26399         this.tb.items.each(function(item){
26400            item.enable();
26401         });
26402     },
26403     buildToolbar: function(tlist, nm)
26404     {
26405         var editor = this.editor;
26406          // create a new element.
26407         var wdiv = editor.wrap.createChild({
26408                 tag: 'div'
26409             }, editor.wrap.dom.firstChild.nextSibling, true);
26410         
26411        
26412         var tb = new Roo.Toolbar(wdiv);
26413         // add the name..
26414         
26415         tb.add(nm+ ":&nbsp;");
26416         
26417         // styles...
26418         if (this.styles) {
26419             
26420             // this needs a multi-select checkbox...
26421             tb.addField( new Roo.form.ComboBox({
26422                 store: new Roo.data.SimpleStore({
26423                     id : 'val',
26424                     fields: ['val', 'selected'],
26425                     data : [] 
26426                 }),
26427                 name : 'className',
26428                 displayField:'val',
26429                 typeAhead: false,
26430                 mode: 'local',
26431                 editable : false,
26432                 triggerAction: 'all',
26433                 emptyText:'Select Style',
26434                 selectOnFocus:true,
26435                 width: 130,
26436                 listeners : {
26437                     'select': function(c, r, i) {
26438                         // initial support only for on class per el..
26439                         tb.selectedNode.className =  r ? r.get('val') : '';
26440                     }
26441                 }
26442     
26443             }));
26444         }
26445             
26446         
26447         
26448         for (var i in tlist) {
26449             
26450             var item = tlist[i];
26451             tb.add(item.title + ":&nbsp;");
26452             
26453             
26454             
26455             
26456             if (item.opts) {
26457                 // opts == pulldown..
26458                 tb.addField( new Roo.form.ComboBox({
26459                     store: new Roo.data.SimpleStore({
26460                         id : 'val',
26461                         fields: ['val'],
26462                         data : item.opts  
26463                     }),
26464                     name : i,
26465                     displayField:'val',
26466                     typeAhead: false,
26467                     mode: 'local',
26468                     editable : false,
26469                     triggerAction: 'all',
26470                     emptyText:'Select',
26471                     selectOnFocus:true,
26472                     width: item.width ? item.width  : 130,
26473                     listeners : {
26474                         'select': function(c, r, i) {
26475                             tb.selectedNode.setAttribute(c.name, r.get('val'));
26476                         }
26477                     }
26478
26479                 }));
26480                 continue;
26481                     
26482                  
26483                 
26484                 tb.addField( new Roo.form.TextField({
26485                     name: i,
26486                     width: 100,
26487                     //allowBlank:false,
26488                     value: ''
26489                 }));
26490                 continue;
26491             }
26492             tb.addField( new Roo.form.TextField({
26493                 name: i,
26494                 width: item.width,
26495                 //allowBlank:true,
26496                 value: '',
26497                 listeners: {
26498                     'change' : function(f, nv, ov) {
26499                         tb.selectedNode.setAttribute(f.name, nv);
26500                     }
26501                 }
26502             }));
26503              
26504         }
26505         tb.el.on('click', function(e){
26506             e.preventDefault(); // what does this do?
26507         });
26508         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
26509         tb.el.hide();
26510         tb.name = nm;
26511         // dont need to disable them... as they will get hidden
26512         return tb;
26513          
26514         
26515     },
26516     buildFooter : function()
26517     {
26518         
26519         var fel = this.editor.wrap.createChild();
26520         this.footer = new Roo.Toolbar(fel);
26521         // toolbar has scrolly on left / right?
26522         var footDisp= new Roo.Toolbar.Fill();
26523         var _t = this;
26524         this.footer.add(
26525             {
26526                 text : '&lt;',
26527                 xtype: 'Button',
26528                 handler : function() {
26529                     _t.footDisp.scrollTo('left',0,true)
26530                 }
26531             }
26532         );
26533         this.footer.add( footDisp );
26534         this.footer.add( 
26535             {
26536                 text : '&gt;',
26537                 xtype: 'Button',
26538                 handler : function() {
26539                     // no animation..
26540                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
26541                 }
26542             }
26543         );
26544         var fel = Roo.get(footDisp.el);
26545         fel.addClass('x-editor-context');
26546         this.footDispWrap = fel; 
26547         this.footDispWrap.overflow  = 'hidden';
26548         
26549         this.footDisp = fel.createChild();
26550         this.footDispWrap.on('click', this.onContextClick, this)
26551         
26552         
26553     },
26554     onContextClick : function (ev,dom)
26555     {
26556         ev.preventDefault();
26557         var  cn = dom.className;
26558         Roo.log(cn);
26559         if (!cn.match(/x-ed-loc-/)) {
26560             return;
26561         }
26562         var n = cn.split('-').pop();
26563         var ans = this.footerEls;
26564         var sel = ans[n];
26565         
26566          // pick
26567         var range = this.editor.createRange();
26568         
26569         range.selectNodeContents(sel);
26570         //range.selectNode(sel);
26571         
26572         
26573         var selection = this.editor.getSelection();
26574         selection.removeAllRanges();
26575         selection.addRange(range);
26576         
26577         
26578         
26579         this.updateToolbar(null, null, sel);
26580         
26581         
26582     }
26583     
26584     
26585     
26586     
26587     
26588 });
26589
26590
26591
26592
26593
26594 /*
26595  * Based on:
26596  * Ext JS Library 1.1.1
26597  * Copyright(c) 2006-2007, Ext JS, LLC.
26598  *
26599  * Originally Released Under LGPL - original licence link has changed is not relivant.
26600  *
26601  * Fork - LGPL
26602  * <script type="text/javascript">
26603  */
26604  
26605 /**
26606  * @class Roo.form.BasicForm
26607  * @extends Roo.util.Observable
26608  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26609  * @constructor
26610  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26611  * @param {Object} config Configuration options
26612  */
26613 Roo.form.BasicForm = function(el, config){
26614     this.allItems = [];
26615     this.childForms = [];
26616     Roo.apply(this, config);
26617     /*
26618      * The Roo.form.Field items in this form.
26619      * @type MixedCollection
26620      */
26621      
26622      
26623     this.items = new Roo.util.MixedCollection(false, function(o){
26624         return o.id || (o.id = Roo.id());
26625     });
26626     this.addEvents({
26627         /**
26628          * @event beforeaction
26629          * Fires before any action is performed. Return false to cancel the action.
26630          * @param {Form} this
26631          * @param {Action} action The action to be performed
26632          */
26633         beforeaction: true,
26634         /**
26635          * @event actionfailed
26636          * Fires when an action fails.
26637          * @param {Form} this
26638          * @param {Action} action The action that failed
26639          */
26640         actionfailed : true,
26641         /**
26642          * @event actioncomplete
26643          * Fires when an action is completed.
26644          * @param {Form} this
26645          * @param {Action} action The action that completed
26646          */
26647         actioncomplete : true
26648     });
26649     if(el){
26650         this.initEl(el);
26651     }
26652     Roo.form.BasicForm.superclass.constructor.call(this);
26653 };
26654
26655 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26656     /**
26657      * @cfg {String} method
26658      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26659      */
26660     /**
26661      * @cfg {DataReader} reader
26662      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26663      * This is optional as there is built-in support for processing JSON.
26664      */
26665     /**
26666      * @cfg {DataReader} errorReader
26667      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26668      * This is completely optional as there is built-in support for processing JSON.
26669      */
26670     /**
26671      * @cfg {String} url
26672      * The URL to use for form actions if one isn't supplied in the action options.
26673      */
26674     /**
26675      * @cfg {Boolean} fileUpload
26676      * Set to true if this form is a file upload.
26677      */
26678      
26679     /**
26680      * @cfg {Object} baseParams
26681      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26682      */
26683      /**
26684      
26685     /**
26686      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26687      */
26688     timeout: 30,
26689
26690     // private
26691     activeAction : null,
26692
26693     /**
26694      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26695      * or setValues() data instead of when the form was first created.
26696      */
26697     trackResetOnLoad : false,
26698     
26699     
26700     /**
26701      * childForms - used for multi-tab forms
26702      * @type {Array}
26703      */
26704     childForms : false,
26705     
26706     /**
26707      * allItems - full list of fields.
26708      * @type {Array}
26709      */
26710     allItems : false,
26711     
26712     /**
26713      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26714      * element by passing it or its id or mask the form itself by passing in true.
26715      * @type Mixed
26716      */
26717     waitMsgTarget : false,
26718
26719     // private
26720     initEl : function(el){
26721         this.el = Roo.get(el);
26722         this.id = this.el.id || Roo.id();
26723         this.el.on('submit', this.onSubmit, this);
26724         this.el.addClass('x-form');
26725     },
26726
26727     // private
26728     onSubmit : function(e){
26729         e.stopEvent();
26730     },
26731
26732     /**
26733      * Returns true if client-side validation on the form is successful.
26734      * @return Boolean
26735      */
26736     isValid : function(){
26737         var valid = true;
26738         this.items.each(function(f){
26739            if(!f.validate()){
26740                valid = false;
26741            }
26742         });
26743         return valid;
26744     },
26745
26746     /**
26747      * Returns true if any fields in this form have changed since their original load.
26748      * @return Boolean
26749      */
26750     isDirty : function(){
26751         var dirty = false;
26752         this.items.each(function(f){
26753            if(f.isDirty()){
26754                dirty = true;
26755                return false;
26756            }
26757         });
26758         return dirty;
26759     },
26760
26761     /**
26762      * Performs a predefined action (submit or load) or custom actions you define on this form.
26763      * @param {String} actionName The name of the action type
26764      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26765      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26766      * accept other config options):
26767      * <pre>
26768 Property          Type             Description
26769 ----------------  ---------------  ----------------------------------------------------------------------------------
26770 url               String           The url for the action (defaults to the form's url)
26771 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26772 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26773 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26774                                    validate the form on the client (defaults to false)
26775      * </pre>
26776      * @return {BasicForm} this
26777      */
26778     doAction : function(action, options){
26779         if(typeof action == 'string'){
26780             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26781         }
26782         if(this.fireEvent('beforeaction', this, action) !== false){
26783             this.beforeAction(action);
26784             action.run.defer(100, action);
26785         }
26786         return this;
26787     },
26788
26789     /**
26790      * Shortcut to do a submit action.
26791      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26792      * @return {BasicForm} this
26793      */
26794     submit : function(options){
26795         this.doAction('submit', options);
26796         return this;
26797     },
26798
26799     /**
26800      * Shortcut to do a load action.
26801      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26802      * @return {BasicForm} this
26803      */
26804     load : function(options){
26805         this.doAction('load', options);
26806         return this;
26807     },
26808
26809     /**
26810      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26811      * @param {Record} record The record to edit
26812      * @return {BasicForm} this
26813      */
26814     updateRecord : function(record){
26815         record.beginEdit();
26816         var fs = record.fields;
26817         fs.each(function(f){
26818             var field = this.findField(f.name);
26819             if(field){
26820                 record.set(f.name, field.getValue());
26821             }
26822         }, this);
26823         record.endEdit();
26824         return this;
26825     },
26826
26827     /**
26828      * Loads an Roo.data.Record into this form.
26829      * @param {Record} record The record to load
26830      * @return {BasicForm} this
26831      */
26832     loadRecord : function(record){
26833         this.setValues(record.data);
26834         return this;
26835     },
26836
26837     // private
26838     beforeAction : function(action){
26839         var o = action.options;
26840         
26841        
26842         if(this.waitMsgTarget === true){
26843             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26844         }else if(this.waitMsgTarget){
26845             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26846             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26847         }else {
26848             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26849         }
26850          
26851     },
26852
26853     // private
26854     afterAction : function(action, success){
26855         this.activeAction = null;
26856         var o = action.options;
26857         
26858         if(this.waitMsgTarget === true){
26859             this.el.unmask();
26860         }else if(this.waitMsgTarget){
26861             this.waitMsgTarget.unmask();
26862         }else{
26863             Roo.MessageBox.updateProgress(1);
26864             Roo.MessageBox.hide();
26865         }
26866          
26867         if(success){
26868             if(o.reset){
26869                 this.reset();
26870             }
26871             Roo.callback(o.success, o.scope, [this, action]);
26872             this.fireEvent('actioncomplete', this, action);
26873             
26874         }else{
26875             Roo.callback(o.failure, o.scope, [this, action]);
26876             // show an error message if no failed handler is set..
26877             if (!this.hasListener('actionfailed')) {
26878                 Roo.MessageBox.alert("Error",
26879                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
26880                         action.result.errorMsg :
26881                         "Saving Failed, please check your entries"
26882                 );
26883             }
26884             
26885             this.fireEvent('actionfailed', this, action);
26886         }
26887         
26888     },
26889
26890     /**
26891      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26892      * @param {String} id The value to search for
26893      * @return Field
26894      */
26895     findField : function(id){
26896         var field = this.items.get(id);
26897         if(!field){
26898             this.items.each(function(f){
26899                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26900                     field = f;
26901                     return false;
26902                 }
26903             });
26904         }
26905         return field || null;
26906     },
26907
26908     /**
26909      * Add a secondary form to this one, 
26910      * Used to provide tabbed forms. One form is primary, with hidden values 
26911      * which mirror the elements from the other forms.
26912      * 
26913      * @param {Roo.form.Form} form to add.
26914      * 
26915      */
26916     addForm : function(form)
26917     {
26918        
26919         if (this.childForms.indexOf(form) > -1) {
26920             // already added..
26921             return;
26922         }
26923         this.childForms.push(form);
26924         var n = '';
26925         Roo.each(form.allItems, function (fe) {
26926             
26927             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26928             if (this.findField(n)) { // already added..
26929                 return;
26930             }
26931             var add = new Roo.form.Hidden({
26932                 name : n
26933             });
26934             add.render(this.el);
26935             
26936             this.add( add );
26937         }, this);
26938         
26939     },
26940     /**
26941      * Mark fields in this form invalid in bulk.
26942      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26943      * @return {BasicForm} this
26944      */
26945     markInvalid : function(errors){
26946         if(errors instanceof Array){
26947             for(var i = 0, len = errors.length; i < len; i++){
26948                 var fieldError = errors[i];
26949                 var f = this.findField(fieldError.id);
26950                 if(f){
26951                     f.markInvalid(fieldError.msg);
26952                 }
26953             }
26954         }else{
26955             var field, id;
26956             for(id in errors){
26957                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26958                     field.markInvalid(errors[id]);
26959                 }
26960             }
26961         }
26962         Roo.each(this.childForms || [], function (f) {
26963             f.markInvalid(errors);
26964         });
26965         
26966         return this;
26967     },
26968
26969     /**
26970      * Set values for fields in this form in bulk.
26971      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26972      * @return {BasicForm} this
26973      */
26974     setValues : function(values){
26975         if(values instanceof Array){ // array of objects
26976             for(var i = 0, len = values.length; i < len; i++){
26977                 var v = values[i];
26978                 var f = this.findField(v.id);
26979                 if(f){
26980                     f.setValue(v.value);
26981                     if(this.trackResetOnLoad){
26982                         f.originalValue = f.getValue();
26983                     }
26984                 }
26985             }
26986         }else{ // object hash
26987             var field, id;
26988             for(id in values){
26989                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26990                     
26991                     if (field.setFromData && 
26992                         field.valueField && 
26993                         field.displayField &&
26994                         // combos' with local stores can 
26995                         // be queried via setValue()
26996                         // to set their value..
26997                         (field.store && !field.store.isLocal)
26998                         ) {
26999                         // it's a combo
27000                         var sd = { };
27001                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27002                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27003                         field.setFromData(sd);
27004                         
27005                     } else {
27006                         field.setValue(values[id]);
27007                     }
27008                     
27009                     
27010                     if(this.trackResetOnLoad){
27011                         field.originalValue = field.getValue();
27012                     }
27013                 }
27014             }
27015         }
27016          
27017         Roo.each(this.childForms || [], function (f) {
27018             f.setValues(values);
27019         });
27020                 
27021         return this;
27022     },
27023
27024     /**
27025      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27026      * they are returned as an array.
27027      * @param {Boolean} asString
27028      * @return {Object}
27029      */
27030     getValues : function(asString){
27031         if (this.childForms) {
27032             // copy values from the child forms
27033             Roo.each(this.childForms, function (f) {
27034                 this.setValues(f.getValues());
27035             }, this);
27036         }
27037         
27038         
27039         
27040         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27041         if(asString === true){
27042             return fs;
27043         }
27044         return Roo.urlDecode(fs);
27045     },
27046     
27047     /**
27048      * Returns the fields in this form as an object with key/value pairs. 
27049      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27050      * @return {Object}
27051      */
27052     getFieldValues : function(with_hidden)
27053     {
27054         if (this.childForms) {
27055             // copy values from the child forms
27056             // should this call getFieldValues - probably not as we do not currently copy
27057             // hidden fields when we generate..
27058             Roo.each(this.childForms, function (f) {
27059                 this.setValues(f.getValues());
27060             }, this);
27061         }
27062         
27063         var ret = {};
27064         this.items.each(function(f){
27065             if (!f.getName()) {
27066                 return;
27067             }
27068             var v = f.getValue();
27069             // not sure if this supported any more..
27070             if ((typeof(v) == 'object') && f.getRawValue) {
27071                 v = f.getRawValue() ; // dates..
27072             }
27073             // combo boxes where name != hiddenName...
27074             if (f.name != f.getName()) {
27075                 ret[f.name] = f.getRawValue();
27076             }
27077             ret[f.getName()] = v;
27078         });
27079         
27080         return ret;
27081     },
27082
27083     /**
27084      * Clears all invalid messages in this form.
27085      * @return {BasicForm} this
27086      */
27087     clearInvalid : function(){
27088         this.items.each(function(f){
27089            f.clearInvalid();
27090         });
27091         
27092         Roo.each(this.childForms || [], function (f) {
27093             f.clearInvalid();
27094         });
27095         
27096         
27097         return this;
27098     },
27099
27100     /**
27101      * Resets this form.
27102      * @return {BasicForm} this
27103      */
27104     reset : function(){
27105         this.items.each(function(f){
27106             f.reset();
27107         });
27108         
27109         Roo.each(this.childForms || [], function (f) {
27110             f.reset();
27111         });
27112        
27113         
27114         return this;
27115     },
27116
27117     /**
27118      * Add Roo.form components to this form.
27119      * @param {Field} field1
27120      * @param {Field} field2 (optional)
27121      * @param {Field} etc (optional)
27122      * @return {BasicForm} this
27123      */
27124     add : function(){
27125         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27126         return this;
27127     },
27128
27129
27130     /**
27131      * Removes a field from the items collection (does NOT remove its markup).
27132      * @param {Field} field
27133      * @return {BasicForm} this
27134      */
27135     remove : function(field){
27136         this.items.remove(field);
27137         return this;
27138     },
27139
27140     /**
27141      * Looks at the fields in this form, checks them for an id attribute,
27142      * and calls applyTo on the existing dom element with that id.
27143      * @return {BasicForm} this
27144      */
27145     render : function(){
27146         this.items.each(function(f){
27147             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27148                 f.applyTo(f.id);
27149             }
27150         });
27151         return this;
27152     },
27153
27154     /**
27155      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27156      * @param {Object} values
27157      * @return {BasicForm} this
27158      */
27159     applyToFields : function(o){
27160         this.items.each(function(f){
27161            Roo.apply(f, o);
27162         });
27163         return this;
27164     },
27165
27166     /**
27167      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27168      * @param {Object} values
27169      * @return {BasicForm} this
27170      */
27171     applyIfToFields : function(o){
27172         this.items.each(function(f){
27173            Roo.applyIf(f, o);
27174         });
27175         return this;
27176     }
27177 });
27178
27179 // back compat
27180 Roo.BasicForm = Roo.form.BasicForm;/*
27181  * Based on:
27182  * Ext JS Library 1.1.1
27183  * Copyright(c) 2006-2007, Ext JS, LLC.
27184  *
27185  * Originally Released Under LGPL - original licence link has changed is not relivant.
27186  *
27187  * Fork - LGPL
27188  * <script type="text/javascript">
27189  */
27190
27191 /**
27192  * @class Roo.form.Form
27193  * @extends Roo.form.BasicForm
27194  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27195  * @constructor
27196  * @param {Object} config Configuration options
27197  */
27198 Roo.form.Form = function(config){
27199     var xitems =  [];
27200     if (config.items) {
27201         xitems = config.items;
27202         delete config.items;
27203     }
27204    
27205     
27206     Roo.form.Form.superclass.constructor.call(this, null, config);
27207     this.url = this.url || this.action;
27208     if(!this.root){
27209         this.root = new Roo.form.Layout(Roo.applyIf({
27210             id: Roo.id()
27211         }, config));
27212     }
27213     this.active = this.root;
27214     /**
27215      * Array of all the buttons that have been added to this form via {@link addButton}
27216      * @type Array
27217      */
27218     this.buttons = [];
27219     this.allItems = [];
27220     this.addEvents({
27221         /**
27222          * @event clientvalidation
27223          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27224          * @param {Form} this
27225          * @param {Boolean} valid true if the form has passed client-side validation
27226          */
27227         clientvalidation: true,
27228         /**
27229          * @event rendered
27230          * Fires when the form is rendered
27231          * @param {Roo.form.Form} form
27232          */
27233         rendered : true
27234     });
27235     
27236     if (this.progressUrl) {
27237             // push a hidden field onto the list of fields..
27238             this.addxtype( {
27239                     xns: Roo.form, 
27240                     xtype : 'Hidden', 
27241                     name : 'UPLOAD_IDENTIFIER' 
27242             });
27243         }
27244         
27245     
27246     Roo.each(xitems, this.addxtype, this);
27247     
27248     
27249     
27250 };
27251
27252 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27253     /**
27254      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27255      */
27256     /**
27257      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27258      */
27259     /**
27260      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27261      */
27262     buttonAlign:'center',
27263
27264     /**
27265      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27266      */
27267     minButtonWidth:75,
27268
27269     /**
27270      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27271      * This property cascades to child containers if not set.
27272      */
27273     labelAlign:'left',
27274
27275     /**
27276      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27277      * fires a looping event with that state. This is required to bind buttons to the valid
27278      * state using the config value formBind:true on the button.
27279      */
27280     monitorValid : false,
27281
27282     /**
27283      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27284      */
27285     monitorPoll : 200,
27286     
27287     /**
27288      * @cfg {String} progressUrl - Url to return progress data 
27289      */
27290     
27291     progressUrl : false,
27292   
27293     /**
27294      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27295      * fields are added and the column is closed. If no fields are passed the column remains open
27296      * until end() is called.
27297      * @param {Object} config The config to pass to the column
27298      * @param {Field} field1 (optional)
27299      * @param {Field} field2 (optional)
27300      * @param {Field} etc (optional)
27301      * @return Column The column container object
27302      */
27303     column : function(c){
27304         var col = new Roo.form.Column(c);
27305         this.start(col);
27306         if(arguments.length > 1){ // duplicate code required because of Opera
27307             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27308             this.end();
27309         }
27310         return col;
27311     },
27312
27313     /**
27314      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27315      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27316      * until end() is called.
27317      * @param {Object} config The config to pass to the fieldset
27318      * @param {Field} field1 (optional)
27319      * @param {Field} field2 (optional)
27320      * @param {Field} etc (optional)
27321      * @return FieldSet The fieldset container object
27322      */
27323     fieldset : function(c){
27324         var fs = new Roo.form.FieldSet(c);
27325         this.start(fs);
27326         if(arguments.length > 1){ // duplicate code required because of Opera
27327             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27328             this.end();
27329         }
27330         return fs;
27331     },
27332
27333     /**
27334      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27335      * fields are added and the container is closed. If no fields are passed the container remains open
27336      * until end() is called.
27337      * @param {Object} config The config to pass to the Layout
27338      * @param {Field} field1 (optional)
27339      * @param {Field} field2 (optional)
27340      * @param {Field} etc (optional)
27341      * @return Layout The container object
27342      */
27343     container : function(c){
27344         var l = new Roo.form.Layout(c);
27345         this.start(l);
27346         if(arguments.length > 1){ // duplicate code required because of Opera
27347             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27348             this.end();
27349         }
27350         return l;
27351     },
27352
27353     /**
27354      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27355      * @param {Object} container A Roo.form.Layout or subclass of Layout
27356      * @return {Form} this
27357      */
27358     start : function(c){
27359         // cascade label info
27360         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27361         this.active.stack.push(c);
27362         c.ownerCt = this.active;
27363         this.active = c;
27364         return this;
27365     },
27366
27367     /**
27368      * Closes the current open container
27369      * @return {Form} this
27370      */
27371     end : function(){
27372         if(this.active == this.root){
27373             return this;
27374         }
27375         this.active = this.active.ownerCt;
27376         return this;
27377     },
27378
27379     /**
27380      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27381      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27382      * as the label of the field.
27383      * @param {Field} field1
27384      * @param {Field} field2 (optional)
27385      * @param {Field} etc. (optional)
27386      * @return {Form} this
27387      */
27388     add : function(){
27389         this.active.stack.push.apply(this.active.stack, arguments);
27390         this.allItems.push.apply(this.allItems,arguments);
27391         var r = [];
27392         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27393             if(a[i].isFormField){
27394                 r.push(a[i]);
27395             }
27396         }
27397         if(r.length > 0){
27398             Roo.form.Form.superclass.add.apply(this, r);
27399         }
27400         return this;
27401     },
27402     
27403
27404     
27405     
27406     
27407      /**
27408      * Find any element that has been added to a form, using it's ID or name
27409      * This can include framesets, columns etc. along with regular fields..
27410      * @param {String} id - id or name to find.
27411      
27412      * @return {Element} e - or false if nothing found.
27413      */
27414     findbyId : function(id)
27415     {
27416         var ret = false;
27417         if (!id) {
27418             return ret;
27419         }
27420         Roo.each(this.allItems, function(f){
27421             if (f.id == id || f.name == id ){
27422                 ret = f;
27423                 return false;
27424             }
27425         });
27426         return ret;
27427     },
27428
27429     
27430     
27431     /**
27432      * Render this form into the passed container. This should only be called once!
27433      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27434      * @return {Form} this
27435      */
27436     render : function(ct)
27437     {
27438         
27439         
27440         
27441         ct = Roo.get(ct);
27442         var o = this.autoCreate || {
27443             tag: 'form',
27444             method : this.method || 'POST',
27445             id : this.id || Roo.id()
27446         };
27447         this.initEl(ct.createChild(o));
27448
27449         this.root.render(this.el);
27450         
27451        
27452              
27453         this.items.each(function(f){
27454             f.render('x-form-el-'+f.id);
27455         });
27456
27457         if(this.buttons.length > 0){
27458             // tables are required to maintain order and for correct IE layout
27459             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27460                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27461                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27462             }}, null, true);
27463             var tr = tb.getElementsByTagName('tr')[0];
27464             for(var i = 0, len = this.buttons.length; i < len; i++) {
27465                 var b = this.buttons[i];
27466                 var td = document.createElement('td');
27467                 td.className = 'x-form-btn-td';
27468                 b.render(tr.appendChild(td));
27469             }
27470         }
27471         if(this.monitorValid){ // initialize after render
27472             this.startMonitoring();
27473         }
27474         this.fireEvent('rendered', this);
27475         return this;
27476     },
27477
27478     /**
27479      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27480      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27481      * object or a valid Roo.DomHelper element config
27482      * @param {Function} handler The function called when the button is clicked
27483      * @param {Object} scope (optional) The scope of the handler function
27484      * @return {Roo.Button}
27485      */
27486     addButton : function(config, handler, scope){
27487         var bc = {
27488             handler: handler,
27489             scope: scope,
27490             minWidth: this.minButtonWidth,
27491             hideParent:true
27492         };
27493         if(typeof config == "string"){
27494             bc.text = config;
27495         }else{
27496             Roo.apply(bc, config);
27497         }
27498         var btn = new Roo.Button(null, bc);
27499         this.buttons.push(btn);
27500         return btn;
27501     },
27502
27503      /**
27504      * Adds a series of form elements (using the xtype property as the factory method.
27505      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27506      * @param {Object} config 
27507      */
27508     
27509     addxtype : function()
27510     {
27511         var ar = Array.prototype.slice.call(arguments, 0);
27512         var ret = false;
27513         for(var i = 0; i < ar.length; i++) {
27514             if (!ar[i]) {
27515                 continue; // skip -- if this happends something invalid got sent, we 
27516                 // should ignore it, as basically that interface element will not show up
27517                 // and that should be pretty obvious!!
27518             }
27519             
27520             if (Roo.form[ar[i].xtype]) {
27521                 ar[i].form = this;
27522                 var fe = Roo.factory(ar[i], Roo.form);
27523                 if (!ret) {
27524                     ret = fe;
27525                 }
27526                 fe.form = this;
27527                 if (fe.store) {
27528                     fe.store.form = this;
27529                 }
27530                 if (fe.isLayout) {  
27531                          
27532                     this.start(fe);
27533                     this.allItems.push(fe);
27534                     if (fe.items && fe.addxtype) {
27535                         fe.addxtype.apply(fe, fe.items);
27536                         delete fe.items;
27537                     }
27538                      this.end();
27539                     continue;
27540                 }
27541                 
27542                 
27543                  
27544                 this.add(fe);
27545               //  console.log('adding ' + ar[i].xtype);
27546             }
27547             if (ar[i].xtype == 'Button') {  
27548                 //console.log('adding button');
27549                 //console.log(ar[i]);
27550                 this.addButton(ar[i]);
27551                 this.allItems.push(fe);
27552                 continue;
27553             }
27554             
27555             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27556                 alert('end is not supported on xtype any more, use items');
27557             //    this.end();
27558             //    //console.log('adding end');
27559             }
27560             
27561         }
27562         return ret;
27563     },
27564     
27565     /**
27566      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27567      * option "monitorValid"
27568      */
27569     startMonitoring : function(){
27570         if(!this.bound){
27571             this.bound = true;
27572             Roo.TaskMgr.start({
27573                 run : this.bindHandler,
27574                 interval : this.monitorPoll || 200,
27575                 scope: this
27576             });
27577         }
27578     },
27579
27580     /**
27581      * Stops monitoring of the valid state of this form
27582      */
27583     stopMonitoring : function(){
27584         this.bound = false;
27585     },
27586
27587     // private
27588     bindHandler : function(){
27589         if(!this.bound){
27590             return false; // stops binding
27591         }
27592         var valid = true;
27593         this.items.each(function(f){
27594             if(!f.isValid(true)){
27595                 valid = false;
27596                 return false;
27597             }
27598         });
27599         for(var i = 0, len = this.buttons.length; i < len; i++){
27600             var btn = this.buttons[i];
27601             if(btn.formBind === true && btn.disabled === valid){
27602                 btn.setDisabled(!valid);
27603             }
27604         }
27605         this.fireEvent('clientvalidation', this, valid);
27606     }
27607     
27608     
27609     
27610     
27611     
27612     
27613     
27614     
27615 });
27616
27617
27618 // back compat
27619 Roo.Form = Roo.form.Form;
27620 /*
27621  * Based on:
27622  * Ext JS Library 1.1.1
27623  * Copyright(c) 2006-2007, Ext JS, LLC.
27624  *
27625  * Originally Released Under LGPL - original licence link has changed is not relivant.
27626  *
27627  * Fork - LGPL
27628  * <script type="text/javascript">
27629  */
27630  
27631  /**
27632  * @class Roo.form.Action
27633  * Internal Class used to handle form actions
27634  * @constructor
27635  * @param {Roo.form.BasicForm} el The form element or its id
27636  * @param {Object} config Configuration options
27637  */
27638  
27639  
27640 // define the action interface
27641 Roo.form.Action = function(form, options){
27642     this.form = form;
27643     this.options = options || {};
27644 };
27645 /**
27646  * Client Validation Failed
27647  * @const 
27648  */
27649 Roo.form.Action.CLIENT_INVALID = 'client';
27650 /**
27651  * Server Validation Failed
27652  * @const 
27653  */
27654  Roo.form.Action.SERVER_INVALID = 'server';
27655  /**
27656  * Connect to Server Failed
27657  * @const 
27658  */
27659 Roo.form.Action.CONNECT_FAILURE = 'connect';
27660 /**
27661  * Reading Data from Server Failed
27662  * @const 
27663  */
27664 Roo.form.Action.LOAD_FAILURE = 'load';
27665
27666 Roo.form.Action.prototype = {
27667     type : 'default',
27668     failureType : undefined,
27669     response : undefined,
27670     result : undefined,
27671
27672     // interface method
27673     run : function(options){
27674
27675     },
27676
27677     // interface method
27678     success : function(response){
27679
27680     },
27681
27682     // interface method
27683     handleResponse : function(response){
27684
27685     },
27686
27687     // default connection failure
27688     failure : function(response){
27689         
27690         this.response = response;
27691         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27692         this.form.afterAction(this, false);
27693     },
27694
27695     processResponse : function(response){
27696         this.response = response;
27697         if(!response.responseText){
27698             return true;
27699         }
27700         this.result = this.handleResponse(response);
27701         return this.result;
27702     },
27703
27704     // utility functions used internally
27705     getUrl : function(appendParams){
27706         var url = this.options.url || this.form.url || this.form.el.dom.action;
27707         if(appendParams){
27708             var p = this.getParams();
27709             if(p){
27710                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27711             }
27712         }
27713         return url;
27714     },
27715
27716     getMethod : function(){
27717         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27718     },
27719
27720     getParams : function(){
27721         var bp = this.form.baseParams;
27722         var p = this.options.params;
27723         if(p){
27724             if(typeof p == "object"){
27725                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27726             }else if(typeof p == 'string' && bp){
27727                 p += '&' + Roo.urlEncode(bp);
27728             }
27729         }else if(bp){
27730             p = Roo.urlEncode(bp);
27731         }
27732         return p;
27733     },
27734
27735     createCallback : function(){
27736         return {
27737             success: this.success,
27738             failure: this.failure,
27739             scope: this,
27740             timeout: (this.form.timeout*1000),
27741             upload: this.form.fileUpload ? this.success : undefined
27742         };
27743     }
27744 };
27745
27746 Roo.form.Action.Submit = function(form, options){
27747     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27748 };
27749
27750 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27751     type : 'submit',
27752
27753     haveProgress : false,
27754     uploadComplete : false,
27755     
27756     // uploadProgress indicator.
27757     uploadProgress : function()
27758     {
27759         if (!this.form.progressUrl) {
27760             return;
27761         }
27762         
27763         if (!this.haveProgress) {
27764             Roo.MessageBox.progress("Uploading", "Uploading");
27765         }
27766         if (this.uploadComplete) {
27767            Roo.MessageBox.hide();
27768            return;
27769         }
27770         
27771         this.haveProgress = true;
27772    
27773         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27774         
27775         var c = new Roo.data.Connection();
27776         c.request({
27777             url : this.form.progressUrl,
27778             params: {
27779                 id : uid
27780             },
27781             method: 'GET',
27782             success : function(req){
27783                //console.log(data);
27784                 var rdata = false;
27785                 var edata;
27786                 try  {
27787                    rdata = Roo.decode(req.responseText)
27788                 } catch (e) {
27789                     Roo.log("Invalid data from server..");
27790                     Roo.log(edata);
27791                     return;
27792                 }
27793                 if (!rdata || !rdata.success) {
27794                     Roo.log(rdata);
27795                     return;
27796                 }
27797                 var data = rdata.data;
27798                 
27799                 if (this.uploadComplete) {
27800                    Roo.MessageBox.hide();
27801                    return;
27802                 }
27803                    
27804                 if (data){
27805                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27806                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27807                     );
27808                 }
27809                 this.uploadProgress.defer(2000,this);
27810             },
27811        
27812             failure: function(data) {
27813                 Roo.log('progress url failed ');
27814                 Roo.log(data);
27815             },
27816             scope : this
27817         });
27818            
27819     },
27820     
27821     
27822     run : function()
27823     {
27824         // run get Values on the form, so it syncs any secondary forms.
27825         this.form.getValues();
27826         
27827         var o = this.options;
27828         var method = this.getMethod();
27829         var isPost = method == 'POST';
27830         if(o.clientValidation === false || this.form.isValid()){
27831             
27832             if (this.form.progressUrl) {
27833                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27834                     (new Date() * 1) + '' + Math.random());
27835                     
27836             } 
27837             
27838             
27839             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27840                 form:this.form.el.dom,
27841                 url:this.getUrl(!isPost),
27842                 method: method,
27843                 params:isPost ? this.getParams() : null,
27844                 isUpload: this.form.fileUpload
27845             }));
27846             
27847             this.uploadProgress();
27848
27849         }else if (o.clientValidation !== false){ // client validation failed
27850             this.failureType = Roo.form.Action.CLIENT_INVALID;
27851             this.form.afterAction(this, false);
27852         }
27853     },
27854
27855     success : function(response)
27856     {
27857         this.uploadComplete= true;
27858         if (this.haveProgress) {
27859             Roo.MessageBox.hide();
27860         }
27861         
27862         
27863         var result = this.processResponse(response);
27864         if(result === true || result.success){
27865             this.form.afterAction(this, true);
27866             return;
27867         }
27868         if(result.errors){
27869             this.form.markInvalid(result.errors);
27870             this.failureType = Roo.form.Action.SERVER_INVALID;
27871         }
27872         this.form.afterAction(this, false);
27873     },
27874     failure : function(response)
27875     {
27876         this.uploadComplete= true;
27877         if (this.haveProgress) {
27878             Roo.MessageBox.hide();
27879         }
27880         
27881         this.response = response;
27882         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27883         this.form.afterAction(this, false);
27884     },
27885     
27886     handleResponse : function(response){
27887         if(this.form.errorReader){
27888             var rs = this.form.errorReader.read(response);
27889             var errors = [];
27890             if(rs.records){
27891                 for(var i = 0, len = rs.records.length; i < len; i++) {
27892                     var r = rs.records[i];
27893                     errors[i] = r.data;
27894                 }
27895             }
27896             if(errors.length < 1){
27897                 errors = null;
27898             }
27899             return {
27900                 success : rs.success,
27901                 errors : errors
27902             };
27903         }
27904         var ret = false;
27905         try {
27906             ret = Roo.decode(response.responseText);
27907         } catch (e) {
27908             ret = {
27909                 success: false,
27910                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27911                 errors : []
27912             };
27913         }
27914         return ret;
27915         
27916     }
27917 });
27918
27919
27920 Roo.form.Action.Load = function(form, options){
27921     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27922     this.reader = this.form.reader;
27923 };
27924
27925 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27926     type : 'load',
27927
27928     run : function(){
27929         
27930         Roo.Ajax.request(Roo.apply(
27931                 this.createCallback(), {
27932                     method:this.getMethod(),
27933                     url:this.getUrl(false),
27934                     params:this.getParams()
27935         }));
27936     },
27937
27938     success : function(response){
27939         
27940         var result = this.processResponse(response);
27941         if(result === true || !result.success || !result.data){
27942             this.failureType = Roo.form.Action.LOAD_FAILURE;
27943             this.form.afterAction(this, false);
27944             return;
27945         }
27946         this.form.clearInvalid();
27947         this.form.setValues(result.data);
27948         this.form.afterAction(this, true);
27949     },
27950
27951     handleResponse : function(response){
27952         if(this.form.reader){
27953             var rs = this.form.reader.read(response);
27954             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27955             return {
27956                 success : rs.success,
27957                 data : data
27958             };
27959         }
27960         return Roo.decode(response.responseText);
27961     }
27962 });
27963
27964 Roo.form.Action.ACTION_TYPES = {
27965     'load' : Roo.form.Action.Load,
27966     'submit' : Roo.form.Action.Submit
27967 };/*
27968  * Based on:
27969  * Ext JS Library 1.1.1
27970  * Copyright(c) 2006-2007, Ext JS, LLC.
27971  *
27972  * Originally Released Under LGPL - original licence link has changed is not relivant.
27973  *
27974  * Fork - LGPL
27975  * <script type="text/javascript">
27976  */
27977  
27978 /**
27979  * @class Roo.form.Layout
27980  * @extends Roo.Component
27981  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27982  * @constructor
27983  * @param {Object} config Configuration options
27984  */
27985 Roo.form.Layout = function(config){
27986     var xitems = [];
27987     if (config.items) {
27988         xitems = config.items;
27989         delete config.items;
27990     }
27991     Roo.form.Layout.superclass.constructor.call(this, config);
27992     this.stack = [];
27993     Roo.each(xitems, this.addxtype, this);
27994      
27995 };
27996
27997 Roo.extend(Roo.form.Layout, Roo.Component, {
27998     /**
27999      * @cfg {String/Object} autoCreate
28000      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28001      */
28002     /**
28003      * @cfg {String/Object/Function} style
28004      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28005      * a function which returns such a specification.
28006      */
28007     /**
28008      * @cfg {String} labelAlign
28009      * Valid values are "left," "top" and "right" (defaults to "left")
28010      */
28011     /**
28012      * @cfg {Number} labelWidth
28013      * Fixed width in pixels of all field labels (defaults to undefined)
28014      */
28015     /**
28016      * @cfg {Boolean} clear
28017      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28018      */
28019     clear : true,
28020     /**
28021      * @cfg {String} labelSeparator
28022      * The separator to use after field labels (defaults to ':')
28023      */
28024     labelSeparator : ':',
28025     /**
28026      * @cfg {Boolean} hideLabels
28027      * True to suppress the display of field labels in this layout (defaults to false)
28028      */
28029     hideLabels : false,
28030
28031     // private
28032     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28033     
28034     isLayout : true,
28035     
28036     // private
28037     onRender : function(ct, position){
28038         if(this.el){ // from markup
28039             this.el = Roo.get(this.el);
28040         }else {  // generate
28041             var cfg = this.getAutoCreate();
28042             this.el = ct.createChild(cfg, position);
28043         }
28044         if(this.style){
28045             this.el.applyStyles(this.style);
28046         }
28047         if(this.labelAlign){
28048             this.el.addClass('x-form-label-'+this.labelAlign);
28049         }
28050         if(this.hideLabels){
28051             this.labelStyle = "display:none";
28052             this.elementStyle = "padding-left:0;";
28053         }else{
28054             if(typeof this.labelWidth == 'number'){
28055                 this.labelStyle = "width:"+this.labelWidth+"px;";
28056                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28057             }
28058             if(this.labelAlign == 'top'){
28059                 this.labelStyle = "width:auto;";
28060                 this.elementStyle = "padding-left:0;";
28061             }
28062         }
28063         var stack = this.stack;
28064         var slen = stack.length;
28065         if(slen > 0){
28066             if(!this.fieldTpl){
28067                 var t = new Roo.Template(
28068                     '<div class="x-form-item {5}">',
28069                         '<label for="{0}" style="{2}">{1}{4}</label>',
28070                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28071                         '</div>',
28072                     '</div><div class="x-form-clear-left"></div>'
28073                 );
28074                 t.disableFormats = true;
28075                 t.compile();
28076                 Roo.form.Layout.prototype.fieldTpl = t;
28077             }
28078             for(var i = 0; i < slen; i++) {
28079                 if(stack[i].isFormField){
28080                     this.renderField(stack[i]);
28081                 }else{
28082                     this.renderComponent(stack[i]);
28083                 }
28084             }
28085         }
28086         if(this.clear){
28087             this.el.createChild({cls:'x-form-clear'});
28088         }
28089     },
28090
28091     // private
28092     renderField : function(f){
28093         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28094                f.id, //0
28095                f.fieldLabel, //1
28096                f.labelStyle||this.labelStyle||'', //2
28097                this.elementStyle||'', //3
28098                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28099                f.itemCls||this.itemCls||''  //5
28100        ], true).getPrevSibling());
28101     },
28102
28103     // private
28104     renderComponent : function(c){
28105         c.render(c.isLayout ? this.el : this.el.createChild());    
28106     },
28107     /**
28108      * Adds a object form elements (using the xtype property as the factory method.)
28109      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28110      * @param {Object} config 
28111      */
28112     addxtype : function(o)
28113     {
28114         // create the lement.
28115         o.form = this.form;
28116         var fe = Roo.factory(o, Roo.form);
28117         this.form.allItems.push(fe);
28118         this.stack.push(fe);
28119         
28120         if (fe.isFormField) {
28121             this.form.items.add(fe);
28122         }
28123          
28124         return fe;
28125     }
28126 });
28127
28128 /**
28129  * @class Roo.form.Column
28130  * @extends Roo.form.Layout
28131  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28132  * @constructor
28133  * @param {Object} config Configuration options
28134  */
28135 Roo.form.Column = function(config){
28136     Roo.form.Column.superclass.constructor.call(this, config);
28137 };
28138
28139 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28140     /**
28141      * @cfg {Number/String} width
28142      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28143      */
28144     /**
28145      * @cfg {String/Object} autoCreate
28146      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28147      */
28148
28149     // private
28150     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28151
28152     // private
28153     onRender : function(ct, position){
28154         Roo.form.Column.superclass.onRender.call(this, ct, position);
28155         if(this.width){
28156             this.el.setWidth(this.width);
28157         }
28158     }
28159 });
28160
28161
28162 /**
28163  * @class Roo.form.Row
28164  * @extends Roo.form.Layout
28165  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28166  * @constructor
28167  * @param {Object} config Configuration options
28168  */
28169
28170  
28171 Roo.form.Row = function(config){
28172     Roo.form.Row.superclass.constructor.call(this, config);
28173 };
28174  
28175 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28176       /**
28177      * @cfg {Number/String} width
28178      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28179      */
28180     /**
28181      * @cfg {Number/String} height
28182      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28183      */
28184     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28185     
28186     padWidth : 20,
28187     // private
28188     onRender : function(ct, position){
28189         //console.log('row render');
28190         if(!this.rowTpl){
28191             var t = new Roo.Template(
28192                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28193                     '<label for="{0}" style="{2}">{1}{4}</label>',
28194                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28195                     '</div>',
28196                 '</div>'
28197             );
28198             t.disableFormats = true;
28199             t.compile();
28200             Roo.form.Layout.prototype.rowTpl = t;
28201         }
28202         this.fieldTpl = this.rowTpl;
28203         
28204         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28205         var labelWidth = 100;
28206         
28207         if ((this.labelAlign != 'top')) {
28208             if (typeof this.labelWidth == 'number') {
28209                 labelWidth = this.labelWidth
28210             }
28211             this.padWidth =  20 + labelWidth;
28212             
28213         }
28214         
28215         Roo.form.Column.superclass.onRender.call(this, ct, position);
28216         if(this.width){
28217             this.el.setWidth(this.width);
28218         }
28219         if(this.height){
28220             this.el.setHeight(this.height);
28221         }
28222     },
28223     
28224     // private
28225     renderField : function(f){
28226         f.fieldEl = this.fieldTpl.append(this.el, [
28227                f.id, f.fieldLabel,
28228                f.labelStyle||this.labelStyle||'',
28229                this.elementStyle||'',
28230                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28231                f.itemCls||this.itemCls||'',
28232                f.width ? f.width + this.padWidth : 160 + this.padWidth
28233        ],true);
28234     }
28235 });
28236  
28237
28238 /**
28239  * @class Roo.form.FieldSet
28240  * @extends Roo.form.Layout
28241  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28242  * @constructor
28243  * @param {Object} config Configuration options
28244  */
28245 Roo.form.FieldSet = function(config){
28246     Roo.form.FieldSet.superclass.constructor.call(this, config);
28247 };
28248
28249 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28250     /**
28251      * @cfg {String} legend
28252      * The text to display as the legend for the FieldSet (defaults to '')
28253      */
28254     /**
28255      * @cfg {String/Object} autoCreate
28256      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28257      */
28258
28259     // private
28260     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28261
28262     // private
28263     onRender : function(ct, position){
28264         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28265         if(this.legend){
28266             this.setLegend(this.legend);
28267         }
28268     },
28269
28270     // private
28271     setLegend : function(text){
28272         if(this.rendered){
28273             this.el.child('legend').update(text);
28274         }
28275     }
28276 });/*
28277  * Based on:
28278  * Ext JS Library 1.1.1
28279  * Copyright(c) 2006-2007, Ext JS, LLC.
28280  *
28281  * Originally Released Under LGPL - original licence link has changed is not relivant.
28282  *
28283  * Fork - LGPL
28284  * <script type="text/javascript">
28285  */
28286 /**
28287  * @class Roo.form.VTypes
28288  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28289  * @singleton
28290  */
28291 Roo.form.VTypes = function(){
28292     // closure these in so they are only created once.
28293     var alpha = /^[a-zA-Z_]+$/;
28294     var alphanum = /^[a-zA-Z0-9_]+$/;
28295     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28296     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28297
28298     // All these messages and functions are configurable
28299     return {
28300         /**
28301          * The function used to validate email addresses
28302          * @param {String} value The email address
28303          */
28304         'email' : function(v){
28305             return email.test(v);
28306         },
28307         /**
28308          * The error text to display when the email validation function returns false
28309          * @type String
28310          */
28311         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28312         /**
28313          * The keystroke filter mask to be applied on email input
28314          * @type RegExp
28315          */
28316         'emailMask' : /[a-z0-9_\.\-@]/i,
28317
28318         /**
28319          * The function used to validate URLs
28320          * @param {String} value The URL
28321          */
28322         'url' : function(v){
28323             return url.test(v);
28324         },
28325         /**
28326          * The error text to display when the url validation function returns false
28327          * @type String
28328          */
28329         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28330         
28331         /**
28332          * The function used to validate alpha values
28333          * @param {String} value The value
28334          */
28335         'alpha' : function(v){
28336             return alpha.test(v);
28337         },
28338         /**
28339          * The error text to display when the alpha validation function returns false
28340          * @type String
28341          */
28342         'alphaText' : 'This field should only contain letters and _',
28343         /**
28344          * The keystroke filter mask to be applied on alpha input
28345          * @type RegExp
28346          */
28347         'alphaMask' : /[a-z_]/i,
28348
28349         /**
28350          * The function used to validate alphanumeric values
28351          * @param {String} value The value
28352          */
28353         'alphanum' : function(v){
28354             return alphanum.test(v);
28355         },
28356         /**
28357          * The error text to display when the alphanumeric validation function returns false
28358          * @type String
28359          */
28360         'alphanumText' : 'This field should only contain letters, numbers and _',
28361         /**
28362          * The keystroke filter mask to be applied on alphanumeric input
28363          * @type RegExp
28364          */
28365         'alphanumMask' : /[a-z0-9_]/i
28366     };
28367 }();//<script type="text/javascript">
28368
28369 /**
28370  * @class Roo.form.FCKeditor
28371  * @extends Roo.form.TextArea
28372  * Wrapper around the FCKEditor http://www.fckeditor.net
28373  * @constructor
28374  * Creates a new FCKeditor
28375  * @param {Object} config Configuration options
28376  */
28377 Roo.form.FCKeditor = function(config){
28378     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28379     this.addEvents({
28380          /**
28381          * @event editorinit
28382          * Fired when the editor is initialized - you can add extra handlers here..
28383          * @param {FCKeditor} this
28384          * @param {Object} the FCK object.
28385          */
28386         editorinit : true
28387     });
28388     
28389     
28390 };
28391 Roo.form.FCKeditor.editors = { };
28392 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28393 {
28394     //defaultAutoCreate : {
28395     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28396     //},
28397     // private
28398     /**
28399      * @cfg {Object} fck options - see fck manual for details.
28400      */
28401     fckconfig : false,
28402     
28403     /**
28404      * @cfg {Object} fck toolbar set (Basic or Default)
28405      */
28406     toolbarSet : 'Basic',
28407     /**
28408      * @cfg {Object} fck BasePath
28409      */ 
28410     basePath : '/fckeditor/',
28411     
28412     
28413     frame : false,
28414     
28415     value : '',
28416     
28417    
28418     onRender : function(ct, position)
28419     {
28420         if(!this.el){
28421             this.defaultAutoCreate = {
28422                 tag: "textarea",
28423                 style:"width:300px;height:60px;",
28424                 autocomplete: "off"
28425             };
28426         }
28427         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28428         /*
28429         if(this.grow){
28430             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28431             if(this.preventScrollbars){
28432                 this.el.setStyle("overflow", "hidden");
28433             }
28434             this.el.setHeight(this.growMin);
28435         }
28436         */
28437         //console.log('onrender' + this.getId() );
28438         Roo.form.FCKeditor.editors[this.getId()] = this;
28439          
28440
28441         this.replaceTextarea() ;
28442         
28443     },
28444     
28445     getEditor : function() {
28446         return this.fckEditor;
28447     },
28448     /**
28449      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28450      * @param {Mixed} value The value to set
28451      */
28452     
28453     
28454     setValue : function(value)
28455     {
28456         //console.log('setValue: ' + value);
28457         
28458         if(typeof(value) == 'undefined') { // not sure why this is happending...
28459             return;
28460         }
28461         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28462         
28463         //if(!this.el || !this.getEditor()) {
28464         //    this.value = value;
28465             //this.setValue.defer(100,this,[value]);    
28466         //    return;
28467         //} 
28468         
28469         if(!this.getEditor()) {
28470             return;
28471         }
28472         
28473         this.getEditor().SetData(value);
28474         
28475         //
28476
28477     },
28478
28479     /**
28480      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28481      * @return {Mixed} value The field value
28482      */
28483     getValue : function()
28484     {
28485         
28486         if (this.frame && this.frame.dom.style.display == 'none') {
28487             return Roo.form.FCKeditor.superclass.getValue.call(this);
28488         }
28489         
28490         if(!this.el || !this.getEditor()) {
28491            
28492            // this.getValue.defer(100,this); 
28493             return this.value;
28494         }
28495        
28496         
28497         var value=this.getEditor().GetData();
28498         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28499         return Roo.form.FCKeditor.superclass.getValue.call(this);
28500         
28501
28502     },
28503
28504     /**
28505      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28506      * @return {Mixed} value The field value
28507      */
28508     getRawValue : function()
28509     {
28510         if (this.frame && this.frame.dom.style.display == 'none') {
28511             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28512         }
28513         
28514         if(!this.el || !this.getEditor()) {
28515             //this.getRawValue.defer(100,this); 
28516             return this.value;
28517             return;
28518         }
28519         
28520         
28521         
28522         var value=this.getEditor().GetData();
28523         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28524         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28525          
28526     },
28527     
28528     setSize : function(w,h) {
28529         
28530         
28531         
28532         //if (this.frame && this.frame.dom.style.display == 'none') {
28533         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28534         //    return;
28535         //}
28536         //if(!this.el || !this.getEditor()) {
28537         //    this.setSize.defer(100,this, [w,h]); 
28538         //    return;
28539         //}
28540         
28541         
28542         
28543         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28544         
28545         this.frame.dom.setAttribute('width', w);
28546         this.frame.dom.setAttribute('height', h);
28547         this.frame.setSize(w,h);
28548         
28549     },
28550     
28551     toggleSourceEdit : function(value) {
28552         
28553       
28554          
28555         this.el.dom.style.display = value ? '' : 'none';
28556         this.frame.dom.style.display = value ?  'none' : '';
28557         
28558     },
28559     
28560     
28561     focus: function(tag)
28562     {
28563         if (this.frame.dom.style.display == 'none') {
28564             return Roo.form.FCKeditor.superclass.focus.call(this);
28565         }
28566         if(!this.el || !this.getEditor()) {
28567             this.focus.defer(100,this, [tag]); 
28568             return;
28569         }
28570         
28571         
28572         
28573         
28574         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28575         this.getEditor().Focus();
28576         if (tgs.length) {
28577             if (!this.getEditor().Selection.GetSelection()) {
28578                 this.focus.defer(100,this, [tag]); 
28579                 return;
28580             }
28581             
28582             
28583             var r = this.getEditor().EditorDocument.createRange();
28584             r.setStart(tgs[0],0);
28585             r.setEnd(tgs[0],0);
28586             this.getEditor().Selection.GetSelection().removeAllRanges();
28587             this.getEditor().Selection.GetSelection().addRange(r);
28588             this.getEditor().Focus();
28589         }
28590         
28591     },
28592     
28593     
28594     
28595     replaceTextarea : function()
28596     {
28597         if ( document.getElementById( this.getId() + '___Frame' ) )
28598             return ;
28599         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28600         //{
28601             // We must check the elements firstly using the Id and then the name.
28602         var oTextarea = document.getElementById( this.getId() );
28603         
28604         var colElementsByName = document.getElementsByName( this.getId() ) ;
28605          
28606         oTextarea.style.display = 'none' ;
28607
28608         if ( oTextarea.tabIndex ) {            
28609             this.TabIndex = oTextarea.tabIndex ;
28610         }
28611         
28612         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28613         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28614         this.frame = Roo.get(this.getId() + '___Frame')
28615     },
28616     
28617     _getConfigHtml : function()
28618     {
28619         var sConfig = '' ;
28620
28621         for ( var o in this.fckconfig ) {
28622             sConfig += sConfig.length > 0  ? '&amp;' : '';
28623             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28624         }
28625
28626         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28627     },
28628     
28629     
28630     _getIFrameHtml : function()
28631     {
28632         var sFile = 'fckeditor.html' ;
28633         /* no idea what this is about..
28634         try
28635         {
28636             if ( (/fcksource=true/i).test( window.top.location.search ) )
28637                 sFile = 'fckeditor.original.html' ;
28638         }
28639         catch (e) { 
28640         */
28641
28642         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28643         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28644         
28645         
28646         var html = '<iframe id="' + this.getId() +
28647             '___Frame" src="' + sLink +
28648             '" width="' + this.width +
28649             '" height="' + this.height + '"' +
28650             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28651             ' frameborder="0" scrolling="no"></iframe>' ;
28652
28653         return html ;
28654     },
28655     
28656     _insertHtmlBefore : function( html, element )
28657     {
28658         if ( element.insertAdjacentHTML )       {
28659             // IE
28660             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28661         } else { // Gecko
28662             var oRange = document.createRange() ;
28663             oRange.setStartBefore( element ) ;
28664             var oFragment = oRange.createContextualFragment( html );
28665             element.parentNode.insertBefore( oFragment, element ) ;
28666         }
28667     }
28668     
28669     
28670   
28671     
28672     
28673     
28674     
28675
28676 });
28677
28678 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28679
28680 function FCKeditor_OnComplete(editorInstance){
28681     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28682     f.fckEditor = editorInstance;
28683     //console.log("loaded");
28684     f.fireEvent('editorinit', f, editorInstance);
28685
28686   
28687
28688  
28689
28690
28691
28692
28693
28694
28695
28696
28697
28698
28699
28700
28701
28702
28703
28704 //<script type="text/javascript">
28705 /**
28706  * @class Roo.form.GridField
28707  * @extends Roo.form.Field
28708  * Embed a grid (or editable grid into a form)
28709  * STATUS ALPHA
28710  * 
28711  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28712  * it needs 
28713  * xgrid.store = Roo.data.Store
28714  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28715  * xgrid.store.reader = Roo.data.JsonReader 
28716  * 
28717  * 
28718  * @constructor
28719  * Creates a new GridField
28720  * @param {Object} config Configuration options
28721  */
28722 Roo.form.GridField = function(config){
28723     Roo.form.GridField.superclass.constructor.call(this, config);
28724      
28725 };
28726
28727 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28728     /**
28729      * @cfg {Number} width  - used to restrict width of grid..
28730      */
28731     width : 100,
28732     /**
28733      * @cfg {Number} height - used to restrict height of grid..
28734      */
28735     height : 50,
28736      /**
28737      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28738          * 
28739          *}
28740      */
28741     xgrid : false, 
28742     /**
28743      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28744      * {tag: "input", type: "checkbox", autocomplete: "off"})
28745      */
28746    // defaultAutoCreate : { tag: 'div' },
28747     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28748     /**
28749      * @cfg {String} addTitle Text to include for adding a title.
28750      */
28751     addTitle : false,
28752     //
28753     onResize : function(){
28754         Roo.form.Field.superclass.onResize.apply(this, arguments);
28755     },
28756
28757     initEvents : function(){
28758         // Roo.form.Checkbox.superclass.initEvents.call(this);
28759         // has no events...
28760        
28761     },
28762
28763
28764     getResizeEl : function(){
28765         return this.wrap;
28766     },
28767
28768     getPositionEl : function(){
28769         return this.wrap;
28770     },
28771
28772     // private
28773     onRender : function(ct, position){
28774         
28775         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28776         var style = this.style;
28777         delete this.style;
28778         
28779         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28780         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28781         this.viewEl = this.wrap.createChild({ tag: 'div' });
28782         if (style) {
28783             this.viewEl.applyStyles(style);
28784         }
28785         if (this.width) {
28786             this.viewEl.setWidth(this.width);
28787         }
28788         if (this.height) {
28789             this.viewEl.setHeight(this.height);
28790         }
28791         //if(this.inputValue !== undefined){
28792         //this.setValue(this.value);
28793         
28794         
28795         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28796         
28797         
28798         this.grid.render();
28799         this.grid.getDataSource().on('remove', this.refreshValue, this);
28800         this.grid.getDataSource().on('update', this.refreshValue, this);
28801         this.grid.on('afteredit', this.refreshValue, this);
28802  
28803     },
28804      
28805     
28806     /**
28807      * Sets the value of the item. 
28808      * @param {String} either an object  or a string..
28809      */
28810     setValue : function(v){
28811         //this.value = v;
28812         v = v || []; // empty set..
28813         // this does not seem smart - it really only affects memoryproxy grids..
28814         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28815             var ds = this.grid.getDataSource();
28816             // assumes a json reader..
28817             var data = {}
28818             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28819             ds.loadData( data);
28820         }
28821         // clear selection so it does not get stale.
28822         if (this.grid.sm) { 
28823             this.grid.sm.clearSelections();
28824         }
28825         
28826         Roo.form.GridField.superclass.setValue.call(this, v);
28827         this.refreshValue();
28828         // should load data in the grid really....
28829     },
28830     
28831     // private
28832     refreshValue: function() {
28833          var val = [];
28834         this.grid.getDataSource().each(function(r) {
28835             val.push(r.data);
28836         });
28837         this.el.dom.value = Roo.encode(val);
28838     }
28839     
28840      
28841     
28842     
28843 });/*
28844  * Based on:
28845  * Ext JS Library 1.1.1
28846  * Copyright(c) 2006-2007, Ext JS, LLC.
28847  *
28848  * Originally Released Under LGPL - original licence link has changed is not relivant.
28849  *
28850  * Fork - LGPL
28851  * <script type="text/javascript">
28852  */
28853 /**
28854  * @class Roo.form.DisplayField
28855  * @extends Roo.form.Field
28856  * A generic Field to display non-editable data.
28857  * @constructor
28858  * Creates a new Display Field item.
28859  * @param {Object} config Configuration options
28860  */
28861 Roo.form.DisplayField = function(config){
28862     Roo.form.DisplayField.superclass.constructor.call(this, config);
28863     
28864 };
28865
28866 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28867     inputType:      'hidden',
28868     allowBlank:     true,
28869     readOnly:         true,
28870     
28871  
28872     /**
28873      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28874      */
28875     focusClass : undefined,
28876     /**
28877      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28878      */
28879     fieldClass: 'x-form-field',
28880     
28881      /**
28882      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28883      */
28884     valueRenderer: undefined,
28885     
28886     width: 100,
28887     /**
28888      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28889      * {tag: "input", type: "checkbox", autocomplete: "off"})
28890      */
28891      
28892  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28893
28894     onResize : function(){
28895         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28896         
28897     },
28898
28899     initEvents : function(){
28900         // Roo.form.Checkbox.superclass.initEvents.call(this);
28901         // has no events...
28902        
28903     },
28904
28905
28906     getResizeEl : function(){
28907         return this.wrap;
28908     },
28909
28910     getPositionEl : function(){
28911         return this.wrap;
28912     },
28913
28914     // private
28915     onRender : function(ct, position){
28916         
28917         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28918         //if(this.inputValue !== undefined){
28919         this.wrap = this.el.wrap();
28920         
28921         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28922         
28923         if (this.bodyStyle) {
28924             this.viewEl.applyStyles(this.bodyStyle);
28925         }
28926         //this.viewEl.setStyle('padding', '2px');
28927         
28928         this.setValue(this.value);
28929         
28930     },
28931 /*
28932     // private
28933     initValue : Roo.emptyFn,
28934
28935   */
28936
28937         // private
28938     onClick : function(){
28939         
28940     },
28941
28942     /**
28943      * Sets the checked state of the checkbox.
28944      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28945      */
28946     setValue : function(v){
28947         this.value = v;
28948         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28949         // this might be called before we have a dom element..
28950         if (!this.viewEl) {
28951             return;
28952         }
28953         this.viewEl.dom.innerHTML = html;
28954         Roo.form.DisplayField.superclass.setValue.call(this, v);
28955
28956     }
28957 });/*
28958  * 
28959  * Licence- LGPL
28960  * 
28961  */
28962
28963 /**
28964  * @class Roo.form.DayPicker
28965  * @extends Roo.form.Field
28966  * A Day picker show [M] [T] [W] ....
28967  * @constructor
28968  * Creates a new Day Picker
28969  * @param {Object} config Configuration options
28970  */
28971 Roo.form.DayPicker= function(config){
28972     Roo.form.DayPicker.superclass.constructor.call(this, config);
28973      
28974 };
28975
28976 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28977     /**
28978      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28979      */
28980     focusClass : undefined,
28981     /**
28982      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28983      */
28984     fieldClass: "x-form-field",
28985    
28986     /**
28987      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28988      * {tag: "input", type: "checkbox", autocomplete: "off"})
28989      */
28990     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
28991     
28992    
28993     actionMode : 'viewEl', 
28994     //
28995     // private
28996  
28997     inputType : 'hidden',
28998     
28999      
29000     inputElement: false, // real input element?
29001     basedOn: false, // ????
29002     
29003     isFormField: true, // not sure where this is needed!!!!
29004
29005     onResize : function(){
29006         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29007         if(!this.boxLabel){
29008             this.el.alignTo(this.wrap, 'c-c');
29009         }
29010     },
29011
29012     initEvents : function(){
29013         Roo.form.Checkbox.superclass.initEvents.call(this);
29014         this.el.on("click", this.onClick,  this);
29015         this.el.on("change", this.onClick,  this);
29016     },
29017
29018
29019     getResizeEl : function(){
29020         return this.wrap;
29021     },
29022
29023     getPositionEl : function(){
29024         return this.wrap;
29025     },
29026
29027     
29028     // private
29029     onRender : function(ct, position){
29030         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29031        
29032         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29033         
29034         var r1 = '<table><tr>';
29035         var r2 = '<tr class="x-form-daypick-icons">';
29036         for (var i=0; i < 7; i++) {
29037             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29038             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29039         }
29040         
29041         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29042         viewEl.select('img').on('click', this.onClick, this);
29043         this.viewEl = viewEl;   
29044         
29045         
29046         // this will not work on Chrome!!!
29047         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29048         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29049         
29050         
29051           
29052
29053     },
29054
29055     // private
29056     initValue : Roo.emptyFn,
29057
29058     /**
29059      * Returns the checked state of the checkbox.
29060      * @return {Boolean} True if checked, else false
29061      */
29062     getValue : function(){
29063         return this.el.dom.value;
29064         
29065     },
29066
29067         // private
29068     onClick : function(e){ 
29069         //this.setChecked(!this.checked);
29070         Roo.get(e.target).toggleClass('x-menu-item-checked');
29071         this.refreshValue();
29072         //if(this.el.dom.checked != this.checked){
29073         //    this.setValue(this.el.dom.checked);
29074        // }
29075     },
29076     
29077     // private
29078     refreshValue : function()
29079     {
29080         var val = '';
29081         this.viewEl.select('img',true).each(function(e,i,n)  {
29082             val += e.is(".x-menu-item-checked") ? String(n) : '';
29083         });
29084         this.setValue(val, true);
29085     },
29086
29087     /**
29088      * Sets the checked state of the checkbox.
29089      * On is always based on a string comparison between inputValue and the param.
29090      * @param {Boolean/String} value - the value to set 
29091      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29092      */
29093     setValue : function(v,suppressEvent){
29094         if (!this.el.dom) {
29095             return;
29096         }
29097         var old = this.el.dom.value ;
29098         this.el.dom.value = v;
29099         if (suppressEvent) {
29100             return ;
29101         }
29102          
29103         // update display..
29104         this.viewEl.select('img',true).each(function(e,i,n)  {
29105             
29106             var on = e.is(".x-menu-item-checked");
29107             var newv = v.indexOf(String(n)) > -1;
29108             if (on != newv) {
29109                 e.toggleClass('x-menu-item-checked');
29110             }
29111             
29112         });
29113         
29114         
29115         this.fireEvent('change', this, v, old);
29116         
29117         
29118     },
29119    
29120     // handle setting of hidden value by some other method!!?!?
29121     setFromHidden: function()
29122     {
29123         if(!this.el){
29124             return;
29125         }
29126         //console.log("SET FROM HIDDEN");
29127         //alert('setFrom hidden');
29128         this.setValue(this.el.dom.value);
29129     },
29130     
29131     onDestroy : function()
29132     {
29133         if(this.viewEl){
29134             Roo.get(this.viewEl).remove();
29135         }
29136          
29137         Roo.form.DayPicker.superclass.onDestroy.call(this);
29138     }
29139
29140 });/*
29141  * RooJS Library 1.1.1
29142  * Copyright(c) 2008-2011  Alan Knowles
29143  *
29144  * License - LGPL
29145  */
29146  
29147
29148 /**
29149  * @class Roo.form.ComboCheck
29150  * @extends Roo.form.ComboBox
29151  * A combobox for multiple select items.
29152  *
29153  * FIXME - could do with a reset button..
29154  * 
29155  * @constructor
29156  * Create a new ComboCheck
29157  * @param {Object} config Configuration options
29158  */
29159 Roo.form.ComboCheck = function(config){
29160     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29161     // should verify some data...
29162     // like
29163     // hiddenName = required..
29164     // displayField = required
29165     // valudField == required
29166     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29167     var _t = this;
29168     Roo.each(req, function(e) {
29169         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29170             throw "Roo.form.ComboCheck : missing value for: " + e;
29171         }
29172     });
29173     
29174     
29175 };
29176
29177 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29178      
29179      
29180     editable : false,
29181      
29182     selectedClass: 'x-menu-item-checked', 
29183     
29184     // private
29185     onRender : function(ct, position){
29186         var _t = this;
29187         
29188         
29189         
29190         if(!this.tpl){
29191             var cls = 'x-combo-list';
29192
29193             
29194             this.tpl =  new Roo.Template({
29195                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29196                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29197                    '<span>{' + this.displayField + '}</span>' +
29198                     '</div>' 
29199                 
29200             });
29201         }
29202  
29203         
29204         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29205         this.view.singleSelect = false;
29206         this.view.multiSelect = true;
29207         this.view.toggleSelect = true;
29208         this.pageTb.add(new Roo.Toolbar.Fill(), {
29209             
29210             text: 'Done',
29211             handler: function()
29212             {
29213                 _t.collapse();
29214             }
29215         });
29216     },
29217     
29218     onViewOver : function(e, t){
29219         // do nothing...
29220         return;
29221         
29222     },
29223     
29224     onViewClick : function(doFocus,index){
29225         return;
29226         
29227     },
29228     select: function () {
29229         //Roo.log("SELECT CALLED");
29230     },
29231      
29232     selectByValue : function(xv, scrollIntoView){
29233         var ar = this.getValueArray();
29234         var sels = [];
29235         
29236         Roo.each(ar, function(v) {
29237             if(v === undefined || v === null){
29238                 return;
29239             }
29240             var r = this.findRecord(this.valueField, v);
29241             if(r){
29242                 sels.push(this.store.indexOf(r))
29243                 
29244             }
29245         },this);
29246         this.view.select(sels);
29247         return false;
29248     },
29249     
29250     
29251     
29252     onSelect : function(record, index){
29253        // Roo.log("onselect Called");
29254        // this is only called by the clear button now..
29255         this.view.clearSelections();
29256         this.setValue('[]');
29257         if (this.value != this.valueBefore) {
29258             this.fireEvent('change', this, this.value, this.valueBefore);
29259         }
29260     },
29261     getValueArray : function()
29262     {
29263         var ar = [] ;
29264         
29265         try {
29266             //Roo.log(this.value);
29267             if (typeof(this.value) == 'undefined') {
29268                 return [];
29269             }
29270             var ar = Roo.decode(this.value);
29271             return  ar instanceof Array ? ar : []; //?? valid?
29272             
29273         } catch(e) {
29274             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29275             return [];
29276         }
29277          
29278     },
29279     expand : function ()
29280     {
29281         Roo.form.ComboCheck.superclass.expand.call(this);
29282         this.valueBefore = this.value;
29283         
29284
29285     },
29286     
29287     collapse : function(){
29288         Roo.form.ComboCheck.superclass.collapse.call(this);
29289         var sl = this.view.getSelectedIndexes();
29290         var st = this.store;
29291         var nv = [];
29292         var tv = [];
29293         var r;
29294         Roo.each(sl, function(i) {
29295             r = st.getAt(i);
29296             nv.push(r.get(this.valueField));
29297         },this);
29298         this.setValue(Roo.encode(nv));
29299         if (this.value != this.valueBefore) {
29300
29301             this.fireEvent('change', this, this.value, this.valueBefore);
29302         }
29303         
29304     },
29305     
29306     setValue : function(v){
29307         // Roo.log(v);
29308         this.value = v;
29309         
29310         var vals = this.getValueArray();
29311         var tv = [];
29312         Roo.each(vals, function(k) {
29313             var r = this.findRecord(this.valueField, k);
29314             if(r){
29315                 tv.push(r.data[this.displayField]);
29316             }else if(this.valueNotFoundText !== undefined){
29317                 tv.push( this.valueNotFoundText );
29318             }
29319         },this);
29320        // Roo.log(tv);
29321         
29322         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29323         this.hiddenField.value = v;
29324         this.value = v;
29325     }
29326     
29327 });//<script type="text/javasscript">
29328  
29329
29330 /**
29331  * @class Roo.DDView
29332  * A DnD enabled version of Roo.View.
29333  * @param {Element/String} container The Element in which to create the View.
29334  * @param {String} tpl The template string used to create the markup for each element of the View
29335  * @param {Object} config The configuration properties. These include all the config options of
29336  * {@link Roo.View} plus some specific to this class.<br>
29337  * <p>
29338  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29339  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29340  * <p>
29341  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29342 .x-view-drag-insert-above {
29343         border-top:1px dotted #3366cc;
29344 }
29345 .x-view-drag-insert-below {
29346         border-bottom:1px dotted #3366cc;
29347 }
29348 </code></pre>
29349  * 
29350  */
29351  
29352 Roo.DDView = function(container, tpl, config) {
29353     Roo.DDView.superclass.constructor.apply(this, arguments);
29354     this.getEl().setStyle("outline", "0px none");
29355     this.getEl().unselectable();
29356     if (this.dragGroup) {
29357                 this.setDraggable(this.dragGroup.split(","));
29358     }
29359     if (this.dropGroup) {
29360                 this.setDroppable(this.dropGroup.split(","));
29361     }
29362     if (this.deletable) {
29363         this.setDeletable();
29364     }
29365     this.isDirtyFlag = false;
29366         this.addEvents({
29367                 "drop" : true
29368         });
29369 };
29370
29371 Roo.extend(Roo.DDView, Roo.View, {
29372 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29373 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
29374 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
29375 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
29376
29377         isFormField: true,
29378
29379         reset: Roo.emptyFn,
29380         
29381         clearInvalid: Roo.form.Field.prototype.clearInvalid,
29382
29383         validate: function() {
29384                 return true;
29385         },
29386         
29387         destroy: function() {
29388                 this.purgeListeners();
29389                 this.getEl.removeAllListeners();
29390                 this.getEl().remove();
29391                 if (this.dragZone) {
29392                         if (this.dragZone.destroy) {
29393                                 this.dragZone.destroy();
29394                         }
29395                 }
29396                 if (this.dropZone) {
29397                         if (this.dropZone.destroy) {
29398                                 this.dropZone.destroy();
29399                         }
29400                 }
29401         },
29402
29403 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
29404         getName: function() {
29405                 return this.name;
29406         },
29407
29408 /**     Loads the View from a JSON string representing the Records to put into the Store. */
29409         setValue: function(v) {
29410                 if (!this.store) {
29411                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
29412                 }
29413                 var data = {};
29414                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
29415                 this.store.proxy = new Roo.data.MemoryProxy(data);
29416                 this.store.load();
29417         },
29418
29419 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
29420         getValue: function() {
29421                 var result = '(';
29422                 this.store.each(function(rec) {
29423                         result += rec.id + ',';
29424                 });
29425                 return result.substr(0, result.length - 1) + ')';
29426         },
29427         
29428         getIds: function() {
29429                 var i = 0, result = new Array(this.store.getCount());
29430                 this.store.each(function(rec) {
29431                         result[i++] = rec.id;
29432                 });
29433                 return result;
29434         },
29435         
29436         isDirty: function() {
29437                 return this.isDirtyFlag;
29438         },
29439
29440 /**
29441  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
29442  *      whole Element becomes the target, and this causes the drop gesture to append.
29443  */
29444     getTargetFromEvent : function(e) {
29445                 var target = e.getTarget();
29446                 while ((target !== null) && (target.parentNode != this.el.dom)) {
29447                 target = target.parentNode;
29448                 }
29449                 if (!target) {
29450                         target = this.el.dom.lastChild || this.el.dom;
29451                 }
29452                 return target;
29453     },
29454
29455 /**
29456  *      Create the drag data which consists of an object which has the property "ddel" as
29457  *      the drag proxy element. 
29458  */
29459     getDragData : function(e) {
29460         var target = this.findItemFromChild(e.getTarget());
29461                 if(target) {
29462                         this.handleSelection(e);
29463                         var selNodes = this.getSelectedNodes();
29464             var dragData = {
29465                 source: this,
29466                 copy: this.copy || (this.allowCopy && e.ctrlKey),
29467                 nodes: selNodes,
29468                 records: []
29469                         };
29470                         var selectedIndices = this.getSelectedIndexes();
29471                         for (var i = 0; i < selectedIndices.length; i++) {
29472                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
29473                         }
29474                         if (selNodes.length == 1) {
29475                                 dragData.ddel = target.cloneNode(true); // the div element
29476                         } else {
29477                                 var div = document.createElement('div'); // create the multi element drag "ghost"
29478                                 div.className = 'multi-proxy';
29479                                 for (var i = 0, len = selNodes.length; i < len; i++) {
29480                                         div.appendChild(selNodes[i].cloneNode(true));
29481                                 }
29482                                 dragData.ddel = div;
29483                         }
29484             //console.log(dragData)
29485             //console.log(dragData.ddel.innerHTML)
29486                         return dragData;
29487                 }
29488         //console.log('nodragData')
29489                 return false;
29490     },
29491     
29492 /**     Specify to which ddGroup items in this DDView may be dragged. */
29493     setDraggable: function(ddGroup) {
29494         if (ddGroup instanceof Array) {
29495                 Roo.each(ddGroup, this.setDraggable, this);
29496                 return;
29497         }
29498         if (this.dragZone) {
29499                 this.dragZone.addToGroup(ddGroup);
29500         } else {
29501                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
29502                                 containerScroll: true,
29503                                 ddGroup: ddGroup 
29504
29505                         });
29506 //                      Draggability implies selection. DragZone's mousedown selects the element.
29507                         if (!this.multiSelect) { this.singleSelect = true; }
29508
29509 //                      Wire the DragZone's handlers up to methods in *this*
29510                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
29511                 }
29512     },
29513
29514 /**     Specify from which ddGroup this DDView accepts drops. */
29515     setDroppable: function(ddGroup) {
29516         if (ddGroup instanceof Array) {
29517                 Roo.each(ddGroup, this.setDroppable, this);
29518                 return;
29519         }
29520         if (this.dropZone) {
29521                 this.dropZone.addToGroup(ddGroup);
29522         } else {
29523                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
29524                                 containerScroll: true,
29525                                 ddGroup: ddGroup
29526                         });
29527
29528 //                      Wire the DropZone's handlers up to methods in *this*
29529                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
29530                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
29531                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
29532                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
29533                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
29534                 }
29535     },
29536
29537 /**     Decide whether to drop above or below a View node. */
29538     getDropPoint : function(e, n, dd){
29539         if (n == this.el.dom) { return "above"; }
29540                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29541                 var c = t + (b - t) / 2;
29542                 var y = Roo.lib.Event.getPageY(e);
29543                 if(y <= c) {
29544                         return "above";
29545                 }else{
29546                         return "below";
29547                 }
29548     },
29549
29550     onNodeEnter : function(n, dd, e, data){
29551                 return false;
29552     },
29553     
29554     onNodeOver : function(n, dd, e, data){
29555                 var pt = this.getDropPoint(e, n, dd);
29556                 // set the insert point style on the target node
29557                 var dragElClass = this.dropNotAllowed;
29558                 if (pt) {
29559                         var targetElClass;
29560                         if (pt == "above"){
29561                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29562                                 targetElClass = "x-view-drag-insert-above";
29563                         } else {
29564                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29565                                 targetElClass = "x-view-drag-insert-below";
29566                         }
29567                         if (this.lastInsertClass != targetElClass){
29568                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29569                                 this.lastInsertClass = targetElClass;
29570                         }
29571                 }
29572                 return dragElClass;
29573         },
29574
29575     onNodeOut : function(n, dd, e, data){
29576                 this.removeDropIndicators(n);
29577     },
29578
29579     onNodeDrop : function(n, dd, e, data){
29580         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29581                 return false;
29582         }
29583         var pt = this.getDropPoint(e, n, dd);
29584                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29585                 if (pt == "below") { insertAt++; }
29586                 for (var i = 0; i < data.records.length; i++) {
29587                         var r = data.records[i];
29588                         var dup = this.store.getById(r.id);
29589                         if (dup && (dd != this.dragZone)) {
29590                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29591                         } else {
29592                                 if (data.copy) {
29593                                         this.store.insert(insertAt++, r.copy());
29594                                 } else {
29595                                         data.source.isDirtyFlag = true;
29596                                         r.store.remove(r);
29597                                         this.store.insert(insertAt++, r);
29598                                 }
29599                                 this.isDirtyFlag = true;
29600                         }
29601                 }
29602                 this.dragZone.cachedTarget = null;
29603                 return true;
29604     },
29605
29606     removeDropIndicators : function(n){
29607                 if(n){
29608                         Roo.fly(n).removeClass([
29609                                 "x-view-drag-insert-above",
29610                                 "x-view-drag-insert-below"]);
29611                         this.lastInsertClass = "_noclass";
29612                 }
29613     },
29614
29615 /**
29616  *      Utility method. Add a delete option to the DDView's context menu.
29617  *      @param {String} imageUrl The URL of the "delete" icon image.
29618  */
29619         setDeletable: function(imageUrl) {
29620                 if (!this.singleSelect && !this.multiSelect) {
29621                         this.singleSelect = true;
29622                 }
29623                 var c = this.getContextMenu();
29624                 this.contextMenu.on("itemclick", function(item) {
29625                         switch (item.id) {
29626                                 case "delete":
29627                                         this.remove(this.getSelectedIndexes());
29628                                         break;
29629                         }
29630                 }, this);
29631                 this.contextMenu.add({
29632                         icon: imageUrl,
29633                         id: "delete",
29634                         text: 'Delete'
29635                 });
29636         },
29637         
29638 /**     Return the context menu for this DDView. */
29639         getContextMenu: function() {
29640                 if (!this.contextMenu) {
29641 //                      Create the View's context menu
29642                         this.contextMenu = new Roo.menu.Menu({
29643                                 id: this.id + "-contextmenu"
29644                         });
29645                         this.el.on("contextmenu", this.showContextMenu, this);
29646                 }
29647                 return this.contextMenu;
29648         },
29649         
29650         disableContextMenu: function() {
29651                 if (this.contextMenu) {
29652                         this.el.un("contextmenu", this.showContextMenu, this);
29653                 }
29654         },
29655
29656         showContextMenu: function(e, item) {
29657         item = this.findItemFromChild(e.getTarget());
29658                 if (item) {
29659                         e.stopEvent();
29660                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29661                         this.contextMenu.showAt(e.getXY());
29662             }
29663     },
29664
29665 /**
29666  *      Remove {@link Roo.data.Record}s at the specified indices.
29667  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29668  */
29669     remove: function(selectedIndices) {
29670                 selectedIndices = [].concat(selectedIndices);
29671                 for (var i = 0; i < selectedIndices.length; i++) {
29672                         var rec = this.store.getAt(selectedIndices[i]);
29673                         this.store.remove(rec);
29674                 }
29675     },
29676
29677 /**
29678  *      Double click fires the event, but also, if this is draggable, and there is only one other
29679  *      related DropZone, it transfers the selected node.
29680  */
29681     onDblClick : function(e){
29682         var item = this.findItemFromChild(e.getTarget());
29683         if(item){
29684             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29685                 return false;
29686             }
29687             if (this.dragGroup) {
29688                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29689                     while (targets.indexOf(this.dropZone) > -1) {
29690                             targets.remove(this.dropZone);
29691                                 }
29692                     if (targets.length == 1) {
29693                                         this.dragZone.cachedTarget = null;
29694                         var el = Roo.get(targets[0].getEl());
29695                         var box = el.getBox(true);
29696                         targets[0].onNodeDrop(el.dom, {
29697                                 target: el.dom,
29698                                 xy: [box.x, box.y + box.height - 1]
29699                         }, null, this.getDragData(e));
29700                     }
29701                 }
29702         }
29703     },
29704     
29705     handleSelection: function(e) {
29706                 this.dragZone.cachedTarget = null;
29707         var item = this.findItemFromChild(e.getTarget());
29708         if (!item) {
29709                 this.clearSelections(true);
29710                 return;
29711         }
29712                 if (item && (this.multiSelect || this.singleSelect)){
29713                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29714                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29715                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29716                                 this.unselect(item);
29717                         } else {
29718                                 this.select(item, this.multiSelect && e.ctrlKey);
29719                                 this.lastSelection = item;
29720                         }
29721                 }
29722     },
29723
29724     onItemClick : function(item, index, e){
29725                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29726                         return false;
29727                 }
29728                 return true;
29729     },
29730
29731     unselect : function(nodeInfo, suppressEvent){
29732                 var node = this.getNode(nodeInfo);
29733                 if(node && this.isSelected(node)){
29734                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29735                                 Roo.fly(node).removeClass(this.selectedClass);
29736                                 this.selections.remove(node);
29737                                 if(!suppressEvent){
29738                                         this.fireEvent("selectionchange", this, this.selections);
29739                                 }
29740                         }
29741                 }
29742     }
29743 });
29744 /*
29745  * Based on:
29746  * Ext JS Library 1.1.1
29747  * Copyright(c) 2006-2007, Ext JS, LLC.
29748  *
29749  * Originally Released Under LGPL - original licence link has changed is not relivant.
29750  *
29751  * Fork - LGPL
29752  * <script type="text/javascript">
29753  */
29754  
29755 /**
29756  * @class Roo.LayoutManager
29757  * @extends Roo.util.Observable
29758  * Base class for layout managers.
29759  */
29760 Roo.LayoutManager = function(container, config){
29761     Roo.LayoutManager.superclass.constructor.call(this);
29762     this.el = Roo.get(container);
29763     // ie scrollbar fix
29764     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29765         document.body.scroll = "no";
29766     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29767         this.el.position('relative');
29768     }
29769     this.id = this.el.id;
29770     this.el.addClass("x-layout-container");
29771     /** false to disable window resize monitoring @type Boolean */
29772     this.monitorWindowResize = true;
29773     this.regions = {};
29774     this.addEvents({
29775         /**
29776          * @event layout
29777          * Fires when a layout is performed. 
29778          * @param {Roo.LayoutManager} this
29779          */
29780         "layout" : true,
29781         /**
29782          * @event regionresized
29783          * Fires when the user resizes a region. 
29784          * @param {Roo.LayoutRegion} region The resized region
29785          * @param {Number} newSize The new size (width for east/west, height for north/south)
29786          */
29787         "regionresized" : true,
29788         /**
29789          * @event regioncollapsed
29790          * Fires when a region is collapsed. 
29791          * @param {Roo.LayoutRegion} region The collapsed region
29792          */
29793         "regioncollapsed" : true,
29794         /**
29795          * @event regionexpanded
29796          * Fires when a region is expanded.  
29797          * @param {Roo.LayoutRegion} region The expanded region
29798          */
29799         "regionexpanded" : true
29800     });
29801     this.updating = false;
29802     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29803 };
29804
29805 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29806     /**
29807      * Returns true if this layout is currently being updated
29808      * @return {Boolean}
29809      */
29810     isUpdating : function(){
29811         return this.updating; 
29812     },
29813     
29814     /**
29815      * Suspend the LayoutManager from doing auto-layouts while
29816      * making multiple add or remove calls
29817      */
29818     beginUpdate : function(){
29819         this.updating = true;    
29820     },
29821     
29822     /**
29823      * Restore auto-layouts and optionally disable the manager from performing a layout
29824      * @param {Boolean} noLayout true to disable a layout update 
29825      */
29826     endUpdate : function(noLayout){
29827         this.updating = false;
29828         if(!noLayout){
29829             this.layout();
29830         }    
29831     },
29832     
29833     layout: function(){
29834         
29835     },
29836     
29837     onRegionResized : function(region, newSize){
29838         this.fireEvent("regionresized", region, newSize);
29839         this.layout();
29840     },
29841     
29842     onRegionCollapsed : function(region){
29843         this.fireEvent("regioncollapsed", region);
29844     },
29845     
29846     onRegionExpanded : function(region){
29847         this.fireEvent("regionexpanded", region);
29848     },
29849         
29850     /**
29851      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29852      * performs box-model adjustments.
29853      * @return {Object} The size as an object {width: (the width), height: (the height)}
29854      */
29855     getViewSize : function(){
29856         var size;
29857         if(this.el.dom != document.body){
29858             size = this.el.getSize();
29859         }else{
29860             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29861         }
29862         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29863         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29864         return size;
29865     },
29866     
29867     /**
29868      * Returns the Element this layout is bound to.
29869      * @return {Roo.Element}
29870      */
29871     getEl : function(){
29872         return this.el;
29873     },
29874     
29875     /**
29876      * Returns the specified region.
29877      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29878      * @return {Roo.LayoutRegion}
29879      */
29880     getRegion : function(target){
29881         return this.regions[target.toLowerCase()];
29882     },
29883     
29884     onWindowResize : function(){
29885         if(this.monitorWindowResize){
29886             this.layout();
29887         }
29888     }
29889 });/*
29890  * Based on:
29891  * Ext JS Library 1.1.1
29892  * Copyright(c) 2006-2007, Ext JS, LLC.
29893  *
29894  * Originally Released Under LGPL - original licence link has changed is not relivant.
29895  *
29896  * Fork - LGPL
29897  * <script type="text/javascript">
29898  */
29899 /**
29900  * @class Roo.BorderLayout
29901  * @extends Roo.LayoutManager
29902  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29903  * please see: <br><br>
29904  * <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>
29905  * <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>
29906  * Example:
29907  <pre><code>
29908  var layout = new Roo.BorderLayout(document.body, {
29909     north: {
29910         initialSize: 25,
29911         titlebar: false
29912     },
29913     west: {
29914         split:true,
29915         initialSize: 200,
29916         minSize: 175,
29917         maxSize: 400,
29918         titlebar: true,
29919         collapsible: true
29920     },
29921     east: {
29922         split:true,
29923         initialSize: 202,
29924         minSize: 175,
29925         maxSize: 400,
29926         titlebar: true,
29927         collapsible: true
29928     },
29929     south: {
29930         split:true,
29931         initialSize: 100,
29932         minSize: 100,
29933         maxSize: 200,
29934         titlebar: true,
29935         collapsible: true
29936     },
29937     center: {
29938         titlebar: true,
29939         autoScroll:true,
29940         resizeTabs: true,
29941         minTabWidth: 50,
29942         preferredTabWidth: 150
29943     }
29944 });
29945
29946 // shorthand
29947 var CP = Roo.ContentPanel;
29948
29949 layout.beginUpdate();
29950 layout.add("north", new CP("north", "North"));
29951 layout.add("south", new CP("south", {title: "South", closable: true}));
29952 layout.add("west", new CP("west", {title: "West"}));
29953 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29954 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29955 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29956 layout.getRegion("center").showPanel("center1");
29957 layout.endUpdate();
29958 </code></pre>
29959
29960 <b>The container the layout is rendered into can be either the body element or any other element.
29961 If it is not the body element, the container needs to either be an absolute positioned element,
29962 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29963 the container size if it is not the body element.</b>
29964
29965 * @constructor
29966 * Create a new BorderLayout
29967 * @param {String/HTMLElement/Element} container The container this layout is bound to
29968 * @param {Object} config Configuration options
29969  */
29970 Roo.BorderLayout = function(container, config){
29971     config = config || {};
29972     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29973     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29974     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29975         var target = this.factory.validRegions[i];
29976         if(config[target]){
29977             this.addRegion(target, config[target]);
29978         }
29979     }
29980 };
29981
29982 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29983     /**
29984      * Creates and adds a new region if it doesn't already exist.
29985      * @param {String} target The target region key (north, south, east, west or center).
29986      * @param {Object} config The regions config object
29987      * @return {BorderLayoutRegion} The new region
29988      */
29989     addRegion : function(target, config){
29990         if(!this.regions[target]){
29991             var r = this.factory.create(target, this, config);
29992             this.bindRegion(target, r);
29993         }
29994         return this.regions[target];
29995     },
29996
29997     // private (kinda)
29998     bindRegion : function(name, r){
29999         this.regions[name] = r;
30000         r.on("visibilitychange", this.layout, this);
30001         r.on("paneladded", this.layout, this);
30002         r.on("panelremoved", this.layout, this);
30003         r.on("invalidated", this.layout, this);
30004         r.on("resized", this.onRegionResized, this);
30005         r.on("collapsed", this.onRegionCollapsed, this);
30006         r.on("expanded", this.onRegionExpanded, this);
30007     },
30008
30009     /**
30010      * Performs a layout update.
30011      */
30012     layout : function(){
30013         if(this.updating) return;
30014         var size = this.getViewSize();
30015         var w = size.width;
30016         var h = size.height;
30017         var centerW = w;
30018         var centerH = h;
30019         var centerY = 0;
30020         var centerX = 0;
30021         //var x = 0, y = 0;
30022
30023         var rs = this.regions;
30024         var north = rs["north"];
30025         var south = rs["south"]; 
30026         var west = rs["west"];
30027         var east = rs["east"];
30028         var center = rs["center"];
30029         //if(this.hideOnLayout){ // not supported anymore
30030             //c.el.setStyle("display", "none");
30031         //}
30032         if(north && north.isVisible()){
30033             var b = north.getBox();
30034             var m = north.getMargins();
30035             b.width = w - (m.left+m.right);
30036             b.x = m.left;
30037             b.y = m.top;
30038             centerY = b.height + b.y + m.bottom;
30039             centerH -= centerY;
30040             north.updateBox(this.safeBox(b));
30041         }
30042         if(south && south.isVisible()){
30043             var b = south.getBox();
30044             var m = south.getMargins();
30045             b.width = w - (m.left+m.right);
30046             b.x = m.left;
30047             var totalHeight = (b.height + m.top + m.bottom);
30048             b.y = h - totalHeight + m.top;
30049             centerH -= totalHeight;
30050             south.updateBox(this.safeBox(b));
30051         }
30052         if(west && west.isVisible()){
30053             var b = west.getBox();
30054             var m = west.getMargins();
30055             b.height = centerH - (m.top+m.bottom);
30056             b.x = m.left;
30057             b.y = centerY + m.top;
30058             var totalWidth = (b.width + m.left + m.right);
30059             centerX += totalWidth;
30060             centerW -= totalWidth;
30061             west.updateBox(this.safeBox(b));
30062         }
30063         if(east && east.isVisible()){
30064             var b = east.getBox();
30065             var m = east.getMargins();
30066             b.height = centerH - (m.top+m.bottom);
30067             var totalWidth = (b.width + m.left + m.right);
30068             b.x = w - totalWidth + m.left;
30069             b.y = centerY + m.top;
30070             centerW -= totalWidth;
30071             east.updateBox(this.safeBox(b));
30072         }
30073         if(center){
30074             var m = center.getMargins();
30075             var centerBox = {
30076                 x: centerX + m.left,
30077                 y: centerY + m.top,
30078                 width: centerW - (m.left+m.right),
30079                 height: centerH - (m.top+m.bottom)
30080             };
30081             //if(this.hideOnLayout){
30082                 //center.el.setStyle("display", "block");
30083             //}
30084             center.updateBox(this.safeBox(centerBox));
30085         }
30086         this.el.repaint();
30087         this.fireEvent("layout", this);
30088     },
30089
30090     // private
30091     safeBox : function(box){
30092         box.width = Math.max(0, box.width);
30093         box.height = Math.max(0, box.height);
30094         return box;
30095     },
30096
30097     /**
30098      * Adds a ContentPanel (or subclass) to this layout.
30099      * @param {String} target The target region key (north, south, east, west or center).
30100      * @param {Roo.ContentPanel} panel The panel to add
30101      * @return {Roo.ContentPanel} The added panel
30102      */
30103     add : function(target, panel){
30104          
30105         target = target.toLowerCase();
30106         return this.regions[target].add(panel);
30107     },
30108
30109     /**
30110      * Remove a ContentPanel (or subclass) to this layout.
30111      * @param {String} target The target region key (north, south, east, west or center).
30112      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30113      * @return {Roo.ContentPanel} The removed panel
30114      */
30115     remove : function(target, panel){
30116         target = target.toLowerCase();
30117         return this.regions[target].remove(panel);
30118     },
30119
30120     /**
30121      * Searches all regions for a panel with the specified id
30122      * @param {String} panelId
30123      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30124      */
30125     findPanel : function(panelId){
30126         var rs = this.regions;
30127         for(var target in rs){
30128             if(typeof rs[target] != "function"){
30129                 var p = rs[target].getPanel(panelId);
30130                 if(p){
30131                     return p;
30132                 }
30133             }
30134         }
30135         return null;
30136     },
30137
30138     /**
30139      * Searches all regions for a panel with the specified id and activates (shows) it.
30140      * @param {String/ContentPanel} panelId The panels id or the panel itself
30141      * @return {Roo.ContentPanel} The shown panel or null
30142      */
30143     showPanel : function(panelId) {
30144       var rs = this.regions;
30145       for(var target in rs){
30146          var r = rs[target];
30147          if(typeof r != "function"){
30148             if(r.hasPanel(panelId)){
30149                return r.showPanel(panelId);
30150             }
30151          }
30152       }
30153       return null;
30154    },
30155
30156    /**
30157      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30158      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30159      */
30160     restoreState : function(provider){
30161         if(!provider){
30162             provider = Roo.state.Manager;
30163         }
30164         var sm = new Roo.LayoutStateManager();
30165         sm.init(this, provider);
30166     },
30167
30168     /**
30169      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30170      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30171      * a valid ContentPanel config object.  Example:
30172      * <pre><code>
30173 // Create the main layout
30174 var layout = new Roo.BorderLayout('main-ct', {
30175     west: {
30176         split:true,
30177         minSize: 175,
30178         titlebar: true
30179     },
30180     center: {
30181         title:'Components'
30182     }
30183 }, 'main-ct');
30184
30185 // Create and add multiple ContentPanels at once via configs
30186 layout.batchAdd({
30187    west: {
30188        id: 'source-files',
30189        autoCreate:true,
30190        title:'Ext Source Files',
30191        autoScroll:true,
30192        fitToFrame:true
30193    },
30194    center : {
30195        el: cview,
30196        autoScroll:true,
30197        fitToFrame:true,
30198        toolbar: tb,
30199        resizeEl:'cbody'
30200    }
30201 });
30202 </code></pre>
30203      * @param {Object} regions An object containing ContentPanel configs by region name
30204      */
30205     batchAdd : function(regions){
30206         this.beginUpdate();
30207         for(var rname in regions){
30208             var lr = this.regions[rname];
30209             if(lr){
30210                 this.addTypedPanels(lr, regions[rname]);
30211             }
30212         }
30213         this.endUpdate();
30214     },
30215
30216     // private
30217     addTypedPanels : function(lr, ps){
30218         if(typeof ps == 'string'){
30219             lr.add(new Roo.ContentPanel(ps));
30220         }
30221         else if(ps instanceof Array){
30222             for(var i =0, len = ps.length; i < len; i++){
30223                 this.addTypedPanels(lr, ps[i]);
30224             }
30225         }
30226         else if(!ps.events){ // raw config?
30227             var el = ps.el;
30228             delete ps.el; // prevent conflict
30229             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30230         }
30231         else {  // panel object assumed!
30232             lr.add(ps);
30233         }
30234     },
30235     /**
30236      * Adds a xtype elements to the layout.
30237      * <pre><code>
30238
30239 layout.addxtype({
30240        xtype : 'ContentPanel',
30241        region: 'west',
30242        items: [ .... ]
30243    }
30244 );
30245
30246 layout.addxtype({
30247         xtype : 'NestedLayoutPanel',
30248         region: 'west',
30249         layout: {
30250            center: { },
30251            west: { }   
30252         },
30253         items : [ ... list of content panels or nested layout panels.. ]
30254    }
30255 );
30256 </code></pre>
30257      * @param {Object} cfg Xtype definition of item to add.
30258      */
30259     addxtype : function(cfg)
30260     {
30261         // basically accepts a pannel...
30262         // can accept a layout region..!?!?
30263         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30264         
30265         if (!cfg.xtype.match(/Panel$/)) {
30266             return false;
30267         }
30268         var ret = false;
30269         
30270         if (typeof(cfg.region) == 'undefined') {
30271             Roo.log("Failed to add Panel, region was not set");
30272             Roo.log(cfg);
30273             return false;
30274         }
30275         var region = cfg.region;
30276         delete cfg.region;
30277         
30278           
30279         var xitems = [];
30280         if (cfg.items) {
30281             xitems = cfg.items;
30282             delete cfg.items;
30283         }
30284         var nb = false;
30285         
30286         switch(cfg.xtype) 
30287         {
30288             case 'ContentPanel':  // ContentPanel (el, cfg)
30289             case 'ScrollPanel':  // ContentPanel (el, cfg)
30290                 if(cfg.autoCreate) {
30291                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30292                 } else {
30293                     var el = this.el.createChild();
30294                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30295                 }
30296                 
30297                 this.add(region, ret);
30298                 break;
30299             
30300             
30301             case 'TreePanel': // our new panel!
30302                 cfg.el = this.el.createChild();
30303                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30304                 this.add(region, ret);
30305                 break;
30306             
30307             case 'NestedLayoutPanel': 
30308                 // create a new Layout (which is  a Border Layout...
30309                 var el = this.el.createChild();
30310                 var clayout = cfg.layout;
30311                 delete cfg.layout;
30312                 clayout.items   = clayout.items  || [];
30313                 // replace this exitems with the clayout ones..
30314                 xitems = clayout.items;
30315                  
30316                 
30317                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30318                     cfg.background = false;
30319                 }
30320                 var layout = new Roo.BorderLayout(el, clayout);
30321                 
30322                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30323                 //console.log('adding nested layout panel '  + cfg.toSource());
30324                 this.add(region, ret);
30325                 nb = {}; /// find first...
30326                 break;
30327                 
30328             case 'GridPanel': 
30329             
30330                 // needs grid and region
30331                 
30332                 //var el = this.getRegion(region).el.createChild();
30333                 var el = this.el.createChild();
30334                 // create the grid first...
30335                 
30336                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30337                 delete cfg.grid;
30338                 if (region == 'center' && this.active ) {
30339                     cfg.background = false;
30340                 }
30341                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30342                 
30343                 this.add(region, ret);
30344                 if (cfg.background) {
30345                     ret.on('activate', function(gp) {
30346                         if (!gp.grid.rendered) {
30347                             gp.grid.render();
30348                         }
30349                     });
30350                 } else {
30351                     grid.render();
30352                 }
30353                 break;
30354            
30355                
30356                 
30357                 
30358             default: 
30359                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30360                 return null;
30361              // GridPanel (grid, cfg)
30362             
30363         }
30364         this.beginUpdate();
30365         // add children..
30366         var region = '';
30367         var abn = {};
30368         Roo.each(xitems, function(i)  {
30369             region = nb && i.region ? i.region : false;
30370             
30371             var add = ret.addxtype(i);
30372            
30373             if (region) {
30374                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
30375                 if (!i.background) {
30376                     abn[region] = nb[region] ;
30377                 }
30378             }
30379             
30380         });
30381         this.endUpdate();
30382
30383         // make the last non-background panel active..
30384         //if (nb) { Roo.log(abn); }
30385         if (nb) {
30386             
30387             for(var r in abn) {
30388                 region = this.getRegion(r);
30389                 if (region) {
30390                     // tried using nb[r], but it does not work..
30391                      
30392                     region.showPanel(abn[r]);
30393                    
30394                 }
30395             }
30396         }
30397         return ret;
30398         
30399     }
30400 });
30401
30402 /**
30403  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
30404  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
30405  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
30406  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
30407  * <pre><code>
30408 // shorthand
30409 var CP = Roo.ContentPanel;
30410
30411 var layout = Roo.BorderLayout.create({
30412     north: {
30413         initialSize: 25,
30414         titlebar: false,
30415         panels: [new CP("north", "North")]
30416     },
30417     west: {
30418         split:true,
30419         initialSize: 200,
30420         minSize: 175,
30421         maxSize: 400,
30422         titlebar: true,
30423         collapsible: true,
30424         panels: [new CP("west", {title: "West"})]
30425     },
30426     east: {
30427         split:true,
30428         initialSize: 202,
30429         minSize: 175,
30430         maxSize: 400,
30431         titlebar: true,
30432         collapsible: true,
30433         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
30434     },
30435     south: {
30436         split:true,
30437         initialSize: 100,
30438         minSize: 100,
30439         maxSize: 200,
30440         titlebar: true,
30441         collapsible: true,
30442         panels: [new CP("south", {title: "South", closable: true})]
30443     },
30444     center: {
30445         titlebar: true,
30446         autoScroll:true,
30447         resizeTabs: true,
30448         minTabWidth: 50,
30449         preferredTabWidth: 150,
30450         panels: [
30451             new CP("center1", {title: "Close Me", closable: true}),
30452             new CP("center2", {title: "Center Panel", closable: false})
30453         ]
30454     }
30455 }, document.body);
30456
30457 layout.getRegion("center").showPanel("center1");
30458 </code></pre>
30459  * @param config
30460  * @param targetEl
30461  */
30462 Roo.BorderLayout.create = function(config, targetEl){
30463     var layout = new Roo.BorderLayout(targetEl || document.body, config);
30464     layout.beginUpdate();
30465     var regions = Roo.BorderLayout.RegionFactory.validRegions;
30466     for(var j = 0, jlen = regions.length; j < jlen; j++){
30467         var lr = regions[j];
30468         if(layout.regions[lr] && config[lr].panels){
30469             var r = layout.regions[lr];
30470             var ps = config[lr].panels;
30471             layout.addTypedPanels(r, ps);
30472         }
30473     }
30474     layout.endUpdate();
30475     return layout;
30476 };
30477
30478 // private
30479 Roo.BorderLayout.RegionFactory = {
30480     // private
30481     validRegions : ["north","south","east","west","center"],
30482
30483     // private
30484     create : function(target, mgr, config){
30485         target = target.toLowerCase();
30486         if(config.lightweight || config.basic){
30487             return new Roo.BasicLayoutRegion(mgr, config, target);
30488         }
30489         switch(target){
30490             case "north":
30491                 return new Roo.NorthLayoutRegion(mgr, config);
30492             case "south":
30493                 return new Roo.SouthLayoutRegion(mgr, config);
30494             case "east":
30495                 return new Roo.EastLayoutRegion(mgr, config);
30496             case "west":
30497                 return new Roo.WestLayoutRegion(mgr, config);
30498             case "center":
30499                 return new Roo.CenterLayoutRegion(mgr, config);
30500         }
30501         throw 'Layout region "'+target+'" not supported.';
30502     }
30503 };/*
30504  * Based on:
30505  * Ext JS Library 1.1.1
30506  * Copyright(c) 2006-2007, Ext JS, LLC.
30507  *
30508  * Originally Released Under LGPL - original licence link has changed is not relivant.
30509  *
30510  * Fork - LGPL
30511  * <script type="text/javascript">
30512  */
30513  
30514 /**
30515  * @class Roo.BasicLayoutRegion
30516  * @extends Roo.util.Observable
30517  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
30518  * and does not have a titlebar, tabs or any other features. All it does is size and position 
30519  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
30520  */
30521 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
30522     this.mgr = mgr;
30523     this.position  = pos;
30524     this.events = {
30525         /**
30526          * @scope Roo.BasicLayoutRegion
30527          */
30528         
30529         /**
30530          * @event beforeremove
30531          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30532          * @param {Roo.LayoutRegion} this
30533          * @param {Roo.ContentPanel} panel The panel
30534          * @param {Object} e The cancel event object
30535          */
30536         "beforeremove" : true,
30537         /**
30538          * @event invalidated
30539          * Fires when the layout for this region is changed.
30540          * @param {Roo.LayoutRegion} this
30541          */
30542         "invalidated" : true,
30543         /**
30544          * @event visibilitychange
30545          * Fires when this region is shown or hidden 
30546          * @param {Roo.LayoutRegion} this
30547          * @param {Boolean} visibility true or false
30548          */
30549         "visibilitychange" : true,
30550         /**
30551          * @event paneladded
30552          * Fires when a panel is added. 
30553          * @param {Roo.LayoutRegion} this
30554          * @param {Roo.ContentPanel} panel The panel
30555          */
30556         "paneladded" : true,
30557         /**
30558          * @event panelremoved
30559          * Fires when a panel is removed. 
30560          * @param {Roo.LayoutRegion} this
30561          * @param {Roo.ContentPanel} panel The panel
30562          */
30563         "panelremoved" : true,
30564         /**
30565          * @event collapsed
30566          * Fires when this region is collapsed.
30567          * @param {Roo.LayoutRegion} this
30568          */
30569         "collapsed" : true,
30570         /**
30571          * @event expanded
30572          * Fires when this region is expanded.
30573          * @param {Roo.LayoutRegion} this
30574          */
30575         "expanded" : true,
30576         /**
30577          * @event slideshow
30578          * Fires when this region is slid into view.
30579          * @param {Roo.LayoutRegion} this
30580          */
30581         "slideshow" : true,
30582         /**
30583          * @event slidehide
30584          * Fires when this region slides out of view. 
30585          * @param {Roo.LayoutRegion} this
30586          */
30587         "slidehide" : true,
30588         /**
30589          * @event panelactivated
30590          * Fires when a panel is activated. 
30591          * @param {Roo.LayoutRegion} this
30592          * @param {Roo.ContentPanel} panel The activated panel
30593          */
30594         "panelactivated" : true,
30595         /**
30596          * @event resized
30597          * Fires when the user resizes this region. 
30598          * @param {Roo.LayoutRegion} this
30599          * @param {Number} newSize The new size (width for east/west, height for north/south)
30600          */
30601         "resized" : true
30602     };
30603     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30604     this.panels = new Roo.util.MixedCollection();
30605     this.panels.getKey = this.getPanelId.createDelegate(this);
30606     this.box = null;
30607     this.activePanel = null;
30608     // ensure listeners are added...
30609     
30610     if (config.listeners || config.events) {
30611         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30612             listeners : config.listeners || {},
30613             events : config.events || {}
30614         });
30615     }
30616     
30617     if(skipConfig !== true){
30618         this.applyConfig(config);
30619     }
30620 };
30621
30622 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30623     getPanelId : function(p){
30624         return p.getId();
30625     },
30626     
30627     applyConfig : function(config){
30628         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30629         this.config = config;
30630         
30631     },
30632     
30633     /**
30634      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30635      * the width, for horizontal (north, south) the height.
30636      * @param {Number} newSize The new width or height
30637      */
30638     resizeTo : function(newSize){
30639         var el = this.el ? this.el :
30640                  (this.activePanel ? this.activePanel.getEl() : null);
30641         if(el){
30642             switch(this.position){
30643                 case "east":
30644                 case "west":
30645                     el.setWidth(newSize);
30646                     this.fireEvent("resized", this, newSize);
30647                 break;
30648                 case "north":
30649                 case "south":
30650                     el.setHeight(newSize);
30651                     this.fireEvent("resized", this, newSize);
30652                 break;                
30653             }
30654         }
30655     },
30656     
30657     getBox : function(){
30658         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30659     },
30660     
30661     getMargins : function(){
30662         return this.margins;
30663     },
30664     
30665     updateBox : function(box){
30666         this.box = box;
30667         var el = this.activePanel.getEl();
30668         el.dom.style.left = box.x + "px";
30669         el.dom.style.top = box.y + "px";
30670         this.activePanel.setSize(box.width, box.height);
30671     },
30672     
30673     /**
30674      * Returns the container element for this region.
30675      * @return {Roo.Element}
30676      */
30677     getEl : function(){
30678         return this.activePanel;
30679     },
30680     
30681     /**
30682      * Returns true if this region is currently visible.
30683      * @return {Boolean}
30684      */
30685     isVisible : function(){
30686         return this.activePanel ? true : false;
30687     },
30688     
30689     setActivePanel : function(panel){
30690         panel = this.getPanel(panel);
30691         if(this.activePanel && this.activePanel != panel){
30692             this.activePanel.setActiveState(false);
30693             this.activePanel.getEl().setLeftTop(-10000,-10000);
30694         }
30695         this.activePanel = panel;
30696         panel.setActiveState(true);
30697         if(this.box){
30698             panel.setSize(this.box.width, this.box.height);
30699         }
30700         this.fireEvent("panelactivated", this, panel);
30701         this.fireEvent("invalidated");
30702     },
30703     
30704     /**
30705      * Show the specified panel.
30706      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30707      * @return {Roo.ContentPanel} The shown panel or null
30708      */
30709     showPanel : function(panel){
30710         if(panel = this.getPanel(panel)){
30711             this.setActivePanel(panel);
30712         }
30713         return panel;
30714     },
30715     
30716     /**
30717      * Get the active panel for this region.
30718      * @return {Roo.ContentPanel} The active panel or null
30719      */
30720     getActivePanel : function(){
30721         return this.activePanel;
30722     },
30723     
30724     /**
30725      * Add the passed ContentPanel(s)
30726      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30727      * @return {Roo.ContentPanel} The panel added (if only one was added)
30728      */
30729     add : function(panel){
30730         if(arguments.length > 1){
30731             for(var i = 0, len = arguments.length; i < len; i++) {
30732                 this.add(arguments[i]);
30733             }
30734             return null;
30735         }
30736         if(this.hasPanel(panel)){
30737             this.showPanel(panel);
30738             return panel;
30739         }
30740         var el = panel.getEl();
30741         if(el.dom.parentNode != this.mgr.el.dom){
30742             this.mgr.el.dom.appendChild(el.dom);
30743         }
30744         if(panel.setRegion){
30745             panel.setRegion(this);
30746         }
30747         this.panels.add(panel);
30748         el.setStyle("position", "absolute");
30749         if(!panel.background){
30750             this.setActivePanel(panel);
30751             if(this.config.initialSize && this.panels.getCount()==1){
30752                 this.resizeTo(this.config.initialSize);
30753             }
30754         }
30755         this.fireEvent("paneladded", this, panel);
30756         return panel;
30757     },
30758     
30759     /**
30760      * Returns true if the panel is in this region.
30761      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30762      * @return {Boolean}
30763      */
30764     hasPanel : function(panel){
30765         if(typeof panel == "object"){ // must be panel obj
30766             panel = panel.getId();
30767         }
30768         return this.getPanel(panel) ? true : false;
30769     },
30770     
30771     /**
30772      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30773      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30774      * @param {Boolean} preservePanel Overrides the config preservePanel option
30775      * @return {Roo.ContentPanel} The panel that was removed
30776      */
30777     remove : function(panel, preservePanel){
30778         panel = this.getPanel(panel);
30779         if(!panel){
30780             return null;
30781         }
30782         var e = {};
30783         this.fireEvent("beforeremove", this, panel, e);
30784         if(e.cancel === true){
30785             return null;
30786         }
30787         var panelId = panel.getId();
30788         this.panels.removeKey(panelId);
30789         return panel;
30790     },
30791     
30792     /**
30793      * Returns the panel specified or null if it's not in this region.
30794      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30795      * @return {Roo.ContentPanel}
30796      */
30797     getPanel : function(id){
30798         if(typeof id == "object"){ // must be panel obj
30799             return id;
30800         }
30801         return this.panels.get(id);
30802     },
30803     
30804     /**
30805      * Returns this regions position (north/south/east/west/center).
30806      * @return {String} 
30807      */
30808     getPosition: function(){
30809         return this.position;    
30810     }
30811 });/*
30812  * Based on:
30813  * Ext JS Library 1.1.1
30814  * Copyright(c) 2006-2007, Ext JS, LLC.
30815  *
30816  * Originally Released Under LGPL - original licence link has changed is not relivant.
30817  *
30818  * Fork - LGPL
30819  * <script type="text/javascript">
30820  */
30821  
30822 /**
30823  * @class Roo.LayoutRegion
30824  * @extends Roo.BasicLayoutRegion
30825  * This class represents a region in a layout manager.
30826  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30827  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30828  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30829  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30830  * @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})
30831  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
30832  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30833  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30834  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30835  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30836  * @cfg {String}    title           The title for the region (overrides panel titles)
30837  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30838  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30839  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30840  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30841  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30842  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30843  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30844  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30845  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30846  * @cfg {Boolean}   showPin         True to show a pin button
30847  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30848  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30849  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30850  * @cfg {Number}    width           For East/West panels
30851  * @cfg {Number}    height          For North/South panels
30852  * @cfg {Boolean}   split           To show the splitter
30853  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30854  */
30855 Roo.LayoutRegion = function(mgr, config, pos){
30856     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30857     var dh = Roo.DomHelper;
30858     /** This region's container element 
30859     * @type Roo.Element */
30860     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30861     /** This region's title element 
30862     * @type Roo.Element */
30863
30864     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30865         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30866         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30867     ]}, true);
30868     this.titleEl.enableDisplayMode();
30869     /** This region's title text element 
30870     * @type HTMLElement */
30871     this.titleTextEl = this.titleEl.dom.firstChild;
30872     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30873     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30874     this.closeBtn.enableDisplayMode();
30875     this.closeBtn.on("click", this.closeClicked, this);
30876     this.closeBtn.hide();
30877
30878     this.createBody(config);
30879     this.visible = true;
30880     this.collapsed = false;
30881
30882     if(config.hideWhenEmpty){
30883         this.hide();
30884         this.on("paneladded", this.validateVisibility, this);
30885         this.on("panelremoved", this.validateVisibility, this);
30886     }
30887     this.applyConfig(config);
30888 };
30889
30890 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30891
30892     createBody : function(){
30893         /** This region's body element 
30894         * @type Roo.Element */
30895         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30896     },
30897
30898     applyConfig : function(c){
30899         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30900             var dh = Roo.DomHelper;
30901             if(c.titlebar !== false){
30902                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30903                 this.collapseBtn.on("click", this.collapse, this);
30904                 this.collapseBtn.enableDisplayMode();
30905
30906                 if(c.showPin === true || this.showPin){
30907                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30908                     this.stickBtn.enableDisplayMode();
30909                     this.stickBtn.on("click", this.expand, this);
30910                     this.stickBtn.hide();
30911                 }
30912             }
30913             /** This region's collapsed element
30914             * @type Roo.Element */
30915             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30916                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30917             ]}, true);
30918             if(c.floatable !== false){
30919                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30920                this.collapsedEl.on("click", this.collapseClick, this);
30921             }
30922
30923             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30924                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30925                    id: "message", unselectable: "on", style:{"float":"left"}});
30926                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30927              }
30928             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30929             this.expandBtn.on("click", this.expand, this);
30930         }
30931         if(this.collapseBtn){
30932             this.collapseBtn.setVisible(c.collapsible == true);
30933         }
30934         this.cmargins = c.cmargins || this.cmargins ||
30935                          (this.position == "west" || this.position == "east" ?
30936                              {top: 0, left: 2, right:2, bottom: 0} :
30937                              {top: 2, left: 0, right:0, bottom: 2});
30938         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30939         this.bottomTabs = c.tabPosition != "top";
30940         this.autoScroll = c.autoScroll || false;
30941         if(this.autoScroll){
30942             this.bodyEl.setStyle("overflow", "auto");
30943         }else{
30944             this.bodyEl.setStyle("overflow", "hidden");
30945         }
30946         //if(c.titlebar !== false){
30947             if((!c.titlebar && !c.title) || c.titlebar === false){
30948                 this.titleEl.hide();
30949             }else{
30950                 this.titleEl.show();
30951                 if(c.title){
30952                     this.titleTextEl.innerHTML = c.title;
30953                 }
30954             }
30955         //}
30956         this.duration = c.duration || .30;
30957         this.slideDuration = c.slideDuration || .45;
30958         this.config = c;
30959         if(c.collapsed){
30960             this.collapse(true);
30961         }
30962         if(c.hidden){
30963             this.hide();
30964         }
30965     },
30966     /**
30967      * Returns true if this region is currently visible.
30968      * @return {Boolean}
30969      */
30970     isVisible : function(){
30971         return this.visible;
30972     },
30973
30974     /**
30975      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30976      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30977      */
30978     setCollapsedTitle : function(title){
30979         title = title || "&#160;";
30980         if(this.collapsedTitleTextEl){
30981             this.collapsedTitleTextEl.innerHTML = title;
30982         }
30983     },
30984
30985     getBox : function(){
30986         var b;
30987         if(!this.collapsed){
30988             b = this.el.getBox(false, true);
30989         }else{
30990             b = this.collapsedEl.getBox(false, true);
30991         }
30992         return b;
30993     },
30994
30995     getMargins : function(){
30996         return this.collapsed ? this.cmargins : this.margins;
30997     },
30998
30999     highlight : function(){
31000         this.el.addClass("x-layout-panel-dragover");
31001     },
31002
31003     unhighlight : function(){
31004         this.el.removeClass("x-layout-panel-dragover");
31005     },
31006
31007     updateBox : function(box){
31008         this.box = box;
31009         if(!this.collapsed){
31010             this.el.dom.style.left = box.x + "px";
31011             this.el.dom.style.top = box.y + "px";
31012             this.updateBody(box.width, box.height);
31013         }else{
31014             this.collapsedEl.dom.style.left = box.x + "px";
31015             this.collapsedEl.dom.style.top = box.y + "px";
31016             this.collapsedEl.setSize(box.width, box.height);
31017         }
31018         if(this.tabs){
31019             this.tabs.autoSizeTabs();
31020         }
31021     },
31022
31023     updateBody : function(w, h){
31024         if(w !== null){
31025             this.el.setWidth(w);
31026             w -= this.el.getBorderWidth("rl");
31027             if(this.config.adjustments){
31028                 w += this.config.adjustments[0];
31029             }
31030         }
31031         if(h !== null){
31032             this.el.setHeight(h);
31033             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31034             h -= this.el.getBorderWidth("tb");
31035             if(this.config.adjustments){
31036                 h += this.config.adjustments[1];
31037             }
31038             this.bodyEl.setHeight(h);
31039             if(this.tabs){
31040                 h = this.tabs.syncHeight(h);
31041             }
31042         }
31043         if(this.panelSize){
31044             w = w !== null ? w : this.panelSize.width;
31045             h = h !== null ? h : this.panelSize.height;
31046         }
31047         if(this.activePanel){
31048             var el = this.activePanel.getEl();
31049             w = w !== null ? w : el.getWidth();
31050             h = h !== null ? h : el.getHeight();
31051             this.panelSize = {width: w, height: h};
31052             this.activePanel.setSize(w, h);
31053         }
31054         if(Roo.isIE && this.tabs){
31055             this.tabs.el.repaint();
31056         }
31057     },
31058
31059     /**
31060      * Returns the container element for this region.
31061      * @return {Roo.Element}
31062      */
31063     getEl : function(){
31064         return this.el;
31065     },
31066
31067     /**
31068      * Hides this region.
31069      */
31070     hide : function(){
31071         if(!this.collapsed){
31072             this.el.dom.style.left = "-2000px";
31073             this.el.hide();
31074         }else{
31075             this.collapsedEl.dom.style.left = "-2000px";
31076             this.collapsedEl.hide();
31077         }
31078         this.visible = false;
31079         this.fireEvent("visibilitychange", this, false);
31080     },
31081
31082     /**
31083      * Shows this region if it was previously hidden.
31084      */
31085     show : function(){
31086         if(!this.collapsed){
31087             this.el.show();
31088         }else{
31089             this.collapsedEl.show();
31090         }
31091         this.visible = true;
31092         this.fireEvent("visibilitychange", this, true);
31093     },
31094
31095     closeClicked : function(){
31096         if(this.activePanel){
31097             this.remove(this.activePanel);
31098         }
31099     },
31100
31101     collapseClick : function(e){
31102         if(this.isSlid){
31103            e.stopPropagation();
31104            this.slideIn();
31105         }else{
31106            e.stopPropagation();
31107            this.slideOut();
31108         }
31109     },
31110
31111     /**
31112      * Collapses this region.
31113      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31114      */
31115     collapse : function(skipAnim){
31116         if(this.collapsed) return;
31117         this.collapsed = true;
31118         if(this.split){
31119             this.split.el.hide();
31120         }
31121         if(this.config.animate && skipAnim !== true){
31122             this.fireEvent("invalidated", this);
31123             this.animateCollapse();
31124         }else{
31125             this.el.setLocation(-20000,-20000);
31126             this.el.hide();
31127             this.collapsedEl.show();
31128             this.fireEvent("collapsed", this);
31129             this.fireEvent("invalidated", this);
31130         }
31131     },
31132
31133     animateCollapse : function(){
31134         // overridden
31135     },
31136
31137     /**
31138      * Expands this region if it was previously collapsed.
31139      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31140      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31141      */
31142     expand : function(e, skipAnim){
31143         if(e) e.stopPropagation();
31144         if(!this.collapsed || this.el.hasActiveFx()) return;
31145         if(this.isSlid){
31146             this.afterSlideIn();
31147             skipAnim = true;
31148         }
31149         this.collapsed = false;
31150         if(this.config.animate && skipAnim !== true){
31151             this.animateExpand();
31152         }else{
31153             this.el.show();
31154             if(this.split){
31155                 this.split.el.show();
31156             }
31157             this.collapsedEl.setLocation(-2000,-2000);
31158             this.collapsedEl.hide();
31159             this.fireEvent("invalidated", this);
31160             this.fireEvent("expanded", this);
31161         }
31162     },
31163
31164     animateExpand : function(){
31165         // overridden
31166     },
31167
31168     initTabs : function()
31169     {
31170         this.bodyEl.setStyle("overflow", "hidden");
31171         var ts = new Roo.TabPanel(
31172                 this.bodyEl.dom,
31173                 {
31174                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31175                     disableTooltips: this.config.disableTabTips,
31176                     toolbar : this.config.toolbar
31177                 }
31178         );
31179         if(this.config.hideTabs){
31180             ts.stripWrap.setDisplayed(false);
31181         }
31182         this.tabs = ts;
31183         ts.resizeTabs = this.config.resizeTabs === true;
31184         ts.minTabWidth = this.config.minTabWidth || 40;
31185         ts.maxTabWidth = this.config.maxTabWidth || 250;
31186         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31187         ts.monitorResize = false;
31188         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31189         ts.bodyEl.addClass('x-layout-tabs-body');
31190         this.panels.each(this.initPanelAsTab, this);
31191     },
31192
31193     initPanelAsTab : function(panel){
31194         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31195                     this.config.closeOnTab && panel.isClosable());
31196         if(panel.tabTip !== undefined){
31197             ti.setTooltip(panel.tabTip);
31198         }
31199         ti.on("activate", function(){
31200               this.setActivePanel(panel);
31201         }, this);
31202         if(this.config.closeOnTab){
31203             ti.on("beforeclose", function(t, e){
31204                 e.cancel = true;
31205                 this.remove(panel);
31206             }, this);
31207         }
31208         return ti;
31209     },
31210
31211     updatePanelTitle : function(panel, title){
31212         if(this.activePanel == panel){
31213             this.updateTitle(title);
31214         }
31215         if(this.tabs){
31216             var ti = this.tabs.getTab(panel.getEl().id);
31217             ti.setText(title);
31218             if(panel.tabTip !== undefined){
31219                 ti.setTooltip(panel.tabTip);
31220             }
31221         }
31222     },
31223
31224     updateTitle : function(title){
31225         if(this.titleTextEl && !this.config.title){
31226             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31227         }
31228     },
31229
31230     setActivePanel : function(panel){
31231         panel = this.getPanel(panel);
31232         if(this.activePanel && this.activePanel != panel){
31233             this.activePanel.setActiveState(false);
31234         }
31235         this.activePanel = panel;
31236         panel.setActiveState(true);
31237         if(this.panelSize){
31238             panel.setSize(this.panelSize.width, this.panelSize.height);
31239         }
31240         if(this.closeBtn){
31241             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31242         }
31243         this.updateTitle(panel.getTitle());
31244         if(this.tabs){
31245             this.fireEvent("invalidated", this);
31246         }
31247         this.fireEvent("panelactivated", this, panel);
31248     },
31249
31250     /**
31251      * Shows the specified panel.
31252      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31253      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31254      */
31255     showPanel : function(panel){
31256         if(panel = this.getPanel(panel)){
31257             if(this.tabs){
31258                 var tab = this.tabs.getTab(panel.getEl().id);
31259                 if(tab.isHidden()){
31260                     this.tabs.unhideTab(tab.id);
31261                 }
31262                 tab.activate();
31263             }else{
31264                 this.setActivePanel(panel);
31265             }
31266         }
31267         return panel;
31268     },
31269
31270     /**
31271      * Get the active panel for this region.
31272      * @return {Roo.ContentPanel} The active panel or null
31273      */
31274     getActivePanel : function(){
31275         return this.activePanel;
31276     },
31277
31278     validateVisibility : function(){
31279         if(this.panels.getCount() < 1){
31280             this.updateTitle("&#160;");
31281             this.closeBtn.hide();
31282             this.hide();
31283         }else{
31284             if(!this.isVisible()){
31285                 this.show();
31286             }
31287         }
31288     },
31289
31290     /**
31291      * Adds the passed ContentPanel(s) to this region.
31292      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31293      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31294      */
31295     add : function(panel){
31296         if(arguments.length > 1){
31297             for(var i = 0, len = arguments.length; i < len; i++) {
31298                 this.add(arguments[i]);
31299             }
31300             return null;
31301         }
31302         if(this.hasPanel(panel)){
31303             this.showPanel(panel);
31304             return panel;
31305         }
31306         panel.setRegion(this);
31307         this.panels.add(panel);
31308         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31309             this.bodyEl.dom.appendChild(panel.getEl().dom);
31310             if(panel.background !== true){
31311                 this.setActivePanel(panel);
31312             }
31313             this.fireEvent("paneladded", this, panel);
31314             return panel;
31315         }
31316         if(!this.tabs){
31317             this.initTabs();
31318         }else{
31319             this.initPanelAsTab(panel);
31320         }
31321         if(panel.background !== true){
31322             this.tabs.activate(panel.getEl().id);
31323         }
31324         this.fireEvent("paneladded", this, panel);
31325         return panel;
31326     },
31327
31328     /**
31329      * Hides the tab for the specified panel.
31330      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31331      */
31332     hidePanel : function(panel){
31333         if(this.tabs && (panel = this.getPanel(panel))){
31334             this.tabs.hideTab(panel.getEl().id);
31335         }
31336     },
31337
31338     /**
31339      * Unhides the tab for a previously hidden panel.
31340      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31341      */
31342     unhidePanel : function(panel){
31343         if(this.tabs && (panel = this.getPanel(panel))){
31344             this.tabs.unhideTab(panel.getEl().id);
31345         }
31346     },
31347
31348     clearPanels : function(){
31349         while(this.panels.getCount() > 0){
31350              this.remove(this.panels.first());
31351         }
31352     },
31353
31354     /**
31355      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31356      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31357      * @param {Boolean} preservePanel Overrides the config preservePanel option
31358      * @return {Roo.ContentPanel} The panel that was removed
31359      */
31360     remove : function(panel, preservePanel){
31361         panel = this.getPanel(panel);
31362         if(!panel){
31363             return null;
31364         }
31365         var e = {};
31366         this.fireEvent("beforeremove", this, panel, e);
31367         if(e.cancel === true){
31368             return null;
31369         }
31370         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31371         var panelId = panel.getId();
31372         this.panels.removeKey(panelId);
31373         if(preservePanel){
31374             document.body.appendChild(panel.getEl().dom);
31375         }
31376         if(this.tabs){
31377             this.tabs.removeTab(panel.getEl().id);
31378         }else if (!preservePanel){
31379             this.bodyEl.dom.removeChild(panel.getEl().dom);
31380         }
31381         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
31382             var p = this.panels.first();
31383             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
31384             tempEl.appendChild(p.getEl().dom);
31385             this.bodyEl.update("");
31386             this.bodyEl.dom.appendChild(p.getEl().dom);
31387             tempEl = null;
31388             this.updateTitle(p.getTitle());
31389             this.tabs = null;
31390             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31391             this.setActivePanel(p);
31392         }
31393         panel.setRegion(null);
31394         if(this.activePanel == panel){
31395             this.activePanel = null;
31396         }
31397         if(this.config.autoDestroy !== false && preservePanel !== true){
31398             try{panel.destroy();}catch(e){}
31399         }
31400         this.fireEvent("panelremoved", this, panel);
31401         return panel;
31402     },
31403
31404     /**
31405      * Returns the TabPanel component used by this region
31406      * @return {Roo.TabPanel}
31407      */
31408     getTabs : function(){
31409         return this.tabs;
31410     },
31411
31412     createTool : function(parentEl, className){
31413         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
31414             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
31415         btn.addClassOnOver("x-layout-tools-button-over");
31416         return btn;
31417     }
31418 });/*
31419  * Based on:
31420  * Ext JS Library 1.1.1
31421  * Copyright(c) 2006-2007, Ext JS, LLC.
31422  *
31423  * Originally Released Under LGPL - original licence link has changed is not relivant.
31424  *
31425  * Fork - LGPL
31426  * <script type="text/javascript">
31427  */
31428  
31429
31430
31431 /**
31432  * @class Roo.SplitLayoutRegion
31433  * @extends Roo.LayoutRegion
31434  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
31435  */
31436 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
31437     this.cursor = cursor;
31438     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
31439 };
31440
31441 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
31442     splitTip : "Drag to resize.",
31443     collapsibleSplitTip : "Drag to resize. Double click to hide.",
31444     useSplitTips : false,
31445
31446     applyConfig : function(config){
31447         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
31448         if(config.split){
31449             if(!this.split){
31450                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
31451                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
31452                 /** The SplitBar for this region 
31453                 * @type Roo.SplitBar */
31454                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
31455                 this.split.on("moved", this.onSplitMove, this);
31456                 this.split.useShim = config.useShim === true;
31457                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
31458                 if(this.useSplitTips){
31459                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
31460                 }
31461                 if(config.collapsible){
31462                     this.split.el.on("dblclick", this.collapse,  this);
31463                 }
31464             }
31465             if(typeof config.minSize != "undefined"){
31466                 this.split.minSize = config.minSize;
31467             }
31468             if(typeof config.maxSize != "undefined"){
31469                 this.split.maxSize = config.maxSize;
31470             }
31471             if(config.hideWhenEmpty || config.hidden || config.collapsed){
31472                 this.hideSplitter();
31473             }
31474         }
31475     },
31476
31477     getHMaxSize : function(){
31478          var cmax = this.config.maxSize || 10000;
31479          var center = this.mgr.getRegion("center");
31480          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
31481     },
31482
31483     getVMaxSize : function(){
31484          var cmax = this.config.maxSize || 10000;
31485          var center = this.mgr.getRegion("center");
31486          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
31487     },
31488
31489     onSplitMove : function(split, newSize){
31490         this.fireEvent("resized", this, newSize);
31491     },
31492     
31493     /** 
31494      * Returns the {@link Roo.SplitBar} for this region.
31495      * @return {Roo.SplitBar}
31496      */
31497     getSplitBar : function(){
31498         return this.split;
31499     },
31500     
31501     hide : function(){
31502         this.hideSplitter();
31503         Roo.SplitLayoutRegion.superclass.hide.call(this);
31504     },
31505
31506     hideSplitter : function(){
31507         if(this.split){
31508             this.split.el.setLocation(-2000,-2000);
31509             this.split.el.hide();
31510         }
31511     },
31512
31513     show : function(){
31514         if(this.split){
31515             this.split.el.show();
31516         }
31517         Roo.SplitLayoutRegion.superclass.show.call(this);
31518     },
31519     
31520     beforeSlide: function(){
31521         if(Roo.isGecko){// firefox overflow auto bug workaround
31522             this.bodyEl.clip();
31523             if(this.tabs) this.tabs.bodyEl.clip();
31524             if(this.activePanel){
31525                 this.activePanel.getEl().clip();
31526                 
31527                 if(this.activePanel.beforeSlide){
31528                     this.activePanel.beforeSlide();
31529                 }
31530             }
31531         }
31532     },
31533     
31534     afterSlide : function(){
31535         if(Roo.isGecko){// firefox overflow auto bug workaround
31536             this.bodyEl.unclip();
31537             if(this.tabs) this.tabs.bodyEl.unclip();
31538             if(this.activePanel){
31539                 this.activePanel.getEl().unclip();
31540                 if(this.activePanel.afterSlide){
31541                     this.activePanel.afterSlide();
31542                 }
31543             }
31544         }
31545     },
31546
31547     initAutoHide : function(){
31548         if(this.autoHide !== false){
31549             if(!this.autoHideHd){
31550                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31551                 this.autoHideHd = {
31552                     "mouseout": function(e){
31553                         if(!e.within(this.el, true)){
31554                             st.delay(500);
31555                         }
31556                     },
31557                     "mouseover" : function(e){
31558                         st.cancel();
31559                     },
31560                     scope : this
31561                 };
31562             }
31563             this.el.on(this.autoHideHd);
31564         }
31565     },
31566
31567     clearAutoHide : function(){
31568         if(this.autoHide !== false){
31569             this.el.un("mouseout", this.autoHideHd.mouseout);
31570             this.el.un("mouseover", this.autoHideHd.mouseover);
31571         }
31572     },
31573
31574     clearMonitor : function(){
31575         Roo.get(document).un("click", this.slideInIf, this);
31576     },
31577
31578     // these names are backwards but not changed for compat
31579     slideOut : function(){
31580         if(this.isSlid || this.el.hasActiveFx()){
31581             return;
31582         }
31583         this.isSlid = true;
31584         if(this.collapseBtn){
31585             this.collapseBtn.hide();
31586         }
31587         this.closeBtnState = this.closeBtn.getStyle('display');
31588         this.closeBtn.hide();
31589         if(this.stickBtn){
31590             this.stickBtn.show();
31591         }
31592         this.el.show();
31593         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31594         this.beforeSlide();
31595         this.el.setStyle("z-index", 10001);
31596         this.el.slideIn(this.getSlideAnchor(), {
31597             callback: function(){
31598                 this.afterSlide();
31599                 this.initAutoHide();
31600                 Roo.get(document).on("click", this.slideInIf, this);
31601                 this.fireEvent("slideshow", this);
31602             },
31603             scope: this,
31604             block: true
31605         });
31606     },
31607
31608     afterSlideIn : function(){
31609         this.clearAutoHide();
31610         this.isSlid = false;
31611         this.clearMonitor();
31612         this.el.setStyle("z-index", "");
31613         if(this.collapseBtn){
31614             this.collapseBtn.show();
31615         }
31616         this.closeBtn.setStyle('display', this.closeBtnState);
31617         if(this.stickBtn){
31618             this.stickBtn.hide();
31619         }
31620         this.fireEvent("slidehide", this);
31621     },
31622
31623     slideIn : function(cb){
31624         if(!this.isSlid || this.el.hasActiveFx()){
31625             Roo.callback(cb);
31626             return;
31627         }
31628         this.isSlid = false;
31629         this.beforeSlide();
31630         this.el.slideOut(this.getSlideAnchor(), {
31631             callback: function(){
31632                 this.el.setLeftTop(-10000, -10000);
31633                 this.afterSlide();
31634                 this.afterSlideIn();
31635                 Roo.callback(cb);
31636             },
31637             scope: this,
31638             block: true
31639         });
31640     },
31641     
31642     slideInIf : function(e){
31643         if(!e.within(this.el)){
31644             this.slideIn();
31645         }
31646     },
31647
31648     animateCollapse : function(){
31649         this.beforeSlide();
31650         this.el.setStyle("z-index", 20000);
31651         var anchor = this.getSlideAnchor();
31652         this.el.slideOut(anchor, {
31653             callback : function(){
31654                 this.el.setStyle("z-index", "");
31655                 this.collapsedEl.slideIn(anchor, {duration:.3});
31656                 this.afterSlide();
31657                 this.el.setLocation(-10000,-10000);
31658                 this.el.hide();
31659                 this.fireEvent("collapsed", this);
31660             },
31661             scope: this,
31662             block: true
31663         });
31664     },
31665
31666     animateExpand : function(){
31667         this.beforeSlide();
31668         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31669         this.el.setStyle("z-index", 20000);
31670         this.collapsedEl.hide({
31671             duration:.1
31672         });
31673         this.el.slideIn(this.getSlideAnchor(), {
31674             callback : function(){
31675                 this.el.setStyle("z-index", "");
31676                 this.afterSlide();
31677                 if(this.split){
31678                     this.split.el.show();
31679                 }
31680                 this.fireEvent("invalidated", this);
31681                 this.fireEvent("expanded", this);
31682             },
31683             scope: this,
31684             block: true
31685         });
31686     },
31687
31688     anchors : {
31689         "west" : "left",
31690         "east" : "right",
31691         "north" : "top",
31692         "south" : "bottom"
31693     },
31694
31695     sanchors : {
31696         "west" : "l",
31697         "east" : "r",
31698         "north" : "t",
31699         "south" : "b"
31700     },
31701
31702     canchors : {
31703         "west" : "tl-tr",
31704         "east" : "tr-tl",
31705         "north" : "tl-bl",
31706         "south" : "bl-tl"
31707     },
31708
31709     getAnchor : function(){
31710         return this.anchors[this.position];
31711     },
31712
31713     getCollapseAnchor : function(){
31714         return this.canchors[this.position];
31715     },
31716
31717     getSlideAnchor : function(){
31718         return this.sanchors[this.position];
31719     },
31720
31721     getAlignAdj : function(){
31722         var cm = this.cmargins;
31723         switch(this.position){
31724             case "west":
31725                 return [0, 0];
31726             break;
31727             case "east":
31728                 return [0, 0];
31729             break;
31730             case "north":
31731                 return [0, 0];
31732             break;
31733             case "south":
31734                 return [0, 0];
31735             break;
31736         }
31737     },
31738
31739     getExpandAdj : function(){
31740         var c = this.collapsedEl, cm = this.cmargins;
31741         switch(this.position){
31742             case "west":
31743                 return [-(cm.right+c.getWidth()+cm.left), 0];
31744             break;
31745             case "east":
31746                 return [cm.right+c.getWidth()+cm.left, 0];
31747             break;
31748             case "north":
31749                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31750             break;
31751             case "south":
31752                 return [0, cm.top+cm.bottom+c.getHeight()];
31753             break;
31754         }
31755     }
31756 });/*
31757  * Based on:
31758  * Ext JS Library 1.1.1
31759  * Copyright(c) 2006-2007, Ext JS, LLC.
31760  *
31761  * Originally Released Under LGPL - original licence link has changed is not relivant.
31762  *
31763  * Fork - LGPL
31764  * <script type="text/javascript">
31765  */
31766 /*
31767  * These classes are private internal classes
31768  */
31769 Roo.CenterLayoutRegion = function(mgr, config){
31770     Roo.LayoutRegion.call(this, mgr, config, "center");
31771     this.visible = true;
31772     this.minWidth = config.minWidth || 20;
31773     this.minHeight = config.minHeight || 20;
31774 };
31775
31776 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31777     hide : function(){
31778         // center panel can't be hidden
31779     },
31780     
31781     show : function(){
31782         // center panel can't be hidden
31783     },
31784     
31785     getMinWidth: function(){
31786         return this.minWidth;
31787     },
31788     
31789     getMinHeight: function(){
31790         return this.minHeight;
31791     }
31792 });
31793
31794
31795 Roo.NorthLayoutRegion = function(mgr, config){
31796     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31797     if(this.split){
31798         this.split.placement = Roo.SplitBar.TOP;
31799         this.split.orientation = Roo.SplitBar.VERTICAL;
31800         this.split.el.addClass("x-layout-split-v");
31801     }
31802     var size = config.initialSize || config.height;
31803     if(typeof size != "undefined"){
31804         this.el.setHeight(size);
31805     }
31806 };
31807 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31808     orientation: Roo.SplitBar.VERTICAL,
31809     getBox : function(){
31810         if(this.collapsed){
31811             return this.collapsedEl.getBox();
31812         }
31813         var box = this.el.getBox();
31814         if(this.split){
31815             box.height += this.split.el.getHeight();
31816         }
31817         return box;
31818     },
31819     
31820     updateBox : function(box){
31821         if(this.split && !this.collapsed){
31822             box.height -= this.split.el.getHeight();
31823             this.split.el.setLeft(box.x);
31824             this.split.el.setTop(box.y+box.height);
31825             this.split.el.setWidth(box.width);
31826         }
31827         if(this.collapsed){
31828             this.updateBody(box.width, null);
31829         }
31830         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31831     }
31832 });
31833
31834 Roo.SouthLayoutRegion = function(mgr, config){
31835     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31836     if(this.split){
31837         this.split.placement = Roo.SplitBar.BOTTOM;
31838         this.split.orientation = Roo.SplitBar.VERTICAL;
31839         this.split.el.addClass("x-layout-split-v");
31840     }
31841     var size = config.initialSize || config.height;
31842     if(typeof size != "undefined"){
31843         this.el.setHeight(size);
31844     }
31845 };
31846 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31847     orientation: Roo.SplitBar.VERTICAL,
31848     getBox : function(){
31849         if(this.collapsed){
31850             return this.collapsedEl.getBox();
31851         }
31852         var box = this.el.getBox();
31853         if(this.split){
31854             var sh = this.split.el.getHeight();
31855             box.height += sh;
31856             box.y -= sh;
31857         }
31858         return box;
31859     },
31860     
31861     updateBox : function(box){
31862         if(this.split && !this.collapsed){
31863             var sh = this.split.el.getHeight();
31864             box.height -= sh;
31865             box.y += sh;
31866             this.split.el.setLeft(box.x);
31867             this.split.el.setTop(box.y-sh);
31868             this.split.el.setWidth(box.width);
31869         }
31870         if(this.collapsed){
31871             this.updateBody(box.width, null);
31872         }
31873         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31874     }
31875 });
31876
31877 Roo.EastLayoutRegion = function(mgr, config){
31878     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31879     if(this.split){
31880         this.split.placement = Roo.SplitBar.RIGHT;
31881         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31882         this.split.el.addClass("x-layout-split-h");
31883     }
31884     var size = config.initialSize || config.width;
31885     if(typeof size != "undefined"){
31886         this.el.setWidth(size);
31887     }
31888 };
31889 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31890     orientation: Roo.SplitBar.HORIZONTAL,
31891     getBox : function(){
31892         if(this.collapsed){
31893             return this.collapsedEl.getBox();
31894         }
31895         var box = this.el.getBox();
31896         if(this.split){
31897             var sw = this.split.el.getWidth();
31898             box.width += sw;
31899             box.x -= sw;
31900         }
31901         return box;
31902     },
31903
31904     updateBox : function(box){
31905         if(this.split && !this.collapsed){
31906             var sw = this.split.el.getWidth();
31907             box.width -= sw;
31908             this.split.el.setLeft(box.x);
31909             this.split.el.setTop(box.y);
31910             this.split.el.setHeight(box.height);
31911             box.x += sw;
31912         }
31913         if(this.collapsed){
31914             this.updateBody(null, box.height);
31915         }
31916         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31917     }
31918 });
31919
31920 Roo.WestLayoutRegion = function(mgr, config){
31921     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31922     if(this.split){
31923         this.split.placement = Roo.SplitBar.LEFT;
31924         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31925         this.split.el.addClass("x-layout-split-h");
31926     }
31927     var size = config.initialSize || config.width;
31928     if(typeof size != "undefined"){
31929         this.el.setWidth(size);
31930     }
31931 };
31932 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31933     orientation: Roo.SplitBar.HORIZONTAL,
31934     getBox : function(){
31935         if(this.collapsed){
31936             return this.collapsedEl.getBox();
31937         }
31938         var box = this.el.getBox();
31939         if(this.split){
31940             box.width += this.split.el.getWidth();
31941         }
31942         return box;
31943     },
31944     
31945     updateBox : function(box){
31946         if(this.split && !this.collapsed){
31947             var sw = this.split.el.getWidth();
31948             box.width -= sw;
31949             this.split.el.setLeft(box.x+box.width);
31950             this.split.el.setTop(box.y);
31951             this.split.el.setHeight(box.height);
31952         }
31953         if(this.collapsed){
31954             this.updateBody(null, box.height);
31955         }
31956         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31957     }
31958 });
31959 /*
31960  * Based on:
31961  * Ext JS Library 1.1.1
31962  * Copyright(c) 2006-2007, Ext JS, LLC.
31963  *
31964  * Originally Released Under LGPL - original licence link has changed is not relivant.
31965  *
31966  * Fork - LGPL
31967  * <script type="text/javascript">
31968  */
31969  
31970  
31971 /*
31972  * Private internal class for reading and applying state
31973  */
31974 Roo.LayoutStateManager = function(layout){
31975      // default empty state
31976      this.state = {
31977         north: {},
31978         south: {},
31979         east: {},
31980         west: {}       
31981     };
31982 };
31983
31984 Roo.LayoutStateManager.prototype = {
31985     init : function(layout, provider){
31986         this.provider = provider;
31987         var state = provider.get(layout.id+"-layout-state");
31988         if(state){
31989             var wasUpdating = layout.isUpdating();
31990             if(!wasUpdating){
31991                 layout.beginUpdate();
31992             }
31993             for(var key in state){
31994                 if(typeof state[key] != "function"){
31995                     var rstate = state[key];
31996                     var r = layout.getRegion(key);
31997                     if(r && rstate){
31998                         if(rstate.size){
31999                             r.resizeTo(rstate.size);
32000                         }
32001                         if(rstate.collapsed == true){
32002                             r.collapse(true);
32003                         }else{
32004                             r.expand(null, true);
32005                         }
32006                     }
32007                 }
32008             }
32009             if(!wasUpdating){
32010                 layout.endUpdate();
32011             }
32012             this.state = state; 
32013         }
32014         this.layout = layout;
32015         layout.on("regionresized", this.onRegionResized, this);
32016         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32017         layout.on("regionexpanded", this.onRegionExpanded, this);
32018     },
32019     
32020     storeState : function(){
32021         this.provider.set(this.layout.id+"-layout-state", this.state);
32022     },
32023     
32024     onRegionResized : function(region, newSize){
32025         this.state[region.getPosition()].size = newSize;
32026         this.storeState();
32027     },
32028     
32029     onRegionCollapsed : function(region){
32030         this.state[region.getPosition()].collapsed = true;
32031         this.storeState();
32032     },
32033     
32034     onRegionExpanded : function(region){
32035         this.state[region.getPosition()].collapsed = false;
32036         this.storeState();
32037     }
32038 };/*
32039  * Based on:
32040  * Ext JS Library 1.1.1
32041  * Copyright(c) 2006-2007, Ext JS, LLC.
32042  *
32043  * Originally Released Under LGPL - original licence link has changed is not relivant.
32044  *
32045  * Fork - LGPL
32046  * <script type="text/javascript">
32047  */
32048 /**
32049  * @class Roo.ContentPanel
32050  * @extends Roo.util.Observable
32051  * A basic ContentPanel element.
32052  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32053  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32054  * @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
32055  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32056  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32057  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32058  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32059  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32060  * @cfg {String} title          The title for this panel
32061  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32062  * @cfg {String} url            Calls {@link #setUrl} with this value
32063  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32064  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32065  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32066  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32067
32068  * @constructor
32069  * Create a new ContentPanel.
32070  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32071  * @param {String/Object} config A string to set only the title or a config object
32072  * @param {String} content (optional) Set the HTML content for this panel
32073  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32074  */
32075 Roo.ContentPanel = function(el, config, content){
32076     
32077      
32078     /*
32079     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32080         config = el;
32081         el = Roo.id();
32082     }
32083     if (config && config.parentLayout) { 
32084         el = config.parentLayout.el.createChild(); 
32085     }
32086     */
32087     if(el.autoCreate){ // xtype is available if this is called from factory
32088         config = el;
32089         el = Roo.id();
32090     }
32091     this.el = Roo.get(el);
32092     if(!this.el && config && config.autoCreate){
32093         if(typeof config.autoCreate == "object"){
32094             if(!config.autoCreate.id){
32095                 config.autoCreate.id = config.id||el;
32096             }
32097             this.el = Roo.DomHelper.append(document.body,
32098                         config.autoCreate, true);
32099         }else{
32100             this.el = Roo.DomHelper.append(document.body,
32101                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32102         }
32103     }
32104     this.closable = false;
32105     this.loaded = false;
32106     this.active = false;
32107     if(typeof config == "string"){
32108         this.title = config;
32109     }else{
32110         Roo.apply(this, config);
32111     }
32112     
32113     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32114         this.wrapEl = this.el.wrap();
32115         this.toolbar.container = this.el.insertSibling(false, 'before');
32116         this.toolbar = new Roo.Toolbar(this.toolbar);
32117     }
32118     
32119     
32120     
32121     if(this.resizeEl){
32122         this.resizeEl = Roo.get(this.resizeEl, true);
32123     }else{
32124         this.resizeEl = this.el;
32125     }
32126     this.addEvents({
32127         /**
32128          * @event activate
32129          * Fires when this panel is activated. 
32130          * @param {Roo.ContentPanel} this
32131          */
32132         "activate" : true,
32133         /**
32134          * @event deactivate
32135          * Fires when this panel is activated. 
32136          * @param {Roo.ContentPanel} this
32137          */
32138         "deactivate" : true,
32139
32140         /**
32141          * @event resize
32142          * Fires when this panel is resized if fitToFrame is true.
32143          * @param {Roo.ContentPanel} this
32144          * @param {Number} width The width after any component adjustments
32145          * @param {Number} height The height after any component adjustments
32146          */
32147         "resize" : true,
32148         
32149          /**
32150          * @event render
32151          * Fires when this tab is created
32152          * @param {Roo.ContentPanel} this
32153          */
32154         "render" : true
32155         
32156         
32157         
32158     });
32159     if(this.autoScroll){
32160         this.resizeEl.setStyle("overflow", "auto");
32161     } else {
32162         // fix randome scrolling
32163         this.el.on('scroll', function() {
32164             Roo.log('fix random scolling');
32165             this.scrollTo('top',0); 
32166         });
32167     }
32168     content = content || this.content;
32169     if(content){
32170         this.setContent(content);
32171     }
32172     if(config && config.url){
32173         this.setUrl(this.url, this.params, this.loadOnce);
32174     }
32175     
32176     
32177     
32178     Roo.ContentPanel.superclass.constructor.call(this);
32179     
32180     this.fireEvent('render', this);
32181 };
32182
32183 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32184     tabTip:'',
32185     setRegion : function(region){
32186         this.region = region;
32187         if(region){
32188            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32189         }else{
32190            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32191         } 
32192     },
32193     
32194     /**
32195      * Returns the toolbar for this Panel if one was configured. 
32196      * @return {Roo.Toolbar} 
32197      */
32198     getToolbar : function(){
32199         return this.toolbar;
32200     },
32201     
32202     setActiveState : function(active){
32203         this.active = active;
32204         if(!active){
32205             this.fireEvent("deactivate", this);
32206         }else{
32207             this.fireEvent("activate", this);
32208         }
32209     },
32210     /**
32211      * Updates this panel's element
32212      * @param {String} content The new content
32213      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32214     */
32215     setContent : function(content, loadScripts){
32216         this.el.update(content, loadScripts);
32217     },
32218
32219     ignoreResize : function(w, h){
32220         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32221             return true;
32222         }else{
32223             this.lastSize = {width: w, height: h};
32224             return false;
32225         }
32226     },
32227     /**
32228      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32229      * @return {Roo.UpdateManager} The UpdateManager
32230      */
32231     getUpdateManager : function(){
32232         return this.el.getUpdateManager();
32233     },
32234      /**
32235      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32236      * @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:
32237 <pre><code>
32238 panel.load({
32239     url: "your-url.php",
32240     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32241     callback: yourFunction,
32242     scope: yourObject, //(optional scope)
32243     discardUrl: false,
32244     nocache: false,
32245     text: "Loading...",
32246     timeout: 30,
32247     scripts: false
32248 });
32249 </code></pre>
32250      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32251      * 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.
32252      * @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}
32253      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32254      * @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.
32255      * @return {Roo.ContentPanel} this
32256      */
32257     load : function(){
32258         var um = this.el.getUpdateManager();
32259         um.update.apply(um, arguments);
32260         return this;
32261     },
32262
32263
32264     /**
32265      * 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.
32266      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32267      * @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)
32268      * @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)
32269      * @return {Roo.UpdateManager} The UpdateManager
32270      */
32271     setUrl : function(url, params, loadOnce){
32272         if(this.refreshDelegate){
32273             this.removeListener("activate", this.refreshDelegate);
32274         }
32275         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32276         this.on("activate", this.refreshDelegate);
32277         return this.el.getUpdateManager();
32278     },
32279     
32280     _handleRefresh : function(url, params, loadOnce){
32281         if(!loadOnce || !this.loaded){
32282             var updater = this.el.getUpdateManager();
32283             updater.update(url, params, this._setLoaded.createDelegate(this));
32284         }
32285     },
32286     
32287     _setLoaded : function(){
32288         this.loaded = true;
32289     }, 
32290     
32291     /**
32292      * Returns this panel's id
32293      * @return {String} 
32294      */
32295     getId : function(){
32296         return this.el.id;
32297     },
32298     
32299     /** 
32300      * Returns this panel's element - used by regiosn to add.
32301      * @return {Roo.Element} 
32302      */
32303     getEl : function(){
32304         return this.wrapEl || this.el;
32305     },
32306     
32307     adjustForComponents : function(width, height){
32308         if(this.resizeEl != this.el){
32309             width -= this.el.getFrameWidth('lr');
32310             height -= this.el.getFrameWidth('tb');
32311         }
32312         if(this.toolbar){
32313             var te = this.toolbar.getEl();
32314             height -= te.getHeight();
32315             te.setWidth(width);
32316         }
32317         if(this.adjustments){
32318             width += this.adjustments[0];
32319             height += this.adjustments[1];
32320         }
32321         return {"width": width, "height": height};
32322     },
32323     
32324     setSize : function(width, height){
32325         if(this.fitToFrame && !this.ignoreResize(width, height)){
32326             if(this.fitContainer && this.resizeEl != this.el){
32327                 this.el.setSize(width, height);
32328             }
32329             var size = this.adjustForComponents(width, height);
32330             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32331             this.fireEvent('resize', this, size.width, size.height);
32332         }
32333     },
32334     
32335     /**
32336      * Returns this panel's title
32337      * @return {String} 
32338      */
32339     getTitle : function(){
32340         return this.title;
32341     },
32342     
32343     /**
32344      * Set this panel's title
32345      * @param {String} title
32346      */
32347     setTitle : function(title){
32348         this.title = title;
32349         if(this.region){
32350             this.region.updatePanelTitle(this, title);
32351         }
32352     },
32353     
32354     /**
32355      * Returns true is this panel was configured to be closable
32356      * @return {Boolean} 
32357      */
32358     isClosable : function(){
32359         return this.closable;
32360     },
32361     
32362     beforeSlide : function(){
32363         this.el.clip();
32364         this.resizeEl.clip();
32365     },
32366     
32367     afterSlide : function(){
32368         this.el.unclip();
32369         this.resizeEl.unclip();
32370     },
32371     
32372     /**
32373      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
32374      *   Will fail silently if the {@link #setUrl} method has not been called.
32375      *   This does not activate the panel, just updates its content.
32376      */
32377     refresh : function(){
32378         if(this.refreshDelegate){
32379            this.loaded = false;
32380            this.refreshDelegate();
32381         }
32382     },
32383     
32384     /**
32385      * Destroys this panel
32386      */
32387     destroy : function(){
32388         this.el.removeAllListeners();
32389         var tempEl = document.createElement("span");
32390         tempEl.appendChild(this.el.dom);
32391         tempEl.innerHTML = "";
32392         this.el.remove();
32393         this.el = null;
32394     },
32395     
32396     /**
32397      * form - if the content panel contains a form - this is a reference to it.
32398      * @type {Roo.form.Form}
32399      */
32400     form : false,
32401     /**
32402      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
32403      *    This contains a reference to it.
32404      * @type {Roo.View}
32405      */
32406     view : false,
32407     
32408       /**
32409      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
32410      * <pre><code>
32411
32412 layout.addxtype({
32413        xtype : 'Form',
32414        items: [ .... ]
32415    }
32416 );
32417
32418 </code></pre>
32419      * @param {Object} cfg Xtype definition of item to add.
32420      */
32421     
32422     addxtype : function(cfg) {
32423         // add form..
32424         if (cfg.xtype.match(/^Form$/)) {
32425             var el = this.el.createChild();
32426
32427             this.form = new  Roo.form.Form(cfg);
32428             
32429             
32430             if ( this.form.allItems.length) this.form.render(el.dom);
32431             return this.form;
32432         }
32433         // should only have one of theses..
32434         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
32435             // views..
32436             cfg.el = this.el.appendChild(document.createElement("div"));
32437             // factory?
32438             
32439             var ret = new Roo.factory(cfg);
32440             ret.render && ret.render(false, ''); // render blank..
32441             this.view = ret;
32442             return ret;
32443         }
32444         return false;
32445     }
32446 });
32447
32448 /**
32449  * @class Roo.GridPanel
32450  * @extends Roo.ContentPanel
32451  * @constructor
32452  * Create a new GridPanel.
32453  * @param {Roo.grid.Grid} grid The grid for this panel
32454  * @param {String/Object} config A string to set only the panel's title, or a config object
32455  */
32456 Roo.GridPanel = function(grid, config){
32457     
32458   
32459     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32460         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32461         
32462     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32463     
32464     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32465     
32466     if(this.toolbar){
32467         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32468     }
32469     // xtype created footer. - not sure if will work as we normally have to render first..
32470     if (this.footer && !this.footer.el && this.footer.xtype) {
32471         
32472         this.footer.container = this.grid.getView().getFooterPanel(true);
32473         this.footer.dataSource = this.grid.dataSource;
32474         this.footer = Roo.factory(this.footer, Roo);
32475         
32476     }
32477     
32478     grid.monitorWindowResize = false; // turn off autosizing
32479     grid.autoHeight = false;
32480     grid.autoWidth = false;
32481     this.grid = grid;
32482     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32483 };
32484
32485 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32486     getId : function(){
32487         return this.grid.id;
32488     },
32489     
32490     /**
32491      * Returns the grid for this panel
32492      * @return {Roo.grid.Grid} 
32493      */
32494     getGrid : function(){
32495         return this.grid;    
32496     },
32497     
32498     setSize : function(width, height){
32499         if(!this.ignoreResize(width, height)){
32500             var grid = this.grid;
32501             var size = this.adjustForComponents(width, height);
32502             grid.getGridEl().setSize(size.width, size.height);
32503             grid.autoSize();
32504         }
32505     },
32506     
32507     beforeSlide : function(){
32508         this.grid.getView().scroller.clip();
32509     },
32510     
32511     afterSlide : function(){
32512         this.grid.getView().scroller.unclip();
32513     },
32514     
32515     destroy : function(){
32516         this.grid.destroy();
32517         delete this.grid;
32518         Roo.GridPanel.superclass.destroy.call(this); 
32519     }
32520 });
32521
32522
32523 /**
32524  * @class Roo.NestedLayoutPanel
32525  * @extends Roo.ContentPanel
32526  * @constructor
32527  * Create a new NestedLayoutPanel.
32528  * 
32529  * 
32530  * @param {Roo.BorderLayout} layout The layout for this panel
32531  * @param {String/Object} config A string to set only the title or a config object
32532  */
32533 Roo.NestedLayoutPanel = function(layout, config)
32534 {
32535     // construct with only one argument..
32536     /* FIXME - implement nicer consturctors
32537     if (layout.layout) {
32538         config = layout;
32539         layout = config.layout;
32540         delete config.layout;
32541     }
32542     if (layout.xtype && !layout.getEl) {
32543         // then layout needs constructing..
32544         layout = Roo.factory(layout, Roo);
32545     }
32546     */
32547     
32548     
32549     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32550     
32551     layout.monitorWindowResize = false; // turn off autosizing
32552     this.layout = layout;
32553     this.layout.getEl().addClass("x-layout-nested-layout");
32554     
32555     
32556     
32557     
32558 };
32559
32560 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32561
32562     setSize : function(width, height){
32563         if(!this.ignoreResize(width, height)){
32564             var size = this.adjustForComponents(width, height);
32565             var el = this.layout.getEl();
32566             el.setSize(size.width, size.height);
32567             var touch = el.dom.offsetWidth;
32568             this.layout.layout();
32569             // ie requires a double layout on the first pass
32570             if(Roo.isIE && !this.initialized){
32571                 this.initialized = true;
32572                 this.layout.layout();
32573             }
32574         }
32575     },
32576     
32577     // activate all subpanels if not currently active..
32578     
32579     setActiveState : function(active){
32580         this.active = active;
32581         if(!active){
32582             this.fireEvent("deactivate", this);
32583             return;
32584         }
32585         
32586         this.fireEvent("activate", this);
32587         // not sure if this should happen before or after..
32588         if (!this.layout) {
32589             return; // should not happen..
32590         }
32591         var reg = false;
32592         for (var r in this.layout.regions) {
32593             reg = this.layout.getRegion(r);
32594             if (reg.getActivePanel()) {
32595                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32596                 reg.setActivePanel(reg.getActivePanel());
32597                 continue;
32598             }
32599             if (!reg.panels.length) {
32600                 continue;
32601             }
32602             reg.showPanel(reg.getPanel(0));
32603         }
32604         
32605         
32606         
32607         
32608     },
32609     
32610     /**
32611      * Returns the nested BorderLayout for this panel
32612      * @return {Roo.BorderLayout} 
32613      */
32614     getLayout : function(){
32615         return this.layout;
32616     },
32617     
32618      /**
32619      * Adds a xtype elements to the layout of the nested panel
32620      * <pre><code>
32621
32622 panel.addxtype({
32623        xtype : 'ContentPanel',
32624        region: 'west',
32625        items: [ .... ]
32626    }
32627 );
32628
32629 panel.addxtype({
32630         xtype : 'NestedLayoutPanel',
32631         region: 'west',
32632         layout: {
32633            center: { },
32634            west: { }   
32635         },
32636         items : [ ... list of content panels or nested layout panels.. ]
32637    }
32638 );
32639 </code></pre>
32640      * @param {Object} cfg Xtype definition of item to add.
32641      */
32642     addxtype : function(cfg) {
32643         return this.layout.addxtype(cfg);
32644     
32645     }
32646 });
32647
32648 Roo.ScrollPanel = function(el, config, content){
32649     config = config || {};
32650     config.fitToFrame = true;
32651     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32652     
32653     this.el.dom.style.overflow = "hidden";
32654     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32655     this.el.removeClass("x-layout-inactive-content");
32656     this.el.on("mousewheel", this.onWheel, this);
32657
32658     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32659     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32660     up.unselectable(); down.unselectable();
32661     up.on("click", this.scrollUp, this);
32662     down.on("click", this.scrollDown, this);
32663     up.addClassOnOver("x-scroller-btn-over");
32664     down.addClassOnOver("x-scroller-btn-over");
32665     up.addClassOnClick("x-scroller-btn-click");
32666     down.addClassOnClick("x-scroller-btn-click");
32667     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32668
32669     this.resizeEl = this.el;
32670     this.el = wrap; this.up = up; this.down = down;
32671 };
32672
32673 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32674     increment : 100,
32675     wheelIncrement : 5,
32676     scrollUp : function(){
32677         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32678     },
32679
32680     scrollDown : function(){
32681         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32682     },
32683
32684     afterScroll : function(){
32685         var el = this.resizeEl;
32686         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32687         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32688         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32689     },
32690
32691     setSize : function(){
32692         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32693         this.afterScroll();
32694     },
32695
32696     onWheel : function(e){
32697         var d = e.getWheelDelta();
32698         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32699         this.afterScroll();
32700         e.stopEvent();
32701     },
32702
32703     setContent : function(content, loadScripts){
32704         this.resizeEl.update(content, loadScripts);
32705     }
32706
32707 });
32708
32709
32710
32711
32712
32713
32714
32715
32716
32717 /**
32718  * @class Roo.TreePanel
32719  * @extends Roo.ContentPanel
32720  * @constructor
32721  * Create a new TreePanel. - defaults to fit/scoll contents.
32722  * @param {String/Object} config A string to set only the panel's title, or a config object
32723  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32724  */
32725 Roo.TreePanel = function(config){
32726     var el = config.el;
32727     var tree = config.tree;
32728     delete config.tree; 
32729     delete config.el; // hopefull!
32730     
32731     // wrapper for IE7 strict & safari scroll issue
32732     
32733     var treeEl = el.createChild();
32734     config.resizeEl = treeEl;
32735     
32736     
32737     
32738     Roo.TreePanel.superclass.constructor.call(this, el, config);
32739  
32740  
32741     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32742     //console.log(tree);
32743     this.on('activate', function()
32744     {
32745         if (this.tree.rendered) {
32746             return;
32747         }
32748         //console.log('render tree');
32749         this.tree.render();
32750     });
32751     
32752     this.on('resize',  function (cp, w, h) {
32753             this.tree.innerCt.setWidth(w);
32754             this.tree.innerCt.setHeight(h);
32755             this.tree.innerCt.setStyle('overflow-y', 'auto');
32756     });
32757
32758         
32759     
32760 };
32761
32762 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32763     fitToFrame : true,
32764     autoScroll : true
32765 });
32766
32767
32768
32769
32770
32771
32772
32773
32774
32775
32776
32777 /*
32778  * Based on:
32779  * Ext JS Library 1.1.1
32780  * Copyright(c) 2006-2007, Ext JS, LLC.
32781  *
32782  * Originally Released Under LGPL - original licence link has changed is not relivant.
32783  *
32784  * Fork - LGPL
32785  * <script type="text/javascript">
32786  */
32787  
32788
32789 /**
32790  * @class Roo.ReaderLayout
32791  * @extends Roo.BorderLayout
32792  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32793  * center region containing two nested regions (a top one for a list view and one for item preview below),
32794  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32795  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32796  * expedites the setup of the overall layout and regions for this common application style.
32797  * Example:
32798  <pre><code>
32799 var reader = new Roo.ReaderLayout();
32800 var CP = Roo.ContentPanel;  // shortcut for adding
32801
32802 reader.beginUpdate();
32803 reader.add("north", new CP("north", "North"));
32804 reader.add("west", new CP("west", {title: "West"}));
32805 reader.add("east", new CP("east", {title: "East"}));
32806
32807 reader.regions.listView.add(new CP("listView", "List"));
32808 reader.regions.preview.add(new CP("preview", "Preview"));
32809 reader.endUpdate();
32810 </code></pre>
32811 * @constructor
32812 * Create a new ReaderLayout
32813 * @param {Object} config Configuration options
32814 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32815 * document.body if omitted)
32816 */
32817 Roo.ReaderLayout = function(config, renderTo){
32818     var c = config || {size:{}};
32819     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32820         north: c.north !== false ? Roo.apply({
32821             split:false,
32822             initialSize: 32,
32823             titlebar: false
32824         }, c.north) : false,
32825         west: c.west !== false ? Roo.apply({
32826             split:true,
32827             initialSize: 200,
32828             minSize: 175,
32829             maxSize: 400,
32830             titlebar: true,
32831             collapsible: true,
32832             animate: true,
32833             margins:{left:5,right:0,bottom:5,top:5},
32834             cmargins:{left:5,right:5,bottom:5,top:5}
32835         }, c.west) : false,
32836         east: c.east !== false ? Roo.apply({
32837             split:true,
32838             initialSize: 200,
32839             minSize: 175,
32840             maxSize: 400,
32841             titlebar: true,
32842             collapsible: true,
32843             animate: true,
32844             margins:{left:0,right:5,bottom:5,top:5},
32845             cmargins:{left:5,right:5,bottom:5,top:5}
32846         }, c.east) : false,
32847         center: Roo.apply({
32848             tabPosition: 'top',
32849             autoScroll:false,
32850             closeOnTab: true,
32851             titlebar:false,
32852             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32853         }, c.center)
32854     });
32855
32856     this.el.addClass('x-reader');
32857
32858     this.beginUpdate();
32859
32860     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32861         south: c.preview !== false ? Roo.apply({
32862             split:true,
32863             initialSize: 200,
32864             minSize: 100,
32865             autoScroll:true,
32866             collapsible:true,
32867             titlebar: true,
32868             cmargins:{top:5,left:0, right:0, bottom:0}
32869         }, c.preview) : false,
32870         center: Roo.apply({
32871             autoScroll:false,
32872             titlebar:false,
32873             minHeight:200
32874         }, c.listView)
32875     });
32876     this.add('center', new Roo.NestedLayoutPanel(inner,
32877             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32878
32879     this.endUpdate();
32880
32881     this.regions.preview = inner.getRegion('south');
32882     this.regions.listView = inner.getRegion('center');
32883 };
32884
32885 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32886  * Based on:
32887  * Ext JS Library 1.1.1
32888  * Copyright(c) 2006-2007, Ext JS, LLC.
32889  *
32890  * Originally Released Under LGPL - original licence link has changed is not relivant.
32891  *
32892  * Fork - LGPL
32893  * <script type="text/javascript">
32894  */
32895  
32896 /**
32897  * @class Roo.grid.Grid
32898  * @extends Roo.util.Observable
32899  * This class represents the primary interface of a component based grid control.
32900  * <br><br>Usage:<pre><code>
32901  var grid = new Roo.grid.Grid("my-container-id", {
32902      ds: myDataStore,
32903      cm: myColModel,
32904      selModel: mySelectionModel,
32905      autoSizeColumns: true,
32906      monitorWindowResize: false,
32907      trackMouseOver: true
32908  });
32909  // set any options
32910  grid.render();
32911  * </code></pre>
32912  * <b>Common Problems:</b><br/>
32913  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32914  * element will correct this<br/>
32915  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32916  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32917  * are unpredictable.<br/>
32918  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32919  * grid to calculate dimensions/offsets.<br/>
32920   * @constructor
32921  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32922  * The container MUST have some type of size defined for the grid to fill. The container will be
32923  * automatically set to position relative if it isn't already.
32924  * @param {Object} config A config object that sets properties on this grid.
32925  */
32926 Roo.grid.Grid = function(container, config){
32927         // initialize the container
32928         this.container = Roo.get(container);
32929         this.container.update("");
32930         this.container.setStyle("overflow", "hidden");
32931     this.container.addClass('x-grid-container');
32932
32933     this.id = this.container.id;
32934
32935     Roo.apply(this, config);
32936     // check and correct shorthanded configs
32937     if(this.ds){
32938         this.dataSource = this.ds;
32939         delete this.ds;
32940     }
32941     if(this.cm){
32942         this.colModel = this.cm;
32943         delete this.cm;
32944     }
32945     if(this.sm){
32946         this.selModel = this.sm;
32947         delete this.sm;
32948     }
32949
32950     if (this.selModel) {
32951         this.selModel = Roo.factory(this.selModel, Roo.grid);
32952         this.sm = this.selModel;
32953         this.sm.xmodule = this.xmodule || false;
32954     }
32955     if (typeof(this.colModel.config) == 'undefined') {
32956         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32957         this.cm = this.colModel;
32958         this.cm.xmodule = this.xmodule || false;
32959     }
32960     if (this.dataSource) {
32961         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32962         this.ds = this.dataSource;
32963         this.ds.xmodule = this.xmodule || false;
32964          
32965     }
32966     
32967     
32968     
32969     if(this.width){
32970         this.container.setWidth(this.width);
32971     }
32972
32973     if(this.height){
32974         this.container.setHeight(this.height);
32975     }
32976     /** @private */
32977         this.addEvents({
32978         // raw events
32979         /**
32980          * @event click
32981          * The raw click event for the entire grid.
32982          * @param {Roo.EventObject} e
32983          */
32984         "click" : true,
32985         /**
32986          * @event dblclick
32987          * The raw dblclick event for the entire grid.
32988          * @param {Roo.EventObject} e
32989          */
32990         "dblclick" : true,
32991         /**
32992          * @event contextmenu
32993          * The raw contextmenu event for the entire grid.
32994          * @param {Roo.EventObject} e
32995          */
32996         "contextmenu" : true,
32997         /**
32998          * @event mousedown
32999          * The raw mousedown event for the entire grid.
33000          * @param {Roo.EventObject} e
33001          */
33002         "mousedown" : true,
33003         /**
33004          * @event mouseup
33005          * The raw mouseup event for the entire grid.
33006          * @param {Roo.EventObject} e
33007          */
33008         "mouseup" : true,
33009         /**
33010          * @event mouseover
33011          * The raw mouseover event for the entire grid.
33012          * @param {Roo.EventObject} e
33013          */
33014         "mouseover" : true,
33015         /**
33016          * @event mouseout
33017          * The raw mouseout event for the entire grid.
33018          * @param {Roo.EventObject} e
33019          */
33020         "mouseout" : true,
33021         /**
33022          * @event keypress
33023          * The raw keypress event for the entire grid.
33024          * @param {Roo.EventObject} e
33025          */
33026         "keypress" : true,
33027         /**
33028          * @event keydown
33029          * The raw keydown event for the entire grid.
33030          * @param {Roo.EventObject} e
33031          */
33032         "keydown" : true,
33033
33034         // custom events
33035
33036         /**
33037          * @event cellclick
33038          * Fires when a cell is clicked
33039          * @param {Grid} this
33040          * @param {Number} rowIndex
33041          * @param {Number} columnIndex
33042          * @param {Roo.EventObject} e
33043          */
33044         "cellclick" : true,
33045         /**
33046          * @event celldblclick
33047          * Fires when a cell is double clicked
33048          * @param {Grid} this
33049          * @param {Number} rowIndex
33050          * @param {Number} columnIndex
33051          * @param {Roo.EventObject} e
33052          */
33053         "celldblclick" : true,
33054         /**
33055          * @event rowclick
33056          * Fires when a row is clicked
33057          * @param {Grid} this
33058          * @param {Number} rowIndex
33059          * @param {Roo.EventObject} e
33060          */
33061         "rowclick" : true,
33062         /**
33063          * @event rowdblclick
33064          * Fires when a row is double clicked
33065          * @param {Grid} this
33066          * @param {Number} rowIndex
33067          * @param {Roo.EventObject} e
33068          */
33069         "rowdblclick" : true,
33070         /**
33071          * @event headerclick
33072          * Fires when a header is clicked
33073          * @param {Grid} this
33074          * @param {Number} columnIndex
33075          * @param {Roo.EventObject} e
33076          */
33077         "headerclick" : true,
33078         /**
33079          * @event headerdblclick
33080          * Fires when a header cell is double clicked
33081          * @param {Grid} this
33082          * @param {Number} columnIndex
33083          * @param {Roo.EventObject} e
33084          */
33085         "headerdblclick" : true,
33086         /**
33087          * @event rowcontextmenu
33088          * Fires when a row is right clicked
33089          * @param {Grid} this
33090          * @param {Number} rowIndex
33091          * @param {Roo.EventObject} e
33092          */
33093         "rowcontextmenu" : true,
33094         /**
33095          * @event cellcontextmenu
33096          * Fires when a cell is right clicked
33097          * @param {Grid} this
33098          * @param {Number} rowIndex
33099          * @param {Number} cellIndex
33100          * @param {Roo.EventObject} e
33101          */
33102          "cellcontextmenu" : true,
33103         /**
33104          * @event headercontextmenu
33105          * Fires when a header is right clicked
33106          * @param {Grid} this
33107          * @param {Number} columnIndex
33108          * @param {Roo.EventObject} e
33109          */
33110         "headercontextmenu" : true,
33111         /**
33112          * @event bodyscroll
33113          * Fires when the body element is scrolled
33114          * @param {Number} scrollLeft
33115          * @param {Number} scrollTop
33116          */
33117         "bodyscroll" : true,
33118         /**
33119          * @event columnresize
33120          * Fires when the user resizes a column
33121          * @param {Number} columnIndex
33122          * @param {Number} newSize
33123          */
33124         "columnresize" : true,
33125         /**
33126          * @event columnmove
33127          * Fires when the user moves a column
33128          * @param {Number} oldIndex
33129          * @param {Number} newIndex
33130          */
33131         "columnmove" : true,
33132         /**
33133          * @event startdrag
33134          * Fires when row(s) start being dragged
33135          * @param {Grid} this
33136          * @param {Roo.GridDD} dd The drag drop object
33137          * @param {event} e The raw browser event
33138          */
33139         "startdrag" : true,
33140         /**
33141          * @event enddrag
33142          * Fires when a drag operation is complete
33143          * @param {Grid} this
33144          * @param {Roo.GridDD} dd The drag drop object
33145          * @param {event} e The raw browser event
33146          */
33147         "enddrag" : true,
33148         /**
33149          * @event dragdrop
33150          * Fires when dragged row(s) are dropped on a valid DD target
33151          * @param {Grid} this
33152          * @param {Roo.GridDD} dd The drag drop object
33153          * @param {String} targetId The target drag drop object
33154          * @param {event} e The raw browser event
33155          */
33156         "dragdrop" : true,
33157         /**
33158          * @event dragover
33159          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33160          * @param {Grid} this
33161          * @param {Roo.GridDD} dd The drag drop object
33162          * @param {String} targetId The target drag drop object
33163          * @param {event} e The raw browser event
33164          */
33165         "dragover" : true,
33166         /**
33167          * @event dragenter
33168          *  Fires when the dragged row(s) first cross another DD target while being dragged
33169          * @param {Grid} this
33170          * @param {Roo.GridDD} dd The drag drop object
33171          * @param {String} targetId The target drag drop object
33172          * @param {event} e The raw browser event
33173          */
33174         "dragenter" : true,
33175         /**
33176          * @event dragout
33177          * Fires when the dragged row(s) leave another DD target while being dragged
33178          * @param {Grid} this
33179          * @param {Roo.GridDD} dd The drag drop object
33180          * @param {String} targetId The target drag drop object
33181          * @param {event} e The raw browser event
33182          */
33183         "dragout" : true,
33184         /**
33185          * @event rowclass
33186          * Fires when a row is rendered, so you can change add a style to it.
33187          * @param {GridView} gridview   The grid view
33188          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33189          */
33190         'rowclass' : true,
33191
33192         /**
33193          * @event render
33194          * Fires when the grid is rendered
33195          * @param {Grid} grid
33196          */
33197         'render' : true
33198     });
33199
33200     Roo.grid.Grid.superclass.constructor.call(this);
33201 };
33202 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33203     
33204     /**
33205      * @cfg {String} ddGroup - drag drop group.
33206      */
33207
33208     /**
33209      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33210      */
33211     minColumnWidth : 25,
33212
33213     /**
33214      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33215      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33216      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33217      */
33218     autoSizeColumns : false,
33219
33220     /**
33221      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33222      */
33223     autoSizeHeaders : true,
33224
33225     /**
33226      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33227      */
33228     monitorWindowResize : true,
33229
33230     /**
33231      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33232      * rows measured to get a columns size. Default is 0 (all rows).
33233      */
33234     maxRowsToMeasure : 0,
33235
33236     /**
33237      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33238      */
33239     trackMouseOver : true,
33240
33241     /**
33242     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33243     */
33244     
33245     /**
33246     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33247     */
33248     enableDragDrop : false,
33249     
33250     /**
33251     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33252     */
33253     enableColumnMove : true,
33254     
33255     /**
33256     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33257     */
33258     enableColumnHide : true,
33259     
33260     /**
33261     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33262     */
33263     enableRowHeightSync : false,
33264     
33265     /**
33266     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33267     */
33268     stripeRows : true,
33269     
33270     /**
33271     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33272     */
33273     autoHeight : false,
33274
33275     /**
33276      * @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.
33277      */
33278     autoExpandColumn : false,
33279
33280     /**
33281     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33282     * Default is 50.
33283     */
33284     autoExpandMin : 50,
33285
33286     /**
33287     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33288     */
33289     autoExpandMax : 1000,
33290
33291     /**
33292     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33293     */
33294     view : null,
33295
33296     /**
33297     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33298     */
33299     loadMask : false,
33300     /**
33301     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33302     */
33303     dropTarget: false,
33304     
33305    
33306     
33307     // private
33308     rendered : false,
33309
33310     /**
33311     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33312     * of a fixed width. Default is false.
33313     */
33314     /**
33315     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33316     */
33317     /**
33318      * Called once after all setup has been completed and the grid is ready to be rendered.
33319      * @return {Roo.grid.Grid} this
33320      */
33321     render : function()
33322     {
33323         var c = this.container;
33324         // try to detect autoHeight/width mode
33325         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33326             this.autoHeight = true;
33327         }
33328         var view = this.getView();
33329         view.init(this);
33330
33331         c.on("click", this.onClick, this);
33332         c.on("dblclick", this.onDblClick, this);
33333         c.on("contextmenu", this.onContextMenu, this);
33334         c.on("keydown", this.onKeyDown, this);
33335
33336         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33337
33338         this.getSelectionModel().init(this);
33339
33340         view.render();
33341
33342         if(this.loadMask){
33343             this.loadMask = new Roo.LoadMask(this.container,
33344                     Roo.apply({store:this.dataSource}, this.loadMask));
33345         }
33346         
33347         
33348         if (this.toolbar && this.toolbar.xtype) {
33349             this.toolbar.container = this.getView().getHeaderPanel(true);
33350             this.toolbar = new Roo.Toolbar(this.toolbar);
33351         }
33352         if (this.footer && this.footer.xtype) {
33353             this.footer.dataSource = this.getDataSource();
33354             this.footer.container = this.getView().getFooterPanel(true);
33355             this.footer = Roo.factory(this.footer, Roo);
33356         }
33357         if (this.dropTarget && this.dropTarget.xtype) {
33358             delete this.dropTarget.xtype;
33359             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33360         }
33361         
33362         
33363         this.rendered = true;
33364         this.fireEvent('render', this);
33365         return this;
33366     },
33367
33368         /**
33369          * Reconfigures the grid to use a different Store and Column Model.
33370          * The View will be bound to the new objects and refreshed.
33371          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33372          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33373          */
33374     reconfigure : function(dataSource, colModel){
33375         if(this.loadMask){
33376             this.loadMask.destroy();
33377             this.loadMask = new Roo.LoadMask(this.container,
33378                     Roo.apply({store:dataSource}, this.loadMask));
33379         }
33380         this.view.bind(dataSource, colModel);
33381         this.dataSource = dataSource;
33382         this.colModel = colModel;
33383         this.view.refresh(true);
33384     },
33385
33386     // private
33387     onKeyDown : function(e){
33388         this.fireEvent("keydown", e);
33389     },
33390
33391     /**
33392      * Destroy this grid.
33393      * @param {Boolean} removeEl True to remove the element
33394      */
33395     destroy : function(removeEl, keepListeners){
33396         if(this.loadMask){
33397             this.loadMask.destroy();
33398         }
33399         var c = this.container;
33400         c.removeAllListeners();
33401         this.view.destroy();
33402         this.colModel.purgeListeners();
33403         if(!keepListeners){
33404             this.purgeListeners();
33405         }
33406         c.update("");
33407         if(removeEl === true){
33408             c.remove();
33409         }
33410     },
33411
33412     // private
33413     processEvent : function(name, e){
33414         this.fireEvent(name, e);
33415         var t = e.getTarget();
33416         var v = this.view;
33417         var header = v.findHeaderIndex(t);
33418         if(header !== false){
33419             this.fireEvent("header" + name, this, header, e);
33420         }else{
33421             var row = v.findRowIndex(t);
33422             var cell = v.findCellIndex(t);
33423             if(row !== false){
33424                 this.fireEvent("row" + name, this, row, e);
33425                 if(cell !== false){
33426                     this.fireEvent("cell" + name, this, row, cell, e);
33427                 }
33428             }
33429         }
33430     },
33431
33432     // private
33433     onClick : function(e){
33434         this.processEvent("click", e);
33435     },
33436
33437     // private
33438     onContextMenu : function(e, t){
33439         this.processEvent("contextmenu", e);
33440     },
33441
33442     // private
33443     onDblClick : function(e){
33444         this.processEvent("dblclick", e);
33445     },
33446
33447     // private
33448     walkCells : function(row, col, step, fn, scope){
33449         var cm = this.colModel, clen = cm.getColumnCount();
33450         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33451         if(step < 0){
33452             if(col < 0){
33453                 row--;
33454                 first = false;
33455             }
33456             while(row >= 0){
33457                 if(!first){
33458                     col = clen-1;
33459                 }
33460                 first = false;
33461                 while(col >= 0){
33462                     if(fn.call(scope || this, row, col, cm) === true){
33463                         return [row, col];
33464                     }
33465                     col--;
33466                 }
33467                 row--;
33468             }
33469         } else {
33470             if(col >= clen){
33471                 row++;
33472                 first = false;
33473             }
33474             while(row < rlen){
33475                 if(!first){
33476                     col = 0;
33477                 }
33478                 first = false;
33479                 while(col < clen){
33480                     if(fn.call(scope || this, row, col, cm) === true){
33481                         return [row, col];
33482                     }
33483                     col++;
33484                 }
33485                 row++;
33486             }
33487         }
33488         return null;
33489     },
33490
33491     // private
33492     getSelections : function(){
33493         return this.selModel.getSelections();
33494     },
33495
33496     /**
33497      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33498      * but if manual update is required this method will initiate it.
33499      */
33500     autoSize : function(){
33501         if(this.rendered){
33502             this.view.layout();
33503             if(this.view.adjustForScroll){
33504                 this.view.adjustForScroll();
33505             }
33506         }
33507     },
33508
33509     /**
33510      * Returns the grid's underlying element.
33511      * @return {Element} The element
33512      */
33513     getGridEl : function(){
33514         return this.container;
33515     },
33516
33517     // private for compatibility, overridden by editor grid
33518     stopEditing : function(){},
33519
33520     /**
33521      * Returns the grid's SelectionModel.
33522      * @return {SelectionModel}
33523      */
33524     getSelectionModel : function(){
33525         if(!this.selModel){
33526             this.selModel = new Roo.grid.RowSelectionModel();
33527         }
33528         return this.selModel;
33529     },
33530
33531     /**
33532      * Returns the grid's DataSource.
33533      * @return {DataSource}
33534      */
33535     getDataSource : function(){
33536         return this.dataSource;
33537     },
33538
33539     /**
33540      * Returns the grid's ColumnModel.
33541      * @return {ColumnModel}
33542      */
33543     getColumnModel : function(){
33544         return this.colModel;
33545     },
33546
33547     /**
33548      * Returns the grid's GridView object.
33549      * @return {GridView}
33550      */
33551     getView : function(){
33552         if(!this.view){
33553             this.view = new Roo.grid.GridView(this.viewConfig);
33554         }
33555         return this.view;
33556     },
33557     /**
33558      * Called to get grid's drag proxy text, by default returns this.ddText.
33559      * @return {String}
33560      */
33561     getDragDropText : function(){
33562         var count = this.selModel.getCount();
33563         return String.format(this.ddText, count, count == 1 ? '' : 's');
33564     }
33565 });
33566 /**
33567  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33568  * %0 is replaced with the number of selected rows.
33569  * @type String
33570  */
33571 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33572  * Based on:
33573  * Ext JS Library 1.1.1
33574  * Copyright(c) 2006-2007, Ext JS, LLC.
33575  *
33576  * Originally Released Under LGPL - original licence link has changed is not relivant.
33577  *
33578  * Fork - LGPL
33579  * <script type="text/javascript">
33580  */
33581  
33582 Roo.grid.AbstractGridView = function(){
33583         this.grid = null;
33584         
33585         this.events = {
33586             "beforerowremoved" : true,
33587             "beforerowsinserted" : true,
33588             "beforerefresh" : true,
33589             "rowremoved" : true,
33590             "rowsinserted" : true,
33591             "rowupdated" : true,
33592             "refresh" : true
33593         };
33594     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33595 };
33596
33597 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33598     rowClass : "x-grid-row",
33599     cellClass : "x-grid-cell",
33600     tdClass : "x-grid-td",
33601     hdClass : "x-grid-hd",
33602     splitClass : "x-grid-hd-split",
33603     
33604         init: function(grid){
33605         this.grid = grid;
33606                 var cid = this.grid.getGridEl().id;
33607         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33608         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33609         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33610         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33611         },
33612         
33613         getColumnRenderers : function(){
33614         var renderers = [];
33615         var cm = this.grid.colModel;
33616         var colCount = cm.getColumnCount();
33617         for(var i = 0; i < colCount; i++){
33618             renderers[i] = cm.getRenderer(i);
33619         }
33620         return renderers;
33621     },
33622     
33623     getColumnIds : function(){
33624         var ids = [];
33625         var cm = this.grid.colModel;
33626         var colCount = cm.getColumnCount();
33627         for(var i = 0; i < colCount; i++){
33628             ids[i] = cm.getColumnId(i);
33629         }
33630         return ids;
33631     },
33632     
33633     getDataIndexes : function(){
33634         if(!this.indexMap){
33635             this.indexMap = this.buildIndexMap();
33636         }
33637         return this.indexMap.colToData;
33638     },
33639     
33640     getColumnIndexByDataIndex : function(dataIndex){
33641         if(!this.indexMap){
33642             this.indexMap = this.buildIndexMap();
33643         }
33644         return this.indexMap.dataToCol[dataIndex];
33645     },
33646     
33647     /**
33648      * Set a css style for a column dynamically. 
33649      * @param {Number} colIndex The index of the column
33650      * @param {String} name The css property name
33651      * @param {String} value The css value
33652      */
33653     setCSSStyle : function(colIndex, name, value){
33654         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33655         Roo.util.CSS.updateRule(selector, name, value);
33656     },
33657     
33658     generateRules : function(cm){
33659         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33660         Roo.util.CSS.removeStyleSheet(rulesId);
33661         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33662             var cid = cm.getColumnId(i);
33663             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33664                          this.tdSelector, cid, " {\n}\n",
33665                          this.hdSelector, cid, " {\n}\n",
33666                          this.splitSelector, cid, " {\n}\n");
33667         }
33668         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33669     }
33670 });/*
33671  * Based on:
33672  * Ext JS Library 1.1.1
33673  * Copyright(c) 2006-2007, Ext JS, LLC.
33674  *
33675  * Originally Released Under LGPL - original licence link has changed is not relivant.
33676  *
33677  * Fork - LGPL
33678  * <script type="text/javascript">
33679  */
33680
33681 // private
33682 // This is a support class used internally by the Grid components
33683 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33684     this.grid = grid;
33685     this.view = grid.getView();
33686     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33687     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33688     if(hd2){
33689         this.setHandleElId(Roo.id(hd));
33690         this.setOuterHandleElId(Roo.id(hd2));
33691     }
33692     this.scroll = false;
33693 };
33694 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33695     maxDragWidth: 120,
33696     getDragData : function(e){
33697         var t = Roo.lib.Event.getTarget(e);
33698         var h = this.view.findHeaderCell(t);
33699         if(h){
33700             return {ddel: h.firstChild, header:h};
33701         }
33702         return false;
33703     },
33704
33705     onInitDrag : function(e){
33706         this.view.headersDisabled = true;
33707         var clone = this.dragData.ddel.cloneNode(true);
33708         clone.id = Roo.id();
33709         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33710         this.proxy.update(clone);
33711         return true;
33712     },
33713
33714     afterValidDrop : function(){
33715         var v = this.view;
33716         setTimeout(function(){
33717             v.headersDisabled = false;
33718         }, 50);
33719     },
33720
33721     afterInvalidDrop : function(){
33722         var v = this.view;
33723         setTimeout(function(){
33724             v.headersDisabled = false;
33725         }, 50);
33726     }
33727 });
33728 /*
33729  * Based on:
33730  * Ext JS Library 1.1.1
33731  * Copyright(c) 2006-2007, Ext JS, LLC.
33732  *
33733  * Originally Released Under LGPL - original licence link has changed is not relivant.
33734  *
33735  * Fork - LGPL
33736  * <script type="text/javascript">
33737  */
33738 // private
33739 // This is a support class used internally by the Grid components
33740 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33741     this.grid = grid;
33742     this.view = grid.getView();
33743     // split the proxies so they don't interfere with mouse events
33744     this.proxyTop = Roo.DomHelper.append(document.body, {
33745         cls:"col-move-top", html:"&#160;"
33746     }, true);
33747     this.proxyBottom = Roo.DomHelper.append(document.body, {
33748         cls:"col-move-bottom", html:"&#160;"
33749     }, true);
33750     this.proxyTop.hide = this.proxyBottom.hide = function(){
33751         this.setLeftTop(-100,-100);
33752         this.setStyle("visibility", "hidden");
33753     };
33754     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33755     // temporarily disabled
33756     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33757     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33758 };
33759 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33760     proxyOffsets : [-4, -9],
33761     fly: Roo.Element.fly,
33762
33763     getTargetFromEvent : function(e){
33764         var t = Roo.lib.Event.getTarget(e);
33765         var cindex = this.view.findCellIndex(t);
33766         if(cindex !== false){
33767             return this.view.getHeaderCell(cindex);
33768         }
33769         return null;
33770     },
33771
33772     nextVisible : function(h){
33773         var v = this.view, cm = this.grid.colModel;
33774         h = h.nextSibling;
33775         while(h){
33776             if(!cm.isHidden(v.getCellIndex(h))){
33777                 return h;
33778             }
33779             h = h.nextSibling;
33780         }
33781         return null;
33782     },
33783
33784     prevVisible : function(h){
33785         var v = this.view, cm = this.grid.colModel;
33786         h = h.prevSibling;
33787         while(h){
33788             if(!cm.isHidden(v.getCellIndex(h))){
33789                 return h;
33790             }
33791             h = h.prevSibling;
33792         }
33793         return null;
33794     },
33795
33796     positionIndicator : function(h, n, e){
33797         var x = Roo.lib.Event.getPageX(e);
33798         var r = Roo.lib.Dom.getRegion(n.firstChild);
33799         var px, pt, py = r.top + this.proxyOffsets[1];
33800         if((r.right - x) <= (r.right-r.left)/2){
33801             px = r.right+this.view.borderWidth;
33802             pt = "after";
33803         }else{
33804             px = r.left;
33805             pt = "before";
33806         }
33807         var oldIndex = this.view.getCellIndex(h);
33808         var newIndex = this.view.getCellIndex(n);
33809
33810         if(this.grid.colModel.isFixed(newIndex)){
33811             return false;
33812         }
33813
33814         var locked = this.grid.colModel.isLocked(newIndex);
33815
33816         if(pt == "after"){
33817             newIndex++;
33818         }
33819         if(oldIndex < newIndex){
33820             newIndex--;
33821         }
33822         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33823             return false;
33824         }
33825         px +=  this.proxyOffsets[0];
33826         this.proxyTop.setLeftTop(px, py);
33827         this.proxyTop.show();
33828         if(!this.bottomOffset){
33829             this.bottomOffset = this.view.mainHd.getHeight();
33830         }
33831         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33832         this.proxyBottom.show();
33833         return pt;
33834     },
33835
33836     onNodeEnter : function(n, dd, e, data){
33837         if(data.header != n){
33838             this.positionIndicator(data.header, n, e);
33839         }
33840     },
33841
33842     onNodeOver : function(n, dd, e, data){
33843         var result = false;
33844         if(data.header != n){
33845             result = this.positionIndicator(data.header, n, e);
33846         }
33847         if(!result){
33848             this.proxyTop.hide();
33849             this.proxyBottom.hide();
33850         }
33851         return result ? this.dropAllowed : this.dropNotAllowed;
33852     },
33853
33854     onNodeOut : function(n, dd, e, data){
33855         this.proxyTop.hide();
33856         this.proxyBottom.hide();
33857     },
33858
33859     onNodeDrop : function(n, dd, e, data){
33860         var h = data.header;
33861         if(h != n){
33862             var cm = this.grid.colModel;
33863             var x = Roo.lib.Event.getPageX(e);
33864             var r = Roo.lib.Dom.getRegion(n.firstChild);
33865             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33866             var oldIndex = this.view.getCellIndex(h);
33867             var newIndex = this.view.getCellIndex(n);
33868             var locked = cm.isLocked(newIndex);
33869             if(pt == "after"){
33870                 newIndex++;
33871             }
33872             if(oldIndex < newIndex){
33873                 newIndex--;
33874             }
33875             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33876                 return false;
33877             }
33878             cm.setLocked(oldIndex, locked, true);
33879             cm.moveColumn(oldIndex, newIndex);
33880             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33881             return true;
33882         }
33883         return false;
33884     }
33885 });
33886 /*
33887  * Based on:
33888  * Ext JS Library 1.1.1
33889  * Copyright(c) 2006-2007, Ext JS, LLC.
33890  *
33891  * Originally Released Under LGPL - original licence link has changed is not relivant.
33892  *
33893  * Fork - LGPL
33894  * <script type="text/javascript">
33895  */
33896   
33897 /**
33898  * @class Roo.grid.GridView
33899  * @extends Roo.util.Observable
33900  *
33901  * @constructor
33902  * @param {Object} config
33903  */
33904 Roo.grid.GridView = function(config){
33905     Roo.grid.GridView.superclass.constructor.call(this);
33906     this.el = null;
33907
33908     Roo.apply(this, config);
33909 };
33910
33911 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33912
33913     /**
33914      * Override this function to apply custom css classes to rows during rendering
33915      * @param {Record} record The record
33916      * @param {Number} index
33917      * @method getRowClass
33918      */
33919     rowClass : "x-grid-row",
33920
33921     cellClass : "x-grid-col",
33922
33923     tdClass : "x-grid-td",
33924
33925     hdClass : "x-grid-hd",
33926
33927     splitClass : "x-grid-split",
33928
33929     sortClasses : ["sort-asc", "sort-desc"],
33930
33931     enableMoveAnim : false,
33932
33933     hlColor: "C3DAF9",
33934
33935     dh : Roo.DomHelper,
33936
33937     fly : Roo.Element.fly,
33938
33939     css : Roo.util.CSS,
33940
33941     borderWidth: 1,
33942
33943     splitOffset: 3,
33944
33945     scrollIncrement : 22,
33946
33947     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33948
33949     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33950
33951     bind : function(ds, cm){
33952         if(this.ds){
33953             this.ds.un("load", this.onLoad, this);
33954             this.ds.un("datachanged", this.onDataChange, this);
33955             this.ds.un("add", this.onAdd, this);
33956             this.ds.un("remove", this.onRemove, this);
33957             this.ds.un("update", this.onUpdate, this);
33958             this.ds.un("clear", this.onClear, this);
33959         }
33960         if(ds){
33961             ds.on("load", this.onLoad, this);
33962             ds.on("datachanged", this.onDataChange, this);
33963             ds.on("add", this.onAdd, this);
33964             ds.on("remove", this.onRemove, this);
33965             ds.on("update", this.onUpdate, this);
33966             ds.on("clear", this.onClear, this);
33967         }
33968         this.ds = ds;
33969
33970         if(this.cm){
33971             this.cm.un("widthchange", this.onColWidthChange, this);
33972             this.cm.un("headerchange", this.onHeaderChange, this);
33973             this.cm.un("hiddenchange", this.onHiddenChange, this);
33974             this.cm.un("columnmoved", this.onColumnMove, this);
33975             this.cm.un("columnlockchange", this.onColumnLock, this);
33976         }
33977         if(cm){
33978             this.generateRules(cm);
33979             cm.on("widthchange", this.onColWidthChange, this);
33980             cm.on("headerchange", this.onHeaderChange, this);
33981             cm.on("hiddenchange", this.onHiddenChange, this);
33982             cm.on("columnmoved", this.onColumnMove, this);
33983             cm.on("columnlockchange", this.onColumnLock, this);
33984         }
33985         this.cm = cm;
33986     },
33987
33988     init: function(grid){
33989         Roo.grid.GridView.superclass.init.call(this, grid);
33990
33991         this.bind(grid.dataSource, grid.colModel);
33992
33993         grid.on("headerclick", this.handleHeaderClick, this);
33994
33995         if(grid.trackMouseOver){
33996             grid.on("mouseover", this.onRowOver, this);
33997             grid.on("mouseout", this.onRowOut, this);
33998         }
33999         grid.cancelTextSelection = function(){};
34000         this.gridId = grid.id;
34001
34002         var tpls = this.templates || {};
34003
34004         if(!tpls.master){
34005             tpls.master = new Roo.Template(
34006                '<div class="x-grid" hidefocus="true">',
34007                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34008                   '<div class="x-grid-topbar"></div>',
34009                   '<div class="x-grid-scroller"><div></div></div>',
34010                   '<div class="x-grid-locked">',
34011                       '<div class="x-grid-header">{lockedHeader}</div>',
34012                       '<div class="x-grid-body">{lockedBody}</div>',
34013                   "</div>",
34014                   '<div class="x-grid-viewport">',
34015                       '<div class="x-grid-header">{header}</div>',
34016                       '<div class="x-grid-body">{body}</div>',
34017                   "</div>",
34018                   '<div class="x-grid-bottombar"></div>',
34019                  
34020                   '<div class="x-grid-resize-proxy">&#160;</div>',
34021                "</div>"
34022             );
34023             tpls.master.disableformats = true;
34024         }
34025
34026         if(!tpls.header){
34027             tpls.header = new Roo.Template(
34028                '<table border="0" cellspacing="0" cellpadding="0">',
34029                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34030                "</table>{splits}"
34031             );
34032             tpls.header.disableformats = true;
34033         }
34034         tpls.header.compile();
34035
34036         if(!tpls.hcell){
34037             tpls.hcell = new Roo.Template(
34038                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34039                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34040                 "</div></td>"
34041              );
34042              tpls.hcell.disableFormats = true;
34043         }
34044         tpls.hcell.compile();
34045
34046         if(!tpls.hsplit){
34047             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34048             tpls.hsplit.disableFormats = true;
34049         }
34050         tpls.hsplit.compile();
34051
34052         if(!tpls.body){
34053             tpls.body = new Roo.Template(
34054                '<table border="0" cellspacing="0" cellpadding="0">',
34055                "<tbody>{rows}</tbody>",
34056                "</table>"
34057             );
34058             tpls.body.disableFormats = true;
34059         }
34060         tpls.body.compile();
34061
34062         if(!tpls.row){
34063             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34064             tpls.row.disableFormats = true;
34065         }
34066         tpls.row.compile();
34067
34068         if(!tpls.cell){
34069             tpls.cell = new Roo.Template(
34070                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34071                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34072                 "</td>"
34073             );
34074             tpls.cell.disableFormats = true;
34075         }
34076         tpls.cell.compile();
34077
34078         this.templates = tpls;
34079     },
34080
34081     // remap these for backwards compat
34082     onColWidthChange : function(){
34083         this.updateColumns.apply(this, arguments);
34084     },
34085     onHeaderChange : function(){
34086         this.updateHeaders.apply(this, arguments);
34087     }, 
34088     onHiddenChange : function(){
34089         this.handleHiddenChange.apply(this, arguments);
34090     },
34091     onColumnMove : function(){
34092         this.handleColumnMove.apply(this, arguments);
34093     },
34094     onColumnLock : function(){
34095         this.handleLockChange.apply(this, arguments);
34096     },
34097
34098     onDataChange : function(){
34099         this.refresh();
34100         this.updateHeaderSortState();
34101     },
34102
34103     onClear : function(){
34104         this.refresh();
34105     },
34106
34107     onUpdate : function(ds, record){
34108         this.refreshRow(record);
34109     },
34110
34111     refreshRow : function(record){
34112         var ds = this.ds, index;
34113         if(typeof record == 'number'){
34114             index = record;
34115             record = ds.getAt(index);
34116         }else{
34117             index = ds.indexOf(record);
34118         }
34119         this.insertRows(ds, index, index, true);
34120         this.onRemove(ds, record, index+1, true);
34121         this.syncRowHeights(index, index);
34122         this.layout();
34123         this.fireEvent("rowupdated", this, index, record);
34124     },
34125
34126     onAdd : function(ds, records, index){
34127         this.insertRows(ds, index, index + (records.length-1));
34128     },
34129
34130     onRemove : function(ds, record, index, isUpdate){
34131         if(isUpdate !== true){
34132             this.fireEvent("beforerowremoved", this, index, record);
34133         }
34134         var bt = this.getBodyTable(), lt = this.getLockedTable();
34135         if(bt.rows[index]){
34136             bt.firstChild.removeChild(bt.rows[index]);
34137         }
34138         if(lt.rows[index]){
34139             lt.firstChild.removeChild(lt.rows[index]);
34140         }
34141         if(isUpdate !== true){
34142             this.stripeRows(index);
34143             this.syncRowHeights(index, index);
34144             this.layout();
34145             this.fireEvent("rowremoved", this, index, record);
34146         }
34147     },
34148
34149     onLoad : function(){
34150         this.scrollToTop();
34151     },
34152
34153     /**
34154      * Scrolls the grid to the top
34155      */
34156     scrollToTop : function(){
34157         if(this.scroller){
34158             this.scroller.dom.scrollTop = 0;
34159             this.syncScroll();
34160         }
34161     },
34162
34163     /**
34164      * Gets a panel in the header of the grid that can be used for toolbars etc.
34165      * After modifying the contents of this panel a call to grid.autoSize() may be
34166      * required to register any changes in size.
34167      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34168      * @return Roo.Element
34169      */
34170     getHeaderPanel : function(doShow){
34171         if(doShow){
34172             this.headerPanel.show();
34173         }
34174         return this.headerPanel;
34175     },
34176
34177     /**
34178      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34179      * After modifying the contents of this panel a call to grid.autoSize() may be
34180      * required to register any changes in size.
34181      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34182      * @return Roo.Element
34183      */
34184     getFooterPanel : function(doShow){
34185         if(doShow){
34186             this.footerPanel.show();
34187         }
34188         return this.footerPanel;
34189     },
34190
34191     initElements : function(){
34192         var E = Roo.Element;
34193         var el = this.grid.getGridEl().dom.firstChild;
34194         var cs = el.childNodes;
34195
34196         this.el = new E(el);
34197         
34198          this.focusEl = new E(el.firstChild);
34199         this.focusEl.swallowEvent("click", true);
34200         
34201         this.headerPanel = new E(cs[1]);
34202         this.headerPanel.enableDisplayMode("block");
34203
34204         this.scroller = new E(cs[2]);
34205         this.scrollSizer = new E(this.scroller.dom.firstChild);
34206
34207         this.lockedWrap = new E(cs[3]);
34208         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34209         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34210
34211         this.mainWrap = new E(cs[4]);
34212         this.mainHd = new E(this.mainWrap.dom.firstChild);
34213         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34214
34215         this.footerPanel = new E(cs[5]);
34216         this.footerPanel.enableDisplayMode("block");
34217
34218         this.resizeProxy = new E(cs[6]);
34219
34220         this.headerSelector = String.format(
34221            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34222            this.lockedHd.id, this.mainHd.id
34223         );
34224
34225         this.splitterSelector = String.format(
34226            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34227            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34228         );
34229     },
34230     idToCssName : function(s)
34231     {
34232         return s.replace(/[^a-z0-9]+/ig, '-');
34233     },
34234
34235     getHeaderCell : function(index){
34236         return Roo.DomQuery.select(this.headerSelector)[index];
34237     },
34238
34239     getHeaderCellMeasure : function(index){
34240         return this.getHeaderCell(index).firstChild;
34241     },
34242
34243     getHeaderCellText : function(index){
34244         return this.getHeaderCell(index).firstChild.firstChild;
34245     },
34246
34247     getLockedTable : function(){
34248         return this.lockedBody.dom.firstChild;
34249     },
34250
34251     getBodyTable : function(){
34252         return this.mainBody.dom.firstChild;
34253     },
34254
34255     getLockedRow : function(index){
34256         return this.getLockedTable().rows[index];
34257     },
34258
34259     getRow : function(index){
34260         return this.getBodyTable().rows[index];
34261     },
34262
34263     getRowComposite : function(index){
34264         if(!this.rowEl){
34265             this.rowEl = new Roo.CompositeElementLite();
34266         }
34267         var els = [], lrow, mrow;
34268         if(lrow = this.getLockedRow(index)){
34269             els.push(lrow);
34270         }
34271         if(mrow = this.getRow(index)){
34272             els.push(mrow);
34273         }
34274         this.rowEl.elements = els;
34275         return this.rowEl;
34276     },
34277     /**
34278      * Gets the 'td' of the cell
34279      * 
34280      * @param {Integer} rowIndex row to select
34281      * @param {Integer} colIndex column to select
34282      * 
34283      * @return {Object} 
34284      */
34285     getCell : function(rowIndex, colIndex){
34286         var locked = this.cm.getLockedCount();
34287         var source;
34288         if(colIndex < locked){
34289             source = this.lockedBody.dom.firstChild;
34290         }else{
34291             source = this.mainBody.dom.firstChild;
34292             colIndex -= locked;
34293         }
34294         return source.rows[rowIndex].childNodes[colIndex];
34295     },
34296
34297     getCellText : function(rowIndex, colIndex){
34298         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34299     },
34300
34301     getCellBox : function(cell){
34302         var b = this.fly(cell).getBox();
34303         if(Roo.isOpera){ // opera fails to report the Y
34304             b.y = cell.offsetTop + this.mainBody.getY();
34305         }
34306         return b;
34307     },
34308
34309     getCellIndex : function(cell){
34310         var id = String(cell.className).match(this.cellRE);
34311         if(id){
34312             return parseInt(id[1], 10);
34313         }
34314         return 0;
34315     },
34316
34317     findHeaderIndex : function(n){
34318         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34319         return r ? this.getCellIndex(r) : false;
34320     },
34321
34322     findHeaderCell : function(n){
34323         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34324         return r ? r : false;
34325     },
34326
34327     findRowIndex : function(n){
34328         if(!n){
34329             return false;
34330         }
34331         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34332         return r ? r.rowIndex : false;
34333     },
34334
34335     findCellIndex : function(node){
34336         var stop = this.el.dom;
34337         while(node && node != stop){
34338             if(this.findRE.test(node.className)){
34339                 return this.getCellIndex(node);
34340             }
34341             node = node.parentNode;
34342         }
34343         return false;
34344     },
34345
34346     getColumnId : function(index){
34347         return this.cm.getColumnId(index);
34348     },
34349
34350     getSplitters : function()
34351     {
34352         if(this.splitterSelector){
34353            return Roo.DomQuery.select(this.splitterSelector);
34354         }else{
34355             return null;
34356       }
34357     },
34358
34359     getSplitter : function(index){
34360         return this.getSplitters()[index];
34361     },
34362
34363     onRowOver : function(e, t){
34364         var row;
34365         if((row = this.findRowIndex(t)) !== false){
34366             this.getRowComposite(row).addClass("x-grid-row-over");
34367         }
34368     },
34369
34370     onRowOut : function(e, t){
34371         var row;
34372         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34373             this.getRowComposite(row).removeClass("x-grid-row-over");
34374         }
34375     },
34376
34377     renderHeaders : function(){
34378         var cm = this.cm;
34379         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
34380         var cb = [], lb = [], sb = [], lsb = [], p = {};
34381         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34382             p.cellId = "x-grid-hd-0-" + i;
34383             p.splitId = "x-grid-csplit-0-" + i;
34384             p.id = cm.getColumnId(i);
34385             p.title = cm.getColumnTooltip(i) || "";
34386             p.value = cm.getColumnHeader(i) || "";
34387             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
34388             if(!cm.isLocked(i)){
34389                 cb[cb.length] = ct.apply(p);
34390                 sb[sb.length] = st.apply(p);
34391             }else{
34392                 lb[lb.length] = ct.apply(p);
34393                 lsb[lsb.length] = st.apply(p);
34394             }
34395         }
34396         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
34397                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
34398     },
34399
34400     updateHeaders : function(){
34401         var html = this.renderHeaders();
34402         this.lockedHd.update(html[0]);
34403         this.mainHd.update(html[1]);
34404     },
34405
34406     /**
34407      * Focuses the specified row.
34408      * @param {Number} row The row index
34409      */
34410     focusRow : function(row)
34411     {
34412         //Roo.log('GridView.focusRow');
34413         var x = this.scroller.dom.scrollLeft;
34414         this.focusCell(row, 0, false);
34415         this.scroller.dom.scrollLeft = x;
34416     },
34417
34418     /**
34419      * Focuses the specified cell.
34420      * @param {Number} row The row index
34421      * @param {Number} col The column index
34422      * @param {Boolean} hscroll false to disable horizontal scrolling
34423      */
34424     focusCell : function(row, col, hscroll)
34425     {
34426         //Roo.log('GridView.focusCell');
34427         var el = this.ensureVisible(row, col, hscroll);
34428         this.focusEl.alignTo(el, "tl-tl");
34429         if(Roo.isGecko){
34430             this.focusEl.focus();
34431         }else{
34432             this.focusEl.focus.defer(1, this.focusEl);
34433         }
34434     },
34435
34436     /**
34437      * Scrolls the specified cell into view
34438      * @param {Number} row The row index
34439      * @param {Number} col The column index
34440      * @param {Boolean} hscroll false to disable horizontal scrolling
34441      */
34442     ensureVisible : function(row, col, hscroll)
34443     {
34444         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34445         //return null; //disable for testing.
34446         if(typeof row != "number"){
34447             row = row.rowIndex;
34448         }
34449         if(row < 0 && row >= this.ds.getCount()){
34450             return  null;
34451         }
34452         col = (col !== undefined ? col : 0);
34453         var cm = this.grid.colModel;
34454         while(cm.isHidden(col)){
34455             col++;
34456         }
34457
34458         var el = this.getCell(row, col);
34459         if(!el){
34460             return null;
34461         }
34462         var c = this.scroller.dom;
34463
34464         var ctop = parseInt(el.offsetTop, 10);
34465         var cleft = parseInt(el.offsetLeft, 10);
34466         var cbot = ctop + el.offsetHeight;
34467         var cright = cleft + el.offsetWidth;
34468         
34469         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34470         var stop = parseInt(c.scrollTop, 10);
34471         var sleft = parseInt(c.scrollLeft, 10);
34472         var sbot = stop + ch;
34473         var sright = sleft + c.clientWidth;
34474         /*
34475         Roo.log('GridView.ensureVisible:' +
34476                 ' ctop:' + ctop +
34477                 ' c.clientHeight:' + c.clientHeight +
34478                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34479                 ' stop:' + stop +
34480                 ' cbot:' + cbot +
34481                 ' sbot:' + sbot +
34482                 ' ch:' + ch  
34483                 );
34484         */
34485         if(ctop < stop){
34486              c.scrollTop = ctop;
34487             //Roo.log("set scrolltop to ctop DISABLE?");
34488         }else if(cbot > sbot){
34489             //Roo.log("set scrolltop to cbot-ch");
34490             c.scrollTop = cbot-ch;
34491         }
34492         
34493         if(hscroll !== false){
34494             if(cleft < sleft){
34495                 c.scrollLeft = cleft;
34496             }else if(cright > sright){
34497                 c.scrollLeft = cright-c.clientWidth;
34498             }
34499         }
34500          
34501         return el;
34502     },
34503
34504     updateColumns : function(){
34505         this.grid.stopEditing();
34506         var cm = this.grid.colModel, colIds = this.getColumnIds();
34507         //var totalWidth = cm.getTotalWidth();
34508         var pos = 0;
34509         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34510             //if(cm.isHidden(i)) continue;
34511             var w = cm.getColumnWidth(i);
34512             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34513             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34514         }
34515         this.updateSplitters();
34516     },
34517
34518     generateRules : function(cm){
34519         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34520         Roo.util.CSS.removeStyleSheet(rulesId);
34521         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34522             var cid = cm.getColumnId(i);
34523             var align = '';
34524             if(cm.config[i].align){
34525                 align = 'text-align:'+cm.config[i].align+';';
34526             }
34527             var hidden = '';
34528             if(cm.isHidden(i)){
34529                 hidden = 'display:none;';
34530             }
34531             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34532             ruleBuf.push(
34533                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34534                     this.hdSelector, cid, " {\n", align, width, "}\n",
34535                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34536                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34537         }
34538         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34539     },
34540
34541     updateSplitters : function(){
34542         var cm = this.cm, s = this.getSplitters();
34543         if(s){ // splitters not created yet
34544             var pos = 0, locked = true;
34545             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34546                 if(cm.isHidden(i)) continue;
34547                 var w = cm.getColumnWidth(i); // make sure it's a number
34548                 if(!cm.isLocked(i) && locked){
34549                     pos = 0;
34550                     locked = false;
34551                 }
34552                 pos += w;
34553                 s[i].style.left = (pos-this.splitOffset) + "px";
34554             }
34555         }
34556     },
34557
34558     handleHiddenChange : function(colModel, colIndex, hidden){
34559         if(hidden){
34560             this.hideColumn(colIndex);
34561         }else{
34562             this.unhideColumn(colIndex);
34563         }
34564     },
34565
34566     hideColumn : function(colIndex){
34567         var cid = this.getColumnId(colIndex);
34568         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34569         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34570         if(Roo.isSafari){
34571             this.updateHeaders();
34572         }
34573         this.updateSplitters();
34574         this.layout();
34575     },
34576
34577     unhideColumn : function(colIndex){
34578         var cid = this.getColumnId(colIndex);
34579         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34580         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34581
34582         if(Roo.isSafari){
34583             this.updateHeaders();
34584         }
34585         this.updateSplitters();
34586         this.layout();
34587     },
34588
34589     insertRows : function(dm, firstRow, lastRow, isUpdate){
34590         if(firstRow == 0 && lastRow == dm.getCount()-1){
34591             this.refresh();
34592         }else{
34593             if(!isUpdate){
34594                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34595             }
34596             var s = this.getScrollState();
34597             var markup = this.renderRows(firstRow, lastRow);
34598             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34599             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34600             this.restoreScroll(s);
34601             if(!isUpdate){
34602                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34603                 this.syncRowHeights(firstRow, lastRow);
34604                 this.stripeRows(firstRow);
34605                 this.layout();
34606             }
34607         }
34608     },
34609
34610     bufferRows : function(markup, target, index){
34611         var before = null, trows = target.rows, tbody = target.tBodies[0];
34612         if(index < trows.length){
34613             before = trows[index];
34614         }
34615         var b = document.createElement("div");
34616         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34617         var rows = b.firstChild.rows;
34618         for(var i = 0, len = rows.length; i < len; i++){
34619             if(before){
34620                 tbody.insertBefore(rows[0], before);
34621             }else{
34622                 tbody.appendChild(rows[0]);
34623             }
34624         }
34625         b.innerHTML = "";
34626         b = null;
34627     },
34628
34629     deleteRows : function(dm, firstRow, lastRow){
34630         if(dm.getRowCount()<1){
34631             this.fireEvent("beforerefresh", this);
34632             this.mainBody.update("");
34633             this.lockedBody.update("");
34634             this.fireEvent("refresh", this);
34635         }else{
34636             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34637             var bt = this.getBodyTable();
34638             var tbody = bt.firstChild;
34639             var rows = bt.rows;
34640             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34641                 tbody.removeChild(rows[firstRow]);
34642             }
34643             this.stripeRows(firstRow);
34644             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34645         }
34646     },
34647
34648     updateRows : function(dataSource, firstRow, lastRow){
34649         var s = this.getScrollState();
34650         this.refresh();
34651         this.restoreScroll(s);
34652     },
34653
34654     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34655         if(!noRefresh){
34656            this.refresh();
34657         }
34658         this.updateHeaderSortState();
34659     },
34660
34661     getScrollState : function(){
34662         
34663         var sb = this.scroller.dom;
34664         return {left: sb.scrollLeft, top: sb.scrollTop};
34665     },
34666
34667     stripeRows : function(startRow){
34668         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34669             return;
34670         }
34671         startRow = startRow || 0;
34672         var rows = this.getBodyTable().rows;
34673         var lrows = this.getLockedTable().rows;
34674         var cls = ' x-grid-row-alt ';
34675         for(var i = startRow, len = rows.length; i < len; i++){
34676             var row = rows[i], lrow = lrows[i];
34677             var isAlt = ((i+1) % 2 == 0);
34678             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34679             if(isAlt == hasAlt){
34680                 continue;
34681             }
34682             if(isAlt){
34683                 row.className += " x-grid-row-alt";
34684             }else{
34685                 row.className = row.className.replace("x-grid-row-alt", "");
34686             }
34687             if(lrow){
34688                 lrow.className = row.className;
34689             }
34690         }
34691     },
34692
34693     restoreScroll : function(state){
34694         //Roo.log('GridView.restoreScroll');
34695         var sb = this.scroller.dom;
34696         sb.scrollLeft = state.left;
34697         sb.scrollTop = state.top;
34698         this.syncScroll();
34699     },
34700
34701     syncScroll : function(){
34702         //Roo.log('GridView.syncScroll');
34703         var sb = this.scroller.dom;
34704         var sh = this.mainHd.dom;
34705         var bs = this.mainBody.dom;
34706         var lv = this.lockedBody.dom;
34707         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34708         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34709     },
34710
34711     handleScroll : function(e){
34712         this.syncScroll();
34713         var sb = this.scroller.dom;
34714         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34715         e.stopEvent();
34716     },
34717
34718     handleWheel : function(e){
34719         var d = e.getWheelDelta();
34720         this.scroller.dom.scrollTop -= d*22;
34721         // set this here to prevent jumpy scrolling on large tables
34722         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34723         e.stopEvent();
34724     },
34725
34726     renderRows : function(startRow, endRow){
34727         // pull in all the crap needed to render rows
34728         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34729         var colCount = cm.getColumnCount();
34730
34731         if(ds.getCount() < 1){
34732             return ["", ""];
34733         }
34734
34735         // build a map for all the columns
34736         var cs = [];
34737         for(var i = 0; i < colCount; i++){
34738             var name = cm.getDataIndex(i);
34739             cs[i] = {
34740                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34741                 renderer : cm.getRenderer(i),
34742                 id : cm.getColumnId(i),
34743                 locked : cm.isLocked(i)
34744             };
34745         }
34746
34747         startRow = startRow || 0;
34748         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34749
34750         // records to render
34751         var rs = ds.getRange(startRow, endRow);
34752
34753         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34754     },
34755
34756     // As much as I hate to duplicate code, this was branched because FireFox really hates
34757     // [].join("") on strings. The performance difference was substantial enough to
34758     // branch this function
34759     doRender : Roo.isGecko ?
34760             function(cs, rs, ds, startRow, colCount, stripe){
34761                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34762                 // buffers
34763                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34764                 
34765                 var hasListener = this.grid.hasListener('rowclass');
34766                 var rowcfg = {};
34767                 for(var j = 0, len = rs.length; j < len; j++){
34768                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34769                     for(var i = 0; i < colCount; i++){
34770                         c = cs[i];
34771                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34772                         p.id = c.id;
34773                         p.css = p.attr = "";
34774                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34775                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34776                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34777                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34778                         }
34779                         var markup = ct.apply(p);
34780                         if(!c.locked){
34781                             cb+= markup;
34782                         }else{
34783                             lcb+= markup;
34784                         }
34785                     }
34786                     var alt = [];
34787                     if(stripe && ((rowIndex+1) % 2 == 0)){
34788                         alt.push("x-grid-row-alt")
34789                     }
34790                     if(r.dirty){
34791                         alt.push(  " x-grid-dirty-row");
34792                     }
34793                     rp.cells = lcb;
34794                     if(this.getRowClass){
34795                         alt.push(this.getRowClass(r, rowIndex));
34796                     }
34797                     if (hasListener) {
34798                         rowcfg = {
34799                              
34800                             record: r,
34801                             rowIndex : rowIndex,
34802                             rowClass : ''
34803                         }
34804                         this.grid.fireEvent('rowclass', this, rowcfg);
34805                         alt.push(rowcfg.rowClass);
34806                     }
34807                     rp.alt = alt.join(" ");
34808                     lbuf+= rt.apply(rp);
34809                     rp.cells = cb;
34810                     buf+=  rt.apply(rp);
34811                 }
34812                 return [lbuf, buf];
34813             } :
34814             function(cs, rs, ds, startRow, colCount, stripe){
34815                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34816                 // buffers
34817                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34818                 var hasListener = this.grid.hasListener('rowclass');
34819                 var rowcfg = {};
34820                 for(var j = 0, len = rs.length; j < len; j++){
34821                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34822                     for(var i = 0; i < colCount; i++){
34823                         c = cs[i];
34824                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34825                         p.id = c.id;
34826                         p.css = p.attr = "";
34827                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34828                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34829                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34830                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34831                         }
34832                         var markup = ct.apply(p);
34833                         if(!c.locked){
34834                             cb[cb.length] = markup;
34835                         }else{
34836                             lcb[lcb.length] = markup;
34837                         }
34838                     }
34839                     var alt = [];
34840                     if(stripe && ((rowIndex+1) % 2 == 0)){
34841                         alt.push( "x-grid-row-alt");
34842                     }
34843                     if(r.dirty){
34844                         alt.push(" x-grid-dirty-row");
34845                     }
34846                     rp.cells = lcb;
34847                     if(this.getRowClass){
34848                         alt.push( this.getRowClass(r, rowIndex));
34849                     }
34850                     if (hasListener) {
34851                         rowcfg = {
34852                              
34853                             record: r,
34854                             rowIndex : rowIndex,
34855                             rowClass : ''
34856                         }
34857                         this.grid.fireEvent('rowclass', this, rowcfg);
34858                         alt.push(rowcfg.rowClass);
34859                     }
34860                     rp.alt = alt.join(" ");
34861                     rp.cells = lcb.join("");
34862                     lbuf[lbuf.length] = rt.apply(rp);
34863                     rp.cells = cb.join("");
34864                     buf[buf.length] =  rt.apply(rp);
34865                 }
34866                 return [lbuf.join(""), buf.join("")];
34867             },
34868
34869     renderBody : function(){
34870         var markup = this.renderRows();
34871         var bt = this.templates.body;
34872         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34873     },
34874
34875     /**
34876      * Refreshes the grid
34877      * @param {Boolean} headersToo
34878      */
34879     refresh : function(headersToo){
34880         this.fireEvent("beforerefresh", this);
34881         this.grid.stopEditing();
34882         var result = this.renderBody();
34883         this.lockedBody.update(result[0]);
34884         this.mainBody.update(result[1]);
34885         if(headersToo === true){
34886             this.updateHeaders();
34887             this.updateColumns();
34888             this.updateSplitters();
34889             this.updateHeaderSortState();
34890         }
34891         this.syncRowHeights();
34892         this.layout();
34893         this.fireEvent("refresh", this);
34894     },
34895
34896     handleColumnMove : function(cm, oldIndex, newIndex){
34897         this.indexMap = null;
34898         var s = this.getScrollState();
34899         this.refresh(true);
34900         this.restoreScroll(s);
34901         this.afterMove(newIndex);
34902     },
34903
34904     afterMove : function(colIndex){
34905         if(this.enableMoveAnim && Roo.enableFx){
34906             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34907         }
34908         // if multisort - fix sortOrder, and reload..
34909         if (this.grid.dataSource.multiSort) {
34910             // the we can call sort again..
34911             var dm = this.grid.dataSource;
34912             var cm = this.grid.colModel;
34913             var so = [];
34914             for(var i = 0; i < cm.config.length; i++ ) {
34915                 
34916                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34917                     continue; // dont' bother, it's not in sort list or being set.
34918                 }
34919                 
34920                 so.push(cm.config[i].dataIndex);
34921             };
34922             dm.sortOrder = so;
34923             dm.load(dm.lastOptions);
34924             
34925             
34926         }
34927         
34928     },
34929
34930     updateCell : function(dm, rowIndex, dataIndex){
34931         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34932         if(typeof colIndex == "undefined"){ // not present in grid
34933             return;
34934         }
34935         var cm = this.grid.colModel;
34936         var cell = this.getCell(rowIndex, colIndex);
34937         var cellText = this.getCellText(rowIndex, colIndex);
34938
34939         var p = {
34940             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34941             id : cm.getColumnId(colIndex),
34942             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34943         };
34944         var renderer = cm.getRenderer(colIndex);
34945         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34946         if(typeof val == "undefined" || val === "") val = "&#160;";
34947         cellText.innerHTML = val;
34948         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34949         this.syncRowHeights(rowIndex, rowIndex);
34950     },
34951
34952     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34953         var maxWidth = 0;
34954         if(this.grid.autoSizeHeaders){
34955             var h = this.getHeaderCellMeasure(colIndex);
34956             maxWidth = Math.max(maxWidth, h.scrollWidth);
34957         }
34958         var tb, index;
34959         if(this.cm.isLocked(colIndex)){
34960             tb = this.getLockedTable();
34961             index = colIndex;
34962         }else{
34963             tb = this.getBodyTable();
34964             index = colIndex - this.cm.getLockedCount();
34965         }
34966         if(tb && tb.rows){
34967             var rows = tb.rows;
34968             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34969             for(var i = 0; i < stopIndex; i++){
34970                 var cell = rows[i].childNodes[index].firstChild;
34971                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34972             }
34973         }
34974         return maxWidth + /*margin for error in IE*/ 5;
34975     },
34976     /**
34977      * Autofit a column to its content.
34978      * @param {Number} colIndex
34979      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34980      */
34981      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34982          if(this.cm.isHidden(colIndex)){
34983              return; // can't calc a hidden column
34984          }
34985         if(forceMinSize){
34986             var cid = this.cm.getColumnId(colIndex);
34987             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34988            if(this.grid.autoSizeHeaders){
34989                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34990            }
34991         }
34992         var newWidth = this.calcColumnWidth(colIndex);
34993         this.cm.setColumnWidth(colIndex,
34994             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34995         if(!suppressEvent){
34996             this.grid.fireEvent("columnresize", colIndex, newWidth);
34997         }
34998     },
34999
35000     /**
35001      * Autofits all columns to their content and then expands to fit any extra space in the grid
35002      */
35003      autoSizeColumns : function(){
35004         var cm = this.grid.colModel;
35005         var colCount = cm.getColumnCount();
35006         for(var i = 0; i < colCount; i++){
35007             this.autoSizeColumn(i, true, true);
35008         }
35009         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35010             this.fitColumns();
35011         }else{
35012             this.updateColumns();
35013             this.layout();
35014         }
35015     },
35016
35017     /**
35018      * Autofits all columns to the grid's width proportionate with their current size
35019      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35020      */
35021     fitColumns : function(reserveScrollSpace){
35022         var cm = this.grid.colModel;
35023         var colCount = cm.getColumnCount();
35024         var cols = [];
35025         var width = 0;
35026         var i, w;
35027         for (i = 0; i < colCount; i++){
35028             if(!cm.isHidden(i) && !cm.isFixed(i)){
35029                 w = cm.getColumnWidth(i);
35030                 cols.push(i);
35031                 cols.push(w);
35032                 width += w;
35033             }
35034         }
35035         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35036         if(reserveScrollSpace){
35037             avail -= 17;
35038         }
35039         var frac = (avail - cm.getTotalWidth())/width;
35040         while (cols.length){
35041             w = cols.pop();
35042             i = cols.pop();
35043             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35044         }
35045         this.updateColumns();
35046         this.layout();
35047     },
35048
35049     onRowSelect : function(rowIndex){
35050         var row = this.getRowComposite(rowIndex);
35051         row.addClass("x-grid-row-selected");
35052     },
35053
35054     onRowDeselect : function(rowIndex){
35055         var row = this.getRowComposite(rowIndex);
35056         row.removeClass("x-grid-row-selected");
35057     },
35058
35059     onCellSelect : function(row, col){
35060         var cell = this.getCell(row, col);
35061         if(cell){
35062             Roo.fly(cell).addClass("x-grid-cell-selected");
35063         }
35064     },
35065
35066     onCellDeselect : function(row, col){
35067         var cell = this.getCell(row, col);
35068         if(cell){
35069             Roo.fly(cell).removeClass("x-grid-cell-selected");
35070         }
35071     },
35072
35073     updateHeaderSortState : function(){
35074         
35075         // sort state can be single { field: xxx, direction : yyy}
35076         // or   { xxx=>ASC , yyy : DESC ..... }
35077         
35078         var mstate = {};
35079         if (!this.ds.multiSort) { 
35080             var state = this.ds.getSortState();
35081             if(!state){
35082                 return;
35083             }
35084             mstate[state.field] = state.direction;
35085             // FIXME... - this is not used here.. but might be elsewhere..
35086             this.sortState = state;
35087             
35088         } else {
35089             mstate = this.ds.sortToggle;
35090         }
35091         //remove existing sort classes..
35092         
35093         var sc = this.sortClasses;
35094         var hds = this.el.select(this.headerSelector).removeClass(sc);
35095         
35096         for(var f in mstate) {
35097         
35098             var sortColumn = this.cm.findColumnIndex(f);
35099             
35100             if(sortColumn != -1){
35101                 var sortDir = mstate[f];        
35102                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35103             }
35104         }
35105         
35106          
35107         
35108     },
35109
35110
35111     handleHeaderClick : function(g, index){
35112         if(this.headersDisabled){
35113             return;
35114         }
35115         var dm = g.dataSource, cm = g.colModel;
35116         if(!cm.isSortable(index)){
35117             return;
35118         }
35119         g.stopEditing();
35120         
35121         if (dm.multiSort) {
35122             // update the sortOrder
35123             var so = [];
35124             for(var i = 0; i < cm.config.length; i++ ) {
35125                 
35126                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35127                     continue; // dont' bother, it's not in sort list or being set.
35128                 }
35129                 
35130                 so.push(cm.config[i].dataIndex);
35131             };
35132             dm.sortOrder = so;
35133         }
35134         
35135         
35136         dm.sort(cm.getDataIndex(index));
35137     },
35138
35139
35140     destroy : function(){
35141         if(this.colMenu){
35142             this.colMenu.removeAll();
35143             Roo.menu.MenuMgr.unregister(this.colMenu);
35144             this.colMenu.getEl().remove();
35145             delete this.colMenu;
35146         }
35147         if(this.hmenu){
35148             this.hmenu.removeAll();
35149             Roo.menu.MenuMgr.unregister(this.hmenu);
35150             this.hmenu.getEl().remove();
35151             delete this.hmenu;
35152         }
35153         if(this.grid.enableColumnMove){
35154             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35155             if(dds){
35156                 for(var dd in dds){
35157                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35158                         var elid = dds[dd].dragElId;
35159                         dds[dd].unreg();
35160                         Roo.get(elid).remove();
35161                     } else if(dds[dd].config.isTarget){
35162                         dds[dd].proxyTop.remove();
35163                         dds[dd].proxyBottom.remove();
35164                         dds[dd].unreg();
35165                     }
35166                     if(Roo.dd.DDM.locationCache[dd]){
35167                         delete Roo.dd.DDM.locationCache[dd];
35168                     }
35169                 }
35170                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35171             }
35172         }
35173         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35174         this.bind(null, null);
35175         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35176     },
35177
35178     handleLockChange : function(){
35179         this.refresh(true);
35180     },
35181
35182     onDenyColumnLock : function(){
35183
35184     },
35185
35186     onDenyColumnHide : function(){
35187
35188     },
35189
35190     handleHdMenuClick : function(item){
35191         var index = this.hdCtxIndex;
35192         var cm = this.cm, ds = this.ds;
35193         switch(item.id){
35194             case "asc":
35195                 ds.sort(cm.getDataIndex(index), "ASC");
35196                 break;
35197             case "desc":
35198                 ds.sort(cm.getDataIndex(index), "DESC");
35199                 break;
35200             case "lock":
35201                 var lc = cm.getLockedCount();
35202                 if(cm.getColumnCount(true) <= lc+1){
35203                     this.onDenyColumnLock();
35204                     return;
35205                 }
35206                 if(lc != index){
35207                     cm.setLocked(index, true, true);
35208                     cm.moveColumn(index, lc);
35209                     this.grid.fireEvent("columnmove", index, lc);
35210                 }else{
35211                     cm.setLocked(index, true);
35212                 }
35213             break;
35214             case "unlock":
35215                 var lc = cm.getLockedCount();
35216                 if((lc-1) != index){
35217                     cm.setLocked(index, false, true);
35218                     cm.moveColumn(index, lc-1);
35219                     this.grid.fireEvent("columnmove", index, lc-1);
35220                 }else{
35221                     cm.setLocked(index, false);
35222                 }
35223             break;
35224             default:
35225                 index = cm.getIndexById(item.id.substr(4));
35226                 if(index != -1){
35227                     if(item.checked && cm.getColumnCount(true) <= 1){
35228                         this.onDenyColumnHide();
35229                         return false;
35230                     }
35231                     cm.setHidden(index, item.checked);
35232                 }
35233         }
35234         return true;
35235     },
35236
35237     beforeColMenuShow : function(){
35238         var cm = this.cm,  colCount = cm.getColumnCount();
35239         this.colMenu.removeAll();
35240         for(var i = 0; i < colCount; i++){
35241             this.colMenu.add(new Roo.menu.CheckItem({
35242                 id: "col-"+cm.getColumnId(i),
35243                 text: cm.getColumnHeader(i),
35244                 checked: !cm.isHidden(i),
35245                 hideOnClick:false
35246             }));
35247         }
35248     },
35249
35250     handleHdCtx : function(g, index, e){
35251         e.stopEvent();
35252         var hd = this.getHeaderCell(index);
35253         this.hdCtxIndex = index;
35254         var ms = this.hmenu.items, cm = this.cm;
35255         ms.get("asc").setDisabled(!cm.isSortable(index));
35256         ms.get("desc").setDisabled(!cm.isSortable(index));
35257         if(this.grid.enableColLock !== false){
35258             ms.get("lock").setDisabled(cm.isLocked(index));
35259             ms.get("unlock").setDisabled(!cm.isLocked(index));
35260         }
35261         this.hmenu.show(hd, "tl-bl");
35262     },
35263
35264     handleHdOver : function(e){
35265         var hd = this.findHeaderCell(e.getTarget());
35266         if(hd && !this.headersDisabled){
35267             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35268                this.fly(hd).addClass("x-grid-hd-over");
35269             }
35270         }
35271     },
35272
35273     handleHdOut : function(e){
35274         var hd = this.findHeaderCell(e.getTarget());
35275         if(hd){
35276             this.fly(hd).removeClass("x-grid-hd-over");
35277         }
35278     },
35279
35280     handleSplitDblClick : function(e, t){
35281         var i = this.getCellIndex(t);
35282         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35283             this.autoSizeColumn(i, true);
35284             this.layout();
35285         }
35286     },
35287
35288     render : function(){
35289
35290         var cm = this.cm;
35291         var colCount = cm.getColumnCount();
35292
35293         if(this.grid.monitorWindowResize === true){
35294             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35295         }
35296         var header = this.renderHeaders();
35297         var body = this.templates.body.apply({rows:""});
35298         var html = this.templates.master.apply({
35299             lockedBody: body,
35300             body: body,
35301             lockedHeader: header[0],
35302             header: header[1]
35303         });
35304
35305         //this.updateColumns();
35306
35307         this.grid.getGridEl().dom.innerHTML = html;
35308
35309         this.initElements();
35310         
35311         // a kludge to fix the random scolling effect in webkit
35312         this.el.on("scroll", function() {
35313             this.el.dom.scrollTop=0; // hopefully not recursive..
35314         },this);
35315
35316         this.scroller.on("scroll", this.handleScroll, this);
35317         this.lockedBody.on("mousewheel", this.handleWheel, this);
35318         this.mainBody.on("mousewheel", this.handleWheel, this);
35319
35320         this.mainHd.on("mouseover", this.handleHdOver, this);
35321         this.mainHd.on("mouseout", this.handleHdOut, this);
35322         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35323                 {delegate: "."+this.splitClass});
35324
35325         this.lockedHd.on("mouseover", this.handleHdOver, this);
35326         this.lockedHd.on("mouseout", this.handleHdOut, this);
35327         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35328                 {delegate: "."+this.splitClass});
35329
35330         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35331             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35332         }
35333
35334         this.updateSplitters();
35335
35336         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35337             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35338             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35339         }
35340
35341         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35342             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35343             this.hmenu.add(
35344                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35345                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35346             );
35347             if(this.grid.enableColLock !== false){
35348                 this.hmenu.add('-',
35349                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35350                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35351                 );
35352             }
35353             if(this.grid.enableColumnHide !== false){
35354
35355                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35356                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35357                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35358
35359                 this.hmenu.add('-',
35360                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35361                 );
35362             }
35363             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35364
35365             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35366         }
35367
35368         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35369             this.dd = new Roo.grid.GridDragZone(this.grid, {
35370                 ddGroup : this.grid.ddGroup || 'GridDD'
35371             });
35372         }
35373
35374         /*
35375         for(var i = 0; i < colCount; i++){
35376             if(cm.isHidden(i)){
35377                 this.hideColumn(i);
35378             }
35379             if(cm.config[i].align){
35380                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35381                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35382             }
35383         }*/
35384         
35385         this.updateHeaderSortState();
35386
35387         this.beforeInitialResize();
35388         this.layout(true);
35389
35390         // two part rendering gives faster view to the user
35391         this.renderPhase2.defer(1, this);
35392     },
35393
35394     renderPhase2 : function(){
35395         // render the rows now
35396         this.refresh();
35397         if(this.grid.autoSizeColumns){
35398             this.autoSizeColumns();
35399         }
35400     },
35401
35402     beforeInitialResize : function(){
35403
35404     },
35405
35406     onColumnSplitterMoved : function(i, w){
35407         this.userResized = true;
35408         var cm = this.grid.colModel;
35409         cm.setColumnWidth(i, w, true);
35410         var cid = cm.getColumnId(i);
35411         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35412         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35413         this.updateSplitters();
35414         this.layout();
35415         this.grid.fireEvent("columnresize", i, w);
35416     },
35417
35418     syncRowHeights : function(startIndex, endIndex){
35419         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35420             startIndex = startIndex || 0;
35421             var mrows = this.getBodyTable().rows;
35422             var lrows = this.getLockedTable().rows;
35423             var len = mrows.length-1;
35424             endIndex = Math.min(endIndex || len, len);
35425             for(var i = startIndex; i <= endIndex; i++){
35426                 var m = mrows[i], l = lrows[i];
35427                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35428                 m.style.height = l.style.height = h + "px";
35429             }
35430         }
35431     },
35432
35433     layout : function(initialRender, is2ndPass){
35434         var g = this.grid;
35435         var auto = g.autoHeight;
35436         var scrollOffset = 16;
35437         var c = g.getGridEl(), cm = this.cm,
35438                 expandCol = g.autoExpandColumn,
35439                 gv = this;
35440         //c.beginMeasure();
35441
35442         if(!c.dom.offsetWidth){ // display:none?
35443             if(initialRender){
35444                 this.lockedWrap.show();
35445                 this.mainWrap.show();
35446             }
35447             return;
35448         }
35449
35450         var hasLock = this.cm.isLocked(0);
35451
35452         var tbh = this.headerPanel.getHeight();
35453         var bbh = this.footerPanel.getHeight();
35454
35455         if(auto){
35456             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35457             var newHeight = ch + c.getBorderWidth("tb");
35458             if(g.maxHeight){
35459                 newHeight = Math.min(g.maxHeight, newHeight);
35460             }
35461             c.setHeight(newHeight);
35462         }
35463
35464         if(g.autoWidth){
35465             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35466         }
35467
35468         var s = this.scroller;
35469
35470         var csize = c.getSize(true);
35471
35472         this.el.setSize(csize.width, csize.height);
35473
35474         this.headerPanel.setWidth(csize.width);
35475         this.footerPanel.setWidth(csize.width);
35476
35477         var hdHeight = this.mainHd.getHeight();
35478         var vw = csize.width;
35479         var vh = csize.height - (tbh + bbh);
35480
35481         s.setSize(vw, vh);
35482
35483         var bt = this.getBodyTable();
35484         var ltWidth = hasLock ?
35485                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35486
35487         var scrollHeight = bt.offsetHeight;
35488         var scrollWidth = ltWidth + bt.offsetWidth;
35489         var vscroll = false, hscroll = false;
35490
35491         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35492
35493         var lw = this.lockedWrap, mw = this.mainWrap;
35494         var lb = this.lockedBody, mb = this.mainBody;
35495
35496         setTimeout(function(){
35497             var t = s.dom.offsetTop;
35498             var w = s.dom.clientWidth,
35499                 h = s.dom.clientHeight;
35500
35501             lw.setTop(t);
35502             lw.setSize(ltWidth, h);
35503
35504             mw.setLeftTop(ltWidth, t);
35505             mw.setSize(w-ltWidth, h);
35506
35507             lb.setHeight(h-hdHeight);
35508             mb.setHeight(h-hdHeight);
35509
35510             if(is2ndPass !== true && !gv.userResized && expandCol){
35511                 // high speed resize without full column calculation
35512                 
35513                 var ci = cm.getIndexById(expandCol);
35514                 if (ci < 0) {
35515                     ci = cm.findColumnIndex(expandCol);
35516                 }
35517                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35518                 var expandId = cm.getColumnId(ci);
35519                 var  tw = cm.getTotalWidth(false);
35520                 var currentWidth = cm.getColumnWidth(ci);
35521                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35522                 if(currentWidth != cw){
35523                     cm.setColumnWidth(ci, cw, true);
35524                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35525                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35526                     gv.updateSplitters();
35527                     gv.layout(false, true);
35528                 }
35529             }
35530
35531             if(initialRender){
35532                 lw.show();
35533                 mw.show();
35534             }
35535             //c.endMeasure();
35536         }, 10);
35537     },
35538
35539     onWindowResize : function(){
35540         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35541             return;
35542         }
35543         this.layout();
35544     },
35545
35546     appendFooter : function(parentEl){
35547         return null;
35548     },
35549
35550     sortAscText : "Sort Ascending",
35551     sortDescText : "Sort Descending",
35552     lockText : "Lock Column",
35553     unlockText : "Unlock Column",
35554     columnsText : "Columns"
35555 });
35556
35557
35558 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35559     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35560     this.proxy.el.addClass('x-grid3-col-dd');
35561 };
35562
35563 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35564     handleMouseDown : function(e){
35565
35566     },
35567
35568     callHandleMouseDown : function(e){
35569         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35570     }
35571 });
35572 /*
35573  * Based on:
35574  * Ext JS Library 1.1.1
35575  * Copyright(c) 2006-2007, Ext JS, LLC.
35576  *
35577  * Originally Released Under LGPL - original licence link has changed is not relivant.
35578  *
35579  * Fork - LGPL
35580  * <script type="text/javascript">
35581  */
35582  
35583 // private
35584 // This is a support class used internally by the Grid components
35585 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35586     this.grid = grid;
35587     this.view = grid.getView();
35588     this.proxy = this.view.resizeProxy;
35589     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35590         "gridSplitters" + this.grid.getGridEl().id, {
35591         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35592     });
35593     this.setHandleElId(Roo.id(hd));
35594     this.setOuterHandleElId(Roo.id(hd2));
35595     this.scroll = false;
35596 };
35597 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35598     fly: Roo.Element.fly,
35599
35600     b4StartDrag : function(x, y){
35601         this.view.headersDisabled = true;
35602         this.proxy.setHeight(this.view.mainWrap.getHeight());
35603         var w = this.cm.getColumnWidth(this.cellIndex);
35604         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35605         this.resetConstraints();
35606         this.setXConstraint(minw, 1000);
35607         this.setYConstraint(0, 0);
35608         this.minX = x - minw;
35609         this.maxX = x + 1000;
35610         this.startPos = x;
35611         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35612     },
35613
35614
35615     handleMouseDown : function(e){
35616         ev = Roo.EventObject.setEvent(e);
35617         var t = this.fly(ev.getTarget());
35618         if(t.hasClass("x-grid-split")){
35619             this.cellIndex = this.view.getCellIndex(t.dom);
35620             this.split = t.dom;
35621             this.cm = this.grid.colModel;
35622             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35623                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35624             }
35625         }
35626     },
35627
35628     endDrag : function(e){
35629         this.view.headersDisabled = false;
35630         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35631         var diff = endX - this.startPos;
35632         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35633     },
35634
35635     autoOffset : function(){
35636         this.setDelta(0,0);
35637     }
35638 });/*
35639  * Based on:
35640  * Ext JS Library 1.1.1
35641  * Copyright(c) 2006-2007, Ext JS, LLC.
35642  *
35643  * Originally Released Under LGPL - original licence link has changed is not relivant.
35644  *
35645  * Fork - LGPL
35646  * <script type="text/javascript">
35647  */
35648  
35649 // private
35650 // This is a support class used internally by the Grid components
35651 Roo.grid.GridDragZone = function(grid, config){
35652     this.view = grid.getView();
35653     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35654     if(this.view.lockedBody){
35655         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35656         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35657     }
35658     this.scroll = false;
35659     this.grid = grid;
35660     this.ddel = document.createElement('div');
35661     this.ddel.className = 'x-grid-dd-wrap';
35662 };
35663
35664 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35665     ddGroup : "GridDD",
35666
35667     getDragData : function(e){
35668         var t = Roo.lib.Event.getTarget(e);
35669         var rowIndex = this.view.findRowIndex(t);
35670         if(rowIndex !== false){
35671             var sm = this.grid.selModel;
35672             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35673               //  sm.mouseDown(e, t);
35674             //}
35675             if (e.hasModifier()){
35676                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35677             }
35678             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
35679         }
35680         return false;
35681     },
35682
35683     onInitDrag : function(e){
35684         var data = this.dragData;
35685         this.ddel.innerHTML = this.grid.getDragDropText();
35686         this.proxy.update(this.ddel);
35687         // fire start drag?
35688     },
35689
35690     afterRepair : function(){
35691         this.dragging = false;
35692     },
35693
35694     getRepairXY : function(e, data){
35695         return false;
35696     },
35697
35698     onEndDrag : function(data, e){
35699         // fire end drag?
35700     },
35701
35702     onValidDrop : function(dd, e, id){
35703         // fire drag drop?
35704         this.hideProxy();
35705     },
35706
35707     beforeInvalidDrop : function(e, id){
35708
35709     }
35710 });/*
35711  * Based on:
35712  * Ext JS Library 1.1.1
35713  * Copyright(c) 2006-2007, Ext JS, LLC.
35714  *
35715  * Originally Released Under LGPL - original licence link has changed is not relivant.
35716  *
35717  * Fork - LGPL
35718  * <script type="text/javascript">
35719  */
35720  
35721
35722 /**
35723  * @class Roo.grid.ColumnModel
35724  * @extends Roo.util.Observable
35725  * This is the default implementation of a ColumnModel used by the Grid. It defines
35726  * the columns in the grid.
35727  * <br>Usage:<br>
35728  <pre><code>
35729  var colModel = new Roo.grid.ColumnModel([
35730         {header: "Ticker", width: 60, sortable: true, locked: true},
35731         {header: "Company Name", width: 150, sortable: true},
35732         {header: "Market Cap.", width: 100, sortable: true},
35733         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35734         {header: "Employees", width: 100, sortable: true, resizable: false}
35735  ]);
35736  </code></pre>
35737  * <p>
35738  
35739  * The config options listed for this class are options which may appear in each
35740  * individual column definition.
35741  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35742  * @constructor
35743  * @param {Object} config An Array of column config objects. See this class's
35744  * config objects for details.
35745 */
35746 Roo.grid.ColumnModel = function(config){
35747         /**
35748      * The config passed into the constructor
35749      */
35750     this.config = config;
35751     this.lookup = {};
35752
35753     // if no id, create one
35754     // if the column does not have a dataIndex mapping,
35755     // map it to the order it is in the config
35756     for(var i = 0, len = config.length; i < len; i++){
35757         var c = config[i];
35758         if(typeof c.dataIndex == "undefined"){
35759             c.dataIndex = i;
35760         }
35761         if(typeof c.renderer == "string"){
35762             c.renderer = Roo.util.Format[c.renderer];
35763         }
35764         if(typeof c.id == "undefined"){
35765             c.id = Roo.id();
35766         }
35767         if(c.editor && c.editor.xtype){
35768             c.editor  = Roo.factory(c.editor, Roo.grid);
35769         }
35770         if(c.editor && c.editor.isFormField){
35771             c.editor = new Roo.grid.GridEditor(c.editor);
35772         }
35773         this.lookup[c.id] = c;
35774     }
35775
35776     /**
35777      * The width of columns which have no width specified (defaults to 100)
35778      * @type Number
35779      */
35780     this.defaultWidth = 100;
35781
35782     /**
35783      * Default sortable of columns which have no sortable specified (defaults to false)
35784      * @type Boolean
35785      */
35786     this.defaultSortable = false;
35787
35788     this.addEvents({
35789         /**
35790              * @event widthchange
35791              * Fires when the width of a column changes.
35792              * @param {ColumnModel} this
35793              * @param {Number} columnIndex The column index
35794              * @param {Number} newWidth The new width
35795              */
35796             "widthchange": true,
35797         /**
35798              * @event headerchange
35799              * Fires when the text of a header changes.
35800              * @param {ColumnModel} this
35801              * @param {Number} columnIndex The column index
35802              * @param {Number} newText The new header text
35803              */
35804             "headerchange": true,
35805         /**
35806              * @event hiddenchange
35807              * Fires when a column is hidden or "unhidden".
35808              * @param {ColumnModel} this
35809              * @param {Number} columnIndex The column index
35810              * @param {Boolean} hidden true if hidden, false otherwise
35811              */
35812             "hiddenchange": true,
35813             /**
35814          * @event columnmoved
35815          * Fires when a column is moved.
35816          * @param {ColumnModel} this
35817          * @param {Number} oldIndex
35818          * @param {Number} newIndex
35819          */
35820         "columnmoved" : true,
35821         /**
35822          * @event columlockchange
35823          * Fires when a column's locked state is changed
35824          * @param {ColumnModel} this
35825          * @param {Number} colIndex
35826          * @param {Boolean} locked true if locked
35827          */
35828         "columnlockchange" : true
35829     });
35830     Roo.grid.ColumnModel.superclass.constructor.call(this);
35831 };
35832 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35833     /**
35834      * @cfg {String} header The header text to display in the Grid view.
35835      */
35836     /**
35837      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35838      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35839      * specified, the column's index is used as an index into the Record's data Array.
35840      */
35841     /**
35842      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35843      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35844      */
35845     /**
35846      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35847      * Defaults to the value of the {@link #defaultSortable} property.
35848      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35849      */
35850     /**
35851      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35852      */
35853     /**
35854      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35855      */
35856     /**
35857      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35858      */
35859     /**
35860      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35861      */
35862     /**
35863      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35864      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35865      * default renderer uses the raw data value.
35866      */
35867        /**
35868      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35869      */
35870     /**
35871      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35872      */
35873
35874     /**
35875      * Returns the id of the column at the specified index.
35876      * @param {Number} index The column index
35877      * @return {String} the id
35878      */
35879     getColumnId : function(index){
35880         return this.config[index].id;
35881     },
35882
35883     /**
35884      * Returns the column for a specified id.
35885      * @param {String} id The column id
35886      * @return {Object} the column
35887      */
35888     getColumnById : function(id){
35889         return this.lookup[id];
35890     },
35891
35892     
35893     /**
35894      * Returns the column for a specified dataIndex.
35895      * @param {String} dataIndex The column dataIndex
35896      * @return {Object|Boolean} the column or false if not found
35897      */
35898     getColumnByDataIndex: function(dataIndex){
35899         var index = this.findColumnIndex(dataIndex);
35900         return index > -1 ? this.config[index] : false;
35901     },
35902     
35903     /**
35904      * Returns the index for a specified column id.
35905      * @param {String} id The column id
35906      * @return {Number} the index, or -1 if not found
35907      */
35908     getIndexById : function(id){
35909         for(var i = 0, len = this.config.length; i < len; i++){
35910             if(this.config[i].id == id){
35911                 return i;
35912             }
35913         }
35914         return -1;
35915     },
35916     
35917     /**
35918      * Returns the index for a specified column dataIndex.
35919      * @param {String} dataIndex The column dataIndex
35920      * @return {Number} the index, or -1 if not found
35921      */
35922     
35923     findColumnIndex : function(dataIndex){
35924         for(var i = 0, len = this.config.length; i < len; i++){
35925             if(this.config[i].dataIndex == dataIndex){
35926                 return i;
35927             }
35928         }
35929         return -1;
35930     },
35931     
35932     
35933     moveColumn : function(oldIndex, newIndex){
35934         var c = this.config[oldIndex];
35935         this.config.splice(oldIndex, 1);
35936         this.config.splice(newIndex, 0, c);
35937         this.dataMap = null;
35938         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35939     },
35940
35941     isLocked : function(colIndex){
35942         return this.config[colIndex].locked === true;
35943     },
35944
35945     setLocked : function(colIndex, value, suppressEvent){
35946         if(this.isLocked(colIndex) == value){
35947             return;
35948         }
35949         this.config[colIndex].locked = value;
35950         if(!suppressEvent){
35951             this.fireEvent("columnlockchange", this, colIndex, value);
35952         }
35953     },
35954
35955     getTotalLockedWidth : function(){
35956         var totalWidth = 0;
35957         for(var i = 0; i < this.config.length; i++){
35958             if(this.isLocked(i) && !this.isHidden(i)){
35959                 this.totalWidth += this.getColumnWidth(i);
35960             }
35961         }
35962         return totalWidth;
35963     },
35964
35965     getLockedCount : function(){
35966         for(var i = 0, len = this.config.length; i < len; i++){
35967             if(!this.isLocked(i)){
35968                 return i;
35969             }
35970         }
35971     },
35972
35973     /**
35974      * Returns the number of columns.
35975      * @return {Number}
35976      */
35977     getColumnCount : function(visibleOnly){
35978         if(visibleOnly === true){
35979             var c = 0;
35980             for(var i = 0, len = this.config.length; i < len; i++){
35981                 if(!this.isHidden(i)){
35982                     c++;
35983                 }
35984             }
35985             return c;
35986         }
35987         return this.config.length;
35988     },
35989
35990     /**
35991      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35992      * @param {Function} fn
35993      * @param {Object} scope (optional)
35994      * @return {Array} result
35995      */
35996     getColumnsBy : function(fn, scope){
35997         var r = [];
35998         for(var i = 0, len = this.config.length; i < len; i++){
35999             var c = this.config[i];
36000             if(fn.call(scope||this, c, i) === true){
36001                 r[r.length] = c;
36002             }
36003         }
36004         return r;
36005     },
36006
36007     /**
36008      * Returns true if the specified column is sortable.
36009      * @param {Number} col The column index
36010      * @return {Boolean}
36011      */
36012     isSortable : function(col){
36013         if(typeof this.config[col].sortable == "undefined"){
36014             return this.defaultSortable;
36015         }
36016         return this.config[col].sortable;
36017     },
36018
36019     /**
36020      * Returns the rendering (formatting) function defined for the column.
36021      * @param {Number} col The column index.
36022      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36023      */
36024     getRenderer : function(col){
36025         if(!this.config[col].renderer){
36026             return Roo.grid.ColumnModel.defaultRenderer;
36027         }
36028         return this.config[col].renderer;
36029     },
36030
36031     /**
36032      * Sets the rendering (formatting) function for a column.
36033      * @param {Number} col The column index
36034      * @param {Function} fn The function to use to process the cell's raw data
36035      * to return HTML markup for the grid view. The render function is called with
36036      * the following parameters:<ul>
36037      * <li>Data value.</li>
36038      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36039      * <li>css A CSS style string to apply to the table cell.</li>
36040      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36041      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36042      * <li>Row index</li>
36043      * <li>Column index</li>
36044      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36045      */
36046     setRenderer : function(col, fn){
36047         this.config[col].renderer = fn;
36048     },
36049
36050     /**
36051      * Returns the width for the specified column.
36052      * @param {Number} col The column index
36053      * @return {Number}
36054      */
36055     getColumnWidth : function(col){
36056         return this.config[col].width * 1 || this.defaultWidth;
36057     },
36058
36059     /**
36060      * Sets the width for a column.
36061      * @param {Number} col The column index
36062      * @param {Number} width The new width
36063      */
36064     setColumnWidth : function(col, width, suppressEvent){
36065         this.config[col].width = width;
36066         this.totalWidth = null;
36067         if(!suppressEvent){
36068              this.fireEvent("widthchange", this, col, width);
36069         }
36070     },
36071
36072     /**
36073      * Returns the total width of all columns.
36074      * @param {Boolean} includeHidden True to include hidden column widths
36075      * @return {Number}
36076      */
36077     getTotalWidth : function(includeHidden){
36078         if(!this.totalWidth){
36079             this.totalWidth = 0;
36080             for(var i = 0, len = this.config.length; i < len; i++){
36081                 if(includeHidden || !this.isHidden(i)){
36082                     this.totalWidth += this.getColumnWidth(i);
36083                 }
36084             }
36085         }
36086         return this.totalWidth;
36087     },
36088
36089     /**
36090      * Returns the header for the specified column.
36091      * @param {Number} col The column index
36092      * @return {String}
36093      */
36094     getColumnHeader : function(col){
36095         return this.config[col].header;
36096     },
36097
36098     /**
36099      * Sets the header for a column.
36100      * @param {Number} col The column index
36101      * @param {String} header The new header
36102      */
36103     setColumnHeader : function(col, header){
36104         this.config[col].header = header;
36105         this.fireEvent("headerchange", this, col, header);
36106     },
36107
36108     /**
36109      * Returns the tooltip for the specified column.
36110      * @param {Number} col The column index
36111      * @return {String}
36112      */
36113     getColumnTooltip : function(col){
36114             return this.config[col].tooltip;
36115     },
36116     /**
36117      * Sets the tooltip for a column.
36118      * @param {Number} col The column index
36119      * @param {String} tooltip The new tooltip
36120      */
36121     setColumnTooltip : function(col, tooltip){
36122             this.config[col].tooltip = tooltip;
36123     },
36124
36125     /**
36126      * Returns the dataIndex for the specified column.
36127      * @param {Number} col The column index
36128      * @return {Number}
36129      */
36130     getDataIndex : function(col){
36131         return this.config[col].dataIndex;
36132     },
36133
36134     /**
36135      * Sets the dataIndex for a column.
36136      * @param {Number} col The column index
36137      * @param {Number} dataIndex The new dataIndex
36138      */
36139     setDataIndex : function(col, dataIndex){
36140         this.config[col].dataIndex = dataIndex;
36141     },
36142
36143     
36144     
36145     /**
36146      * Returns true if the cell is editable.
36147      * @param {Number} colIndex The column index
36148      * @param {Number} rowIndex The row index
36149      * @return {Boolean}
36150      */
36151     isCellEditable : function(colIndex, rowIndex){
36152         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36153     },
36154
36155     /**
36156      * Returns the editor defined for the cell/column.
36157      * return false or null to disable editing.
36158      * @param {Number} colIndex The column index
36159      * @param {Number} rowIndex The row index
36160      * @return {Object}
36161      */
36162     getCellEditor : function(colIndex, rowIndex){
36163         return this.config[colIndex].editor;
36164     },
36165
36166     /**
36167      * Sets if a column is editable.
36168      * @param {Number} col The column index
36169      * @param {Boolean} editable True if the column is editable
36170      */
36171     setEditable : function(col, editable){
36172         this.config[col].editable = editable;
36173     },
36174
36175
36176     /**
36177      * Returns true if the column is hidden.
36178      * @param {Number} colIndex The column index
36179      * @return {Boolean}
36180      */
36181     isHidden : function(colIndex){
36182         return this.config[colIndex].hidden;
36183     },
36184
36185
36186     /**
36187      * Returns true if the column width cannot be changed
36188      */
36189     isFixed : function(colIndex){
36190         return this.config[colIndex].fixed;
36191     },
36192
36193     /**
36194      * Returns true if the column can be resized
36195      * @return {Boolean}
36196      */
36197     isResizable : function(colIndex){
36198         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36199     },
36200     /**
36201      * Sets if a column is hidden.
36202      * @param {Number} colIndex The column index
36203      * @param {Boolean} hidden True if the column is hidden
36204      */
36205     setHidden : function(colIndex, hidden){
36206         this.config[colIndex].hidden = hidden;
36207         this.totalWidth = null;
36208         this.fireEvent("hiddenchange", this, colIndex, hidden);
36209     },
36210
36211     /**
36212      * Sets the editor for a column.
36213      * @param {Number} col The column index
36214      * @param {Object} editor The editor object
36215      */
36216     setEditor : function(col, editor){
36217         this.config[col].editor = editor;
36218     }
36219 });
36220
36221 Roo.grid.ColumnModel.defaultRenderer = function(value){
36222         if(typeof value == "string" && value.length < 1){
36223             return "&#160;";
36224         }
36225         return value;
36226 };
36227
36228 // Alias for backwards compatibility
36229 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36230 /*
36231  * Based on:
36232  * Ext JS Library 1.1.1
36233  * Copyright(c) 2006-2007, Ext JS, LLC.
36234  *
36235  * Originally Released Under LGPL - original licence link has changed is not relivant.
36236  *
36237  * Fork - LGPL
36238  * <script type="text/javascript">
36239  */
36240
36241 /**
36242  * @class Roo.grid.AbstractSelectionModel
36243  * @extends Roo.util.Observable
36244  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36245  * implemented by descendant classes.  This class should not be directly instantiated.
36246  * @constructor
36247  */
36248 Roo.grid.AbstractSelectionModel = function(){
36249     this.locked = false;
36250     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36251 };
36252
36253 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36254     /** @ignore Called by the grid automatically. Do not call directly. */
36255     init : function(grid){
36256         this.grid = grid;
36257         this.initEvents();
36258     },
36259
36260     /**
36261      * Locks the selections.
36262      */
36263     lock : function(){
36264         this.locked = true;
36265     },
36266
36267     /**
36268      * Unlocks the selections.
36269      */
36270     unlock : function(){
36271         this.locked = false;
36272     },
36273
36274     /**
36275      * Returns true if the selections are locked.
36276      * @return {Boolean}
36277      */
36278     isLocked : function(){
36279         return this.locked;
36280     }
36281 });/*
36282  * Based on:
36283  * Ext JS Library 1.1.1
36284  * Copyright(c) 2006-2007, Ext JS, LLC.
36285  *
36286  * Originally Released Under LGPL - original licence link has changed is not relivant.
36287  *
36288  * Fork - LGPL
36289  * <script type="text/javascript">
36290  */
36291 /**
36292  * @extends Roo.grid.AbstractSelectionModel
36293  * @class Roo.grid.RowSelectionModel
36294  * The default SelectionModel used by {@link Roo.grid.Grid}.
36295  * It supports multiple selections and keyboard selection/navigation. 
36296  * @constructor
36297  * @param {Object} config
36298  */
36299 Roo.grid.RowSelectionModel = function(config){
36300     Roo.apply(this, config);
36301     this.selections = new Roo.util.MixedCollection(false, function(o){
36302         return o.id;
36303     });
36304
36305     this.last = false;
36306     this.lastActive = false;
36307
36308     this.addEvents({
36309         /**
36310              * @event selectionchange
36311              * Fires when the selection changes
36312              * @param {SelectionModel} this
36313              */
36314             "selectionchange" : true,
36315         /**
36316              * @event afterselectionchange
36317              * Fires after the selection changes (eg. by key press or clicking)
36318              * @param {SelectionModel} this
36319              */
36320             "afterselectionchange" : true,
36321         /**
36322              * @event beforerowselect
36323              * Fires when a row is selected being selected, return false to cancel.
36324              * @param {SelectionModel} this
36325              * @param {Number} rowIndex The selected index
36326              * @param {Boolean} keepExisting False if other selections will be cleared
36327              */
36328             "beforerowselect" : true,
36329         /**
36330              * @event rowselect
36331              * Fires when a row is selected.
36332              * @param {SelectionModel} this
36333              * @param {Number} rowIndex The selected index
36334              * @param {Roo.data.Record} r The record
36335              */
36336             "rowselect" : true,
36337         /**
36338              * @event rowdeselect
36339              * Fires when a row is deselected.
36340              * @param {SelectionModel} this
36341              * @param {Number} rowIndex The selected index
36342              */
36343         "rowdeselect" : true
36344     });
36345     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36346     this.locked = false;
36347 };
36348
36349 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36350     /**
36351      * @cfg {Boolean} singleSelect
36352      * True to allow selection of only one row at a time (defaults to false)
36353      */
36354     singleSelect : false,
36355
36356     // private
36357     initEvents : function(){
36358
36359         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36360             this.grid.on("mousedown", this.handleMouseDown, this);
36361         }else{ // allow click to work like normal
36362             this.grid.on("rowclick", this.handleDragableRowClick, this);
36363         }
36364
36365         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36366             "up" : function(e){
36367                 if(!e.shiftKey){
36368                     this.selectPrevious(e.shiftKey);
36369                 }else if(this.last !== false && this.lastActive !== false){
36370                     var last = this.last;
36371                     this.selectRange(this.last,  this.lastActive-1);
36372                     this.grid.getView().focusRow(this.lastActive);
36373                     if(last !== false){
36374                         this.last = last;
36375                     }
36376                 }else{
36377                     this.selectFirstRow();
36378                 }
36379                 this.fireEvent("afterselectionchange", this);
36380             },
36381             "down" : function(e){
36382                 if(!e.shiftKey){
36383                     this.selectNext(e.shiftKey);
36384                 }else if(this.last !== false && this.lastActive !== false){
36385                     var last = this.last;
36386                     this.selectRange(this.last,  this.lastActive+1);
36387                     this.grid.getView().focusRow(this.lastActive);
36388                     if(last !== false){
36389                         this.last = last;
36390                     }
36391                 }else{
36392                     this.selectFirstRow();
36393                 }
36394                 this.fireEvent("afterselectionchange", this);
36395             },
36396             scope: this
36397         });
36398
36399         var view = this.grid.view;
36400         view.on("refresh", this.onRefresh, this);
36401         view.on("rowupdated", this.onRowUpdated, this);
36402         view.on("rowremoved", this.onRemove, this);
36403     },
36404
36405     // private
36406     onRefresh : function(){
36407         var ds = this.grid.dataSource, i, v = this.grid.view;
36408         var s = this.selections;
36409         s.each(function(r){
36410             if((i = ds.indexOfId(r.id)) != -1){
36411                 v.onRowSelect(i);
36412             }else{
36413                 s.remove(r);
36414             }
36415         });
36416     },
36417
36418     // private
36419     onRemove : function(v, index, r){
36420         this.selections.remove(r);
36421     },
36422
36423     // private
36424     onRowUpdated : function(v, index, r){
36425         if(this.isSelected(r)){
36426             v.onRowSelect(index);
36427         }
36428     },
36429
36430     /**
36431      * Select records.
36432      * @param {Array} records The records to select
36433      * @param {Boolean} keepExisting (optional) True to keep existing selections
36434      */
36435     selectRecords : function(records, keepExisting){
36436         if(!keepExisting){
36437             this.clearSelections();
36438         }
36439         var ds = this.grid.dataSource;
36440         for(var i = 0, len = records.length; i < len; i++){
36441             this.selectRow(ds.indexOf(records[i]), true);
36442         }
36443     },
36444
36445     /**
36446      * Gets the number of selected rows.
36447      * @return {Number}
36448      */
36449     getCount : function(){
36450         return this.selections.length;
36451     },
36452
36453     /**
36454      * Selects the first row in the grid.
36455      */
36456     selectFirstRow : function(){
36457         this.selectRow(0);
36458     },
36459
36460     /**
36461      * Select the last row.
36462      * @param {Boolean} keepExisting (optional) True to keep existing selections
36463      */
36464     selectLastRow : function(keepExisting){
36465         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36466     },
36467
36468     /**
36469      * Selects the row immediately following the last selected row.
36470      * @param {Boolean} keepExisting (optional) True to keep existing selections
36471      */
36472     selectNext : function(keepExisting){
36473         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36474             this.selectRow(this.last+1, keepExisting);
36475             this.grid.getView().focusRow(this.last);
36476         }
36477     },
36478
36479     /**
36480      * Selects the row that precedes the last selected row.
36481      * @param {Boolean} keepExisting (optional) True to keep existing selections
36482      */
36483     selectPrevious : function(keepExisting){
36484         if(this.last){
36485             this.selectRow(this.last-1, keepExisting);
36486             this.grid.getView().focusRow(this.last);
36487         }
36488     },
36489
36490     /**
36491      * Returns the selected records
36492      * @return {Array} Array of selected records
36493      */
36494     getSelections : function(){
36495         return [].concat(this.selections.items);
36496     },
36497
36498     /**
36499      * Returns the first selected record.
36500      * @return {Record}
36501      */
36502     getSelected : function(){
36503         return this.selections.itemAt(0);
36504     },
36505
36506
36507     /**
36508      * Clears all selections.
36509      */
36510     clearSelections : function(fast){
36511         if(this.locked) return;
36512         if(fast !== true){
36513             var ds = this.grid.dataSource;
36514             var s = this.selections;
36515             s.each(function(r){
36516                 this.deselectRow(ds.indexOfId(r.id));
36517             }, this);
36518             s.clear();
36519         }else{
36520             this.selections.clear();
36521         }
36522         this.last = false;
36523     },
36524
36525
36526     /**
36527      * Selects all rows.
36528      */
36529     selectAll : function(){
36530         if(this.locked) return;
36531         this.selections.clear();
36532         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36533             this.selectRow(i, true);
36534         }
36535     },
36536
36537     /**
36538      * Returns True if there is a selection.
36539      * @return {Boolean}
36540      */
36541     hasSelection : function(){
36542         return this.selections.length > 0;
36543     },
36544
36545     /**
36546      * Returns True if the specified row is selected.
36547      * @param {Number/Record} record The record or index of the record to check
36548      * @return {Boolean}
36549      */
36550     isSelected : function(index){
36551         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36552         return (r && this.selections.key(r.id) ? true : false);
36553     },
36554
36555     /**
36556      * Returns True if the specified record id is selected.
36557      * @param {String} id The id of record to check
36558      * @return {Boolean}
36559      */
36560     isIdSelected : function(id){
36561         return (this.selections.key(id) ? true : false);
36562     },
36563
36564     // private
36565     handleMouseDown : function(e, t){
36566         var view = this.grid.getView(), rowIndex;
36567         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36568             return;
36569         };
36570         if(e.shiftKey && this.last !== false){
36571             var last = this.last;
36572             this.selectRange(last, rowIndex, e.ctrlKey);
36573             this.last = last; // reset the last
36574             view.focusRow(rowIndex);
36575         }else{
36576             var isSelected = this.isSelected(rowIndex);
36577             if(e.button !== 0 && isSelected){
36578                 view.focusRow(rowIndex);
36579             }else if(e.ctrlKey && isSelected){
36580                 this.deselectRow(rowIndex);
36581             }else if(!isSelected){
36582                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36583                 view.focusRow(rowIndex);
36584             }
36585         }
36586         this.fireEvent("afterselectionchange", this);
36587     },
36588     // private
36589     handleDragableRowClick :  function(grid, rowIndex, e) 
36590     {
36591         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36592             this.selectRow(rowIndex, false);
36593             grid.view.focusRow(rowIndex);
36594              this.fireEvent("afterselectionchange", this);
36595         }
36596     },
36597     
36598     /**
36599      * Selects multiple rows.
36600      * @param {Array} rows Array of the indexes of the row to select
36601      * @param {Boolean} keepExisting (optional) True to keep existing selections
36602      */
36603     selectRows : function(rows, keepExisting){
36604         if(!keepExisting){
36605             this.clearSelections();
36606         }
36607         for(var i = 0, len = rows.length; i < len; i++){
36608             this.selectRow(rows[i], true);
36609         }
36610     },
36611
36612     /**
36613      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36614      * @param {Number} startRow The index of the first row in the range
36615      * @param {Number} endRow The index of the last row in the range
36616      * @param {Boolean} keepExisting (optional) True to retain existing selections
36617      */
36618     selectRange : function(startRow, endRow, keepExisting){
36619         if(this.locked) return;
36620         if(!keepExisting){
36621             this.clearSelections();
36622         }
36623         if(startRow <= endRow){
36624             for(var i = startRow; i <= endRow; i++){
36625                 this.selectRow(i, true);
36626             }
36627         }else{
36628             for(var i = startRow; i >= endRow; i--){
36629                 this.selectRow(i, true);
36630             }
36631         }
36632     },
36633
36634     /**
36635      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36636      * @param {Number} startRow The index of the first row in the range
36637      * @param {Number} endRow The index of the last row in the range
36638      */
36639     deselectRange : function(startRow, endRow, preventViewNotify){
36640         if(this.locked) return;
36641         for(var i = startRow; i <= endRow; i++){
36642             this.deselectRow(i, preventViewNotify);
36643         }
36644     },
36645
36646     /**
36647      * Selects a row.
36648      * @param {Number} row The index of the row to select
36649      * @param {Boolean} keepExisting (optional) True to keep existing selections
36650      */
36651     selectRow : function(index, keepExisting, preventViewNotify){
36652         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
36653         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36654             if(!keepExisting || this.singleSelect){
36655                 this.clearSelections();
36656             }
36657             var r = this.grid.dataSource.getAt(index);
36658             this.selections.add(r);
36659             this.last = this.lastActive = index;
36660             if(!preventViewNotify){
36661                 this.grid.getView().onRowSelect(index);
36662             }
36663             this.fireEvent("rowselect", this, index, r);
36664             this.fireEvent("selectionchange", this);
36665         }
36666     },
36667
36668     /**
36669      * Deselects a row.
36670      * @param {Number} row The index of the row to deselect
36671      */
36672     deselectRow : function(index, preventViewNotify){
36673         if(this.locked) return;
36674         if(this.last == index){
36675             this.last = false;
36676         }
36677         if(this.lastActive == index){
36678             this.lastActive = false;
36679         }
36680         var r = this.grid.dataSource.getAt(index);
36681         this.selections.remove(r);
36682         if(!preventViewNotify){
36683             this.grid.getView().onRowDeselect(index);
36684         }
36685         this.fireEvent("rowdeselect", this, index);
36686         this.fireEvent("selectionchange", this);
36687     },
36688
36689     // private
36690     restoreLast : function(){
36691         if(this._last){
36692             this.last = this._last;
36693         }
36694     },
36695
36696     // private
36697     acceptsNav : function(row, col, cm){
36698         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36699     },
36700
36701     // private
36702     onEditorKey : function(field, e){
36703         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36704         if(k == e.TAB){
36705             e.stopEvent();
36706             ed.completeEdit();
36707             if(e.shiftKey){
36708                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36709             }else{
36710                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36711             }
36712         }else if(k == e.ENTER && !e.ctrlKey){
36713             e.stopEvent();
36714             ed.completeEdit();
36715             if(e.shiftKey){
36716                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36717             }else{
36718                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36719             }
36720         }else if(k == e.ESC){
36721             ed.cancelEdit();
36722         }
36723         if(newCell){
36724             g.startEditing(newCell[0], newCell[1]);
36725         }
36726     }
36727 });/*
36728  * Based on:
36729  * Ext JS Library 1.1.1
36730  * Copyright(c) 2006-2007, Ext JS, LLC.
36731  *
36732  * Originally Released Under LGPL - original licence link has changed is not relivant.
36733  *
36734  * Fork - LGPL
36735  * <script type="text/javascript">
36736  */
36737 /**
36738  * @class Roo.grid.CellSelectionModel
36739  * @extends Roo.grid.AbstractSelectionModel
36740  * This class provides the basic implementation for cell selection in a grid.
36741  * @constructor
36742  * @param {Object} config The object containing the configuration of this model.
36743  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36744  */
36745 Roo.grid.CellSelectionModel = function(config){
36746     Roo.apply(this, config);
36747
36748     this.selection = null;
36749
36750     this.addEvents({
36751         /**
36752              * @event beforerowselect
36753              * Fires before a cell is selected.
36754              * @param {SelectionModel} this
36755              * @param {Number} rowIndex The selected row index
36756              * @param {Number} colIndex The selected cell index
36757              */
36758             "beforecellselect" : true,
36759         /**
36760              * @event cellselect
36761              * Fires when a cell is selected.
36762              * @param {SelectionModel} this
36763              * @param {Number} rowIndex The selected row index
36764              * @param {Number} colIndex The selected cell index
36765              */
36766             "cellselect" : true,
36767         /**
36768              * @event selectionchange
36769              * Fires when the active selection changes.
36770              * @param {SelectionModel} this
36771              * @param {Object} selection null for no selection or an object (o) with two properties
36772                 <ul>
36773                 <li>o.record: the record object for the row the selection is in</li>
36774                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36775                 </ul>
36776              */
36777             "selectionchange" : true,
36778         /**
36779              * @event tabend
36780              * Fires when the tab (or enter) was pressed on the last editable cell
36781              * You can use this to trigger add new row.
36782              * @param {SelectionModel} this
36783              */
36784             "tabend" : true
36785     });
36786     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36787 };
36788
36789 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36790     
36791     enter_is_tab: false,
36792
36793     /** @ignore */
36794     initEvents : function(){
36795         this.grid.on("mousedown", this.handleMouseDown, this);
36796         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36797         var view = this.grid.view;
36798         view.on("refresh", this.onViewChange, this);
36799         view.on("rowupdated", this.onRowUpdated, this);
36800         view.on("beforerowremoved", this.clearSelections, this);
36801         view.on("beforerowsinserted", this.clearSelections, this);
36802         if(this.grid.isEditor){
36803             this.grid.on("beforeedit", this.beforeEdit,  this);
36804         }
36805     },
36806
36807         //private
36808     beforeEdit : function(e){
36809         this.select(e.row, e.column, false, true, e.record);
36810     },
36811
36812         //private
36813     onRowUpdated : function(v, index, r){
36814         if(this.selection && this.selection.record == r){
36815             v.onCellSelect(index, this.selection.cell[1]);
36816         }
36817     },
36818
36819         //private
36820     onViewChange : function(){
36821         this.clearSelections(true);
36822     },
36823
36824         /**
36825          * Returns the currently selected cell,.
36826          * @return {Array} The selected cell (row, column) or null if none selected.
36827          */
36828     getSelectedCell : function(){
36829         return this.selection ? this.selection.cell : null;
36830     },
36831
36832     /**
36833      * Clears all selections.
36834      * @param {Boolean} true to prevent the gridview from being notified about the change.
36835      */
36836     clearSelections : function(preventNotify){
36837         var s = this.selection;
36838         if(s){
36839             if(preventNotify !== true){
36840                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36841             }
36842             this.selection = null;
36843             this.fireEvent("selectionchange", this, null);
36844         }
36845     },
36846
36847     /**
36848      * Returns true if there is a selection.
36849      * @return {Boolean}
36850      */
36851     hasSelection : function(){
36852         return this.selection ? true : false;
36853     },
36854
36855     /** @ignore */
36856     handleMouseDown : function(e, t){
36857         var v = this.grid.getView();
36858         if(this.isLocked()){
36859             return;
36860         };
36861         var row = v.findRowIndex(t);
36862         var cell = v.findCellIndex(t);
36863         if(row !== false && cell !== false){
36864             this.select(row, cell);
36865         }
36866     },
36867
36868     /**
36869      * Selects a cell.
36870      * @param {Number} rowIndex
36871      * @param {Number} collIndex
36872      */
36873     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36874         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36875             this.clearSelections();
36876             r = r || this.grid.dataSource.getAt(rowIndex);
36877             this.selection = {
36878                 record : r,
36879                 cell : [rowIndex, colIndex]
36880             };
36881             if(!preventViewNotify){
36882                 var v = this.grid.getView();
36883                 v.onCellSelect(rowIndex, colIndex);
36884                 if(preventFocus !== true){
36885                     v.focusCell(rowIndex, colIndex);
36886                 }
36887             }
36888             this.fireEvent("cellselect", this, rowIndex, colIndex);
36889             this.fireEvent("selectionchange", this, this.selection);
36890         }
36891     },
36892
36893         //private
36894     isSelectable : function(rowIndex, colIndex, cm){
36895         return !cm.isHidden(colIndex);
36896     },
36897
36898     /** @ignore */
36899     handleKeyDown : function(e){
36900         //Roo.log('Cell Sel Model handleKeyDown');
36901         if(!e.isNavKeyPress()){
36902             return;
36903         }
36904         var g = this.grid, s = this.selection;
36905         if(!s){
36906             e.stopEvent();
36907             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36908             if(cell){
36909                 this.select(cell[0], cell[1]);
36910             }
36911             return;
36912         }
36913         var sm = this;
36914         var walk = function(row, col, step){
36915             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36916         };
36917         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36918         var newCell;
36919
36920       
36921
36922         switch(k){
36923             case e.TAB:
36924                 // handled by onEditorKey
36925                 if (g.isEditor && g.editing) {
36926                     return;
36927                 }
36928                 if(e.shiftKey) {
36929                     newCell = walk(r, c-1, -1);
36930                 } else {
36931                     newCell = walk(r, c+1, 1);
36932                 }
36933                 break;
36934             
36935             case e.DOWN:
36936                newCell = walk(r+1, c, 1);
36937                 break;
36938             
36939             case e.UP:
36940                 newCell = walk(r-1, c, -1);
36941                 break;
36942             
36943             case e.RIGHT:
36944                 newCell = walk(r, c+1, 1);
36945                 break;
36946             
36947             case e.LEFT:
36948                 newCell = walk(r, c-1, -1);
36949                 break;
36950             
36951             case e.ENTER:
36952                 
36953                 if(g.isEditor && !g.editing){
36954                    g.startEditing(r, c);
36955                    e.stopEvent();
36956                    return;
36957                 }
36958                 
36959                 
36960              break;
36961         };
36962         if(newCell){
36963             this.select(newCell[0], newCell[1]);
36964             e.stopEvent();
36965             
36966         }
36967     },
36968
36969     acceptsNav : function(row, col, cm){
36970         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36971     },
36972     /**
36973      * Selects a cell.
36974      * @param {Number} field (not used) - as it's normally used as a listener
36975      * @param {Number} e - event - fake it by using
36976      *
36977      * var e = Roo.EventObjectImpl.prototype;
36978      * e.keyCode = e.TAB
36979      *
36980      * 
36981      */
36982     onEditorKey : function(field, e){
36983         
36984         var k = e.getKey(),
36985             newCell,
36986             g = this.grid,
36987             ed = g.activeEditor,
36988             forward = false;
36989         ///Roo.log('onEditorKey' + k);
36990         
36991         
36992         if (this.enter_is_tab && k == e.ENTER) {
36993             k = e.TAB;
36994         }
36995         
36996         if(k == e.TAB){
36997             if(e.shiftKey){
36998                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36999             }else{
37000                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37001                 forward = true;
37002             }
37003             
37004             e.stopEvent();
37005             
37006         }else if(k == e.ENTER &&  !e.ctrlKey){
37007             ed.completeEdit();
37008             e.stopEvent();
37009             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37010         }else if(k == e.ESC){
37011             ed.cancelEdit();
37012         }
37013         
37014         
37015         if(newCell){
37016             //Roo.log('next cell after edit');
37017             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37018         } else if (forward) {
37019             // tabbed past last
37020             this.fireEvent.defer(100, this, ['tabend',this]);
37021         }
37022     }
37023 });/*
37024  * Based on:
37025  * Ext JS Library 1.1.1
37026  * Copyright(c) 2006-2007, Ext JS, LLC.
37027  *
37028  * Originally Released Under LGPL - original licence link has changed is not relivant.
37029  *
37030  * Fork - LGPL
37031  * <script type="text/javascript">
37032  */
37033  
37034 /**
37035  * @class Roo.grid.EditorGrid
37036  * @extends Roo.grid.Grid
37037  * Class for creating and editable grid.
37038  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37039  * The container MUST have some type of size defined for the grid to fill. The container will be 
37040  * automatically set to position relative if it isn't already.
37041  * @param {Object} dataSource The data model to bind to
37042  * @param {Object} colModel The column model with info about this grid's columns
37043  */
37044 Roo.grid.EditorGrid = function(container, config){
37045     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37046     this.getGridEl().addClass("xedit-grid");
37047
37048     if(!this.selModel){
37049         this.selModel = new Roo.grid.CellSelectionModel();
37050     }
37051
37052     this.activeEditor = null;
37053
37054         this.addEvents({
37055             /**
37056              * @event beforeedit
37057              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37058              * <ul style="padding:5px;padding-left:16px;">
37059              * <li>grid - This grid</li>
37060              * <li>record - The record being edited</li>
37061              * <li>field - The field name being edited</li>
37062              * <li>value - The value for the field being edited.</li>
37063              * <li>row - The grid row index</li>
37064              * <li>column - The grid column index</li>
37065              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37066              * </ul>
37067              * @param {Object} e An edit event (see above for description)
37068              */
37069             "beforeedit" : true,
37070             /**
37071              * @event afteredit
37072              * Fires after a cell is edited. <br />
37073              * <ul style="padding:5px;padding-left:16px;">
37074              * <li>grid - This grid</li>
37075              * <li>record - The record being edited</li>
37076              * <li>field - The field name being edited</li>
37077              * <li>value - The value being set</li>
37078              * <li>originalValue - The original value for the field, before the edit.</li>
37079              * <li>row - The grid row index</li>
37080              * <li>column - The grid column index</li>
37081              * </ul>
37082              * @param {Object} e An edit event (see above for description)
37083              */
37084             "afteredit" : true,
37085             /**
37086              * @event validateedit
37087              * Fires after a cell is edited, but before the value is set in the record. 
37088          * You can use this to modify the value being set in the field, Return false
37089              * to cancel the change. The edit event object has the following properties <br />
37090              * <ul style="padding:5px;padding-left:16px;">
37091          * <li>editor - This editor</li>
37092              * <li>grid - This grid</li>
37093              * <li>record - The record being edited</li>
37094              * <li>field - The field name being edited</li>
37095              * <li>value - The value being set</li>
37096              * <li>originalValue - The original value for the field, before the edit.</li>
37097              * <li>row - The grid row index</li>
37098              * <li>column - The grid column index</li>
37099              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37100              * </ul>
37101              * @param {Object} e An edit event (see above for description)
37102              */
37103             "validateedit" : true
37104         });
37105     this.on("bodyscroll", this.stopEditing,  this);
37106     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37107 };
37108
37109 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37110     /**
37111      * @cfg {Number} clicksToEdit
37112      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37113      */
37114     clicksToEdit: 2,
37115
37116     // private
37117     isEditor : true,
37118     // private
37119     trackMouseOver: false, // causes very odd FF errors
37120
37121     onCellDblClick : function(g, row, col){
37122         this.startEditing(row, col);
37123     },
37124
37125     onEditComplete : function(ed, value, startValue){
37126         this.editing = false;
37127         this.activeEditor = null;
37128         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37129         var r = ed.record;
37130         var field = this.colModel.getDataIndex(ed.col);
37131         var e = {
37132             grid: this,
37133             record: r,
37134             field: field,
37135             originalValue: startValue,
37136             value: value,
37137             row: ed.row,
37138             column: ed.col,
37139             cancel:false,
37140             editor: ed
37141         };
37142         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37143         cell.show();
37144           
37145         if(String(value) !== String(startValue)){
37146             
37147             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37148                 r.set(field, e.value);
37149                 // if we are dealing with a combo box..
37150                 // then we also set the 'name' colum to be the displayField
37151                 if (ed.field.displayField && ed.field.name) {
37152                     r.set(ed.field.name, ed.field.el.dom.value);
37153                 }
37154                 
37155                 delete e.cancel; //?? why!!!
37156                 this.fireEvent("afteredit", e);
37157             }
37158         } else {
37159             this.fireEvent("afteredit", e); // always fire it!
37160         }
37161         this.view.focusCell(ed.row, ed.col);
37162     },
37163
37164     /**
37165      * Starts editing the specified for the specified row/column
37166      * @param {Number} rowIndex
37167      * @param {Number} colIndex
37168      */
37169     startEditing : function(row, col){
37170         this.stopEditing();
37171         if(this.colModel.isCellEditable(col, row)){
37172             this.view.ensureVisible(row, col, true);
37173           
37174             var r = this.dataSource.getAt(row);
37175             var field = this.colModel.getDataIndex(col);
37176             var cell = Roo.get(this.view.getCell(row,col));
37177             var e = {
37178                 grid: this,
37179                 record: r,
37180                 field: field,
37181                 value: r.data[field],
37182                 row: row,
37183                 column: col,
37184                 cancel:false 
37185             };
37186             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37187                 this.editing = true;
37188                 var ed = this.colModel.getCellEditor(col, row);
37189                 
37190                 if (!ed) {
37191                     return;
37192                 }
37193                 if(!ed.rendered){
37194                     ed.render(ed.parentEl || document.body);
37195                 }
37196                 ed.field.reset();
37197                
37198                 cell.hide();
37199                 
37200                 (function(){ // complex but required for focus issues in safari, ie and opera
37201                     ed.row = row;
37202                     ed.col = col;
37203                     ed.record = r;
37204                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37205                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37206                     this.activeEditor = ed;
37207                     var v = r.data[field];
37208                     ed.startEdit(this.view.getCell(row, col), v);
37209                     // combo's with 'displayField and name set
37210                     if (ed.field.displayField && ed.field.name) {
37211                         ed.field.el.dom.value = r.data[ed.field.name];
37212                     }
37213                     
37214                     
37215                 }).defer(50, this);
37216             }
37217         }
37218     },
37219         
37220     /**
37221      * Stops any active editing
37222      */
37223     stopEditing : function(){
37224         if(this.activeEditor){
37225             this.activeEditor.completeEdit();
37226         }
37227         this.activeEditor = null;
37228     }
37229 });/*
37230  * Based on:
37231  * Ext JS Library 1.1.1
37232  * Copyright(c) 2006-2007, Ext JS, LLC.
37233  *
37234  * Originally Released Under LGPL - original licence link has changed is not relivant.
37235  *
37236  * Fork - LGPL
37237  * <script type="text/javascript">
37238  */
37239
37240 // private - not really -- you end up using it !
37241 // This is a support class used internally by the Grid components
37242
37243 /**
37244  * @class Roo.grid.GridEditor
37245  * @extends Roo.Editor
37246  * Class for creating and editable grid elements.
37247  * @param {Object} config any settings (must include field)
37248  */
37249 Roo.grid.GridEditor = function(field, config){
37250     if (!config && field.field) {
37251         config = field;
37252         field = Roo.factory(config.field, Roo.form);
37253     }
37254     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37255     field.monitorTab = false;
37256 };
37257
37258 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37259     
37260     /**
37261      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37262      */
37263     
37264     alignment: "tl-tl",
37265     autoSize: "width",
37266     hideEl : false,
37267     cls: "x-small-editor x-grid-editor",
37268     shim:false,
37269     shadow:"frame"
37270 });/*
37271  * Based on:
37272  * Ext JS Library 1.1.1
37273  * Copyright(c) 2006-2007, Ext JS, LLC.
37274  *
37275  * Originally Released Under LGPL - original licence link has changed is not relivant.
37276  *
37277  * Fork - LGPL
37278  * <script type="text/javascript">
37279  */
37280   
37281
37282   
37283 Roo.grid.PropertyRecord = Roo.data.Record.create([
37284     {name:'name',type:'string'},  'value'
37285 ]);
37286
37287
37288 Roo.grid.PropertyStore = function(grid, source){
37289     this.grid = grid;
37290     this.store = new Roo.data.Store({
37291         recordType : Roo.grid.PropertyRecord
37292     });
37293     this.store.on('update', this.onUpdate,  this);
37294     if(source){
37295         this.setSource(source);
37296     }
37297     Roo.grid.PropertyStore.superclass.constructor.call(this);
37298 };
37299
37300
37301
37302 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37303     setSource : function(o){
37304         this.source = o;
37305         this.store.removeAll();
37306         var data = [];
37307         for(var k in o){
37308             if(this.isEditableValue(o[k])){
37309                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37310             }
37311         }
37312         this.store.loadRecords({records: data}, {}, true);
37313     },
37314
37315     onUpdate : function(ds, record, type){
37316         if(type == Roo.data.Record.EDIT){
37317             var v = record.data['value'];
37318             var oldValue = record.modified['value'];
37319             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37320                 this.source[record.id] = v;
37321                 record.commit();
37322                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37323             }else{
37324                 record.reject();
37325             }
37326         }
37327     },
37328
37329     getProperty : function(row){
37330        return this.store.getAt(row);
37331     },
37332
37333     isEditableValue: function(val){
37334         if(val && val instanceof Date){
37335             return true;
37336         }else if(typeof val == 'object' || typeof val == 'function'){
37337             return false;
37338         }
37339         return true;
37340     },
37341
37342     setValue : function(prop, value){
37343         this.source[prop] = value;
37344         this.store.getById(prop).set('value', value);
37345     },
37346
37347     getSource : function(){
37348         return this.source;
37349     }
37350 });
37351
37352 Roo.grid.PropertyColumnModel = function(grid, store){
37353     this.grid = grid;
37354     var g = Roo.grid;
37355     g.PropertyColumnModel.superclass.constructor.call(this, [
37356         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37357         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37358     ]);
37359     this.store = store;
37360     this.bselect = Roo.DomHelper.append(document.body, {
37361         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37362             {tag: 'option', value: 'true', html: 'true'},
37363             {tag: 'option', value: 'false', html: 'false'}
37364         ]
37365     });
37366     Roo.id(this.bselect);
37367     var f = Roo.form;
37368     this.editors = {
37369         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37370         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37371         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37372         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37373         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37374     };
37375     this.renderCellDelegate = this.renderCell.createDelegate(this);
37376     this.renderPropDelegate = this.renderProp.createDelegate(this);
37377 };
37378
37379 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37380     
37381     
37382     nameText : 'Name',
37383     valueText : 'Value',
37384     
37385     dateFormat : 'm/j/Y',
37386     
37387     
37388     renderDate : function(dateVal){
37389         return dateVal.dateFormat(this.dateFormat);
37390     },
37391
37392     renderBool : function(bVal){
37393         return bVal ? 'true' : 'false';
37394     },
37395
37396     isCellEditable : function(colIndex, rowIndex){
37397         return colIndex == 1;
37398     },
37399
37400     getRenderer : function(col){
37401         return col == 1 ?
37402             this.renderCellDelegate : this.renderPropDelegate;
37403     },
37404
37405     renderProp : function(v){
37406         return this.getPropertyName(v);
37407     },
37408
37409     renderCell : function(val){
37410         var rv = val;
37411         if(val instanceof Date){
37412             rv = this.renderDate(val);
37413         }else if(typeof val == 'boolean'){
37414             rv = this.renderBool(val);
37415         }
37416         return Roo.util.Format.htmlEncode(rv);
37417     },
37418
37419     getPropertyName : function(name){
37420         var pn = this.grid.propertyNames;
37421         return pn && pn[name] ? pn[name] : name;
37422     },
37423
37424     getCellEditor : function(colIndex, rowIndex){
37425         var p = this.store.getProperty(rowIndex);
37426         var n = p.data['name'], val = p.data['value'];
37427         
37428         if(typeof(this.grid.customEditors[n]) == 'string'){
37429             return this.editors[this.grid.customEditors[n]];
37430         }
37431         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37432             return this.grid.customEditors[n];
37433         }
37434         if(val instanceof Date){
37435             return this.editors['date'];
37436         }else if(typeof val == 'number'){
37437             return this.editors['number'];
37438         }else if(typeof val == 'boolean'){
37439             return this.editors['boolean'];
37440         }else{
37441             return this.editors['string'];
37442         }
37443     }
37444 });
37445
37446 /**
37447  * @class Roo.grid.PropertyGrid
37448  * @extends Roo.grid.EditorGrid
37449  * This class represents the  interface of a component based property grid control.
37450  * <br><br>Usage:<pre><code>
37451  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37452       
37453  });
37454  // set any options
37455  grid.render();
37456  * </code></pre>
37457   
37458  * @constructor
37459  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37460  * The container MUST have some type of size defined for the grid to fill. The container will be
37461  * automatically set to position relative if it isn't already.
37462  * @param {Object} config A config object that sets properties on this grid.
37463  */
37464 Roo.grid.PropertyGrid = function(container, config){
37465     config = config || {};
37466     var store = new Roo.grid.PropertyStore(this);
37467     this.store = store;
37468     var cm = new Roo.grid.PropertyColumnModel(this, store);
37469     store.store.sort('name', 'ASC');
37470     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37471         ds: store.store,
37472         cm: cm,
37473         enableColLock:false,
37474         enableColumnMove:false,
37475         stripeRows:false,
37476         trackMouseOver: false,
37477         clicksToEdit:1
37478     }, config));
37479     this.getGridEl().addClass('x-props-grid');
37480     this.lastEditRow = null;
37481     this.on('columnresize', this.onColumnResize, this);
37482     this.addEvents({
37483          /**
37484              * @event beforepropertychange
37485              * Fires before a property changes (return false to stop?)
37486              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37487              * @param {String} id Record Id
37488              * @param {String} newval New Value
37489          * @param {String} oldval Old Value
37490              */
37491         "beforepropertychange": true,
37492         /**
37493              * @event propertychange
37494              * Fires after a property changes
37495              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37496              * @param {String} id Record Id
37497              * @param {String} newval New Value
37498          * @param {String} oldval Old Value
37499              */
37500         "propertychange": true
37501     });
37502     this.customEditors = this.customEditors || {};
37503 };
37504 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37505     
37506      /**
37507      * @cfg {Object} customEditors map of colnames=> custom editors.
37508      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37509      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37510      * false disables editing of the field.
37511          */
37512     
37513       /**
37514      * @cfg {Object} propertyNames map of property Names to their displayed value
37515          */
37516     
37517     render : function(){
37518         Roo.grid.PropertyGrid.superclass.render.call(this);
37519         this.autoSize.defer(100, this);
37520     },
37521
37522     autoSize : function(){
37523         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37524         if(this.view){
37525             this.view.fitColumns();
37526         }
37527     },
37528
37529     onColumnResize : function(){
37530         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37531         this.autoSize();
37532     },
37533     /**
37534      * Sets the data for the Grid
37535      * accepts a Key => Value object of all the elements avaiable.
37536      * @param {Object} data  to appear in grid.
37537      */
37538     setSource : function(source){
37539         this.store.setSource(source);
37540         //this.autoSize();
37541     },
37542     /**
37543      * Gets all the data from the grid.
37544      * @return {Object} data  data stored in grid
37545      */
37546     getSource : function(){
37547         return this.store.getSource();
37548     }
37549 });/*
37550  * Based on:
37551  * Ext JS Library 1.1.1
37552  * Copyright(c) 2006-2007, Ext JS, LLC.
37553  *
37554  * Originally Released Under LGPL - original licence link has changed is not relivant.
37555  *
37556  * Fork - LGPL
37557  * <script type="text/javascript">
37558  */
37559  
37560 /**
37561  * @class Roo.LoadMask
37562  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37563  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37564  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37565  * element's UpdateManager load indicator and will be destroyed after the initial load.
37566  * @constructor
37567  * Create a new LoadMask
37568  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37569  * @param {Object} config The config object
37570  */
37571 Roo.LoadMask = function(el, config){
37572     this.el = Roo.get(el);
37573     Roo.apply(this, config);
37574     if(this.store){
37575         this.store.on('beforeload', this.onBeforeLoad, this);
37576         this.store.on('load', this.onLoad, this);
37577         this.store.on('loadexception', this.onLoadException, this);
37578         this.removeMask = false;
37579     }else{
37580         var um = this.el.getUpdateManager();
37581         um.showLoadIndicator = false; // disable the default indicator
37582         um.on('beforeupdate', this.onBeforeLoad, this);
37583         um.on('update', this.onLoad, this);
37584         um.on('failure', this.onLoad, this);
37585         this.removeMask = true;
37586     }
37587 };
37588
37589 Roo.LoadMask.prototype = {
37590     /**
37591      * @cfg {Boolean} removeMask
37592      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37593      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37594      */
37595     /**
37596      * @cfg {String} msg
37597      * The text to display in a centered loading message box (defaults to 'Loading...')
37598      */
37599     msg : 'Loading...',
37600     /**
37601      * @cfg {String} msgCls
37602      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37603      */
37604     msgCls : 'x-mask-loading',
37605
37606     /**
37607      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37608      * @type Boolean
37609      */
37610     disabled: false,
37611
37612     /**
37613      * Disables the mask to prevent it from being displayed
37614      */
37615     disable : function(){
37616        this.disabled = true;
37617     },
37618
37619     /**
37620      * Enables the mask so that it can be displayed
37621      */
37622     enable : function(){
37623         this.disabled = false;
37624     },
37625     
37626     onLoadException : function()
37627     {
37628         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37629             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37630         }
37631         this.el.unmask(this.removeMask);
37632     },
37633     // private
37634     onLoad : function()
37635     {
37636         this.el.unmask(this.removeMask);
37637     },
37638
37639     // private
37640     onBeforeLoad : function(){
37641         if(!this.disabled){
37642             this.el.mask(this.msg, this.msgCls);
37643         }
37644     },
37645
37646     // private
37647     destroy : function(){
37648         if(this.store){
37649             this.store.un('beforeload', this.onBeforeLoad, this);
37650             this.store.un('load', this.onLoad, this);
37651             this.store.un('loadexception', this.onLoadException, this);
37652         }else{
37653             var um = this.el.getUpdateManager();
37654             um.un('beforeupdate', this.onBeforeLoad, this);
37655             um.un('update', this.onLoad, this);
37656             um.un('failure', this.onLoad, this);
37657         }
37658     }
37659 };/*
37660  * Based on:
37661  * Ext JS Library 1.1.1
37662  * Copyright(c) 2006-2007, Ext JS, LLC.
37663  *
37664  * Originally Released Under LGPL - original licence link has changed is not relivant.
37665  *
37666  * Fork - LGPL
37667  * <script type="text/javascript">
37668  */
37669 Roo.XTemplate = function(){
37670     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37671     var s = this.html;
37672
37673     s = ['<tpl>', s, '</tpl>'].join('');
37674
37675     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
37676
37677     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
37678     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
37679     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
37680     var m, id = 0;
37681     var tpls = [];
37682
37683     while(m = s.match(re)){
37684        var m2 = m[0].match(nameRe);
37685        var m3 = m[0].match(ifRe);
37686        var m4 = m[0].match(execRe);
37687        var exp = null, fn = null, exec = null;
37688        var name = m2 && m2[1] ? m2[1] : '';
37689        if(m3){
37690            exp = m3 && m3[1] ? m3[1] : null;
37691            if(exp){
37692                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
37693            }
37694        }
37695        if(m4){
37696            exp = m4 && m4[1] ? m4[1] : null;
37697            if(exp){
37698                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
37699            }
37700        }
37701        if(name){
37702            switch(name){
37703                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
37704                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
37705                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
37706            }
37707        }
37708        tpls.push({
37709             id: id,
37710             target: name,
37711             exec: exec,
37712             test: fn,
37713             body: m[1]||''
37714         });
37715        s = s.replace(m[0], '{xtpl'+ id + '}');
37716        ++id;
37717     }
37718     for(var i = tpls.length-1; i >= 0; --i){
37719         this.compileTpl(tpls[i]);
37720     }
37721     this.master = tpls[tpls.length-1];
37722     this.tpls = tpls;
37723 };
37724 Roo.extend(Roo.XTemplate, Roo.Template, {
37725
37726     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37727
37728     applySubTemplate : function(id, values, parent){
37729         var t = this.tpls[id];
37730         if(t.test && !t.test.call(this, values, parent)){
37731             return '';
37732         }
37733         if(t.exec && t.exec.call(this, values, parent)){
37734             return '';
37735         }
37736         var vs = t.target ? t.target.call(this, values, parent) : values;
37737         parent = t.target ? values : parent;
37738         if(t.target && vs instanceof Array){
37739             var buf = [];
37740             for(var i = 0, len = vs.length; i < len; i++){
37741                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
37742             }
37743             return buf.join('');
37744         }
37745         return t.compiled.call(this, vs, parent);
37746     },
37747
37748     compileTpl : function(tpl){
37749         var fm = Roo.util.Format;
37750         var useF = this.disableFormats !== true;
37751         var sep = Roo.isGecko ? "+" : ",";
37752         var fn = function(m, name, format, args){
37753             if(name.substr(0, 4) == 'xtpl'){
37754                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
37755             }
37756             var v;
37757             if(name.indexOf('.') != -1){
37758                 v = name;
37759             }else{
37760                 v = "values['" + name + "']";
37761             }
37762             if(format && useF){
37763                 args = args ? ',' + args : "";
37764                 if(format.substr(0, 5) != "this."){
37765                     format = "fm." + format + '(';
37766                 }else{
37767                     format = 'this.call("'+ format.substr(5) + '", ';
37768                     args = ", values";
37769                 }
37770             }else{
37771                 args= ''; format = "("+v+" === undefined ? '' : ";
37772             }
37773             return "'"+ sep + format + v + args + ")"+sep+"'";
37774         };
37775         var body;
37776         // branched to use + in gecko and [].join() in others
37777         if(Roo.isGecko){
37778             body = "tpl.compiled = function(values, parent){ return '" +
37779                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
37780                     "';};";
37781         }else{
37782             body = ["tpl.compiled = function(values, parent){ return ['"];
37783             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
37784             body.push("'].join('');};");
37785             body = body.join('');
37786         }
37787         /** eval:var:zzzzzzz */
37788         eval(body);
37789         return this;
37790     },
37791
37792     applyTemplate : function(values){
37793         return this.master.compiled.call(this, values, {});
37794         var s = this.subs;
37795     },
37796
37797     apply : function(){
37798         return this.applyTemplate.apply(this, arguments);
37799     },
37800
37801     compile : function(){return this;}
37802 });
37803
37804 Roo.XTemplate.from = function(el){
37805     el = Roo.getDom(el);
37806     return new Roo.XTemplate(el.value || el.innerHTML);
37807 };/*
37808  * Original code for Roojs - LGPL
37809  * <script type="text/javascript">
37810  */
37811  
37812 /**
37813  * @class Roo.XComponent
37814  * A delayed Element creator...
37815  * Or a way to group chunks of interface together.
37816  * 
37817  * Mypart.xyx = new Roo.XComponent({
37818
37819     parent : 'Mypart.xyz', // empty == document.element.!!
37820     order : '001',
37821     name : 'xxxx'
37822     region : 'xxxx'
37823     disabled : function() {} 
37824      
37825     tree : function() { // return an tree of xtype declared components
37826         var MODULE = this;
37827         return 
37828         {
37829             xtype : 'NestedLayoutPanel',
37830             // technicall
37831         }
37832      ]
37833  *})
37834  *
37835  *
37836  * It can be used to build a big heiracy, with parent etc.
37837  * or you can just use this to render a single compoent to a dom element
37838  * MYPART.render(Roo.Element | String(id) | dom_element )
37839  * 
37840  * @extends Roo.util.Observable
37841  * @constructor
37842  * @param cfg {Object} configuration of component
37843  * 
37844  */
37845 Roo.XComponent = function(cfg) {
37846     Roo.apply(this, cfg);
37847     this.addEvents({ 
37848         /**
37849              * @event built
37850              * Fires when this the componnt is built
37851              * @param {Roo.XComponent} c the component
37852              */
37853         'built' : true,
37854         /**
37855              * @event buildcomplete
37856              * Fires on the top level element when all elements have been built
37857              * @param {Roo.XComponent} c the top level component.
37858          */
37859         'buildcomplete' : true
37860         
37861     });
37862     this.region = this.region || 'center'; // default..
37863     Roo.XComponent.register(this);
37864     this.modules = false;
37865     this.el = false; // where the layout goes..
37866     
37867     
37868 }
37869 Roo.extend(Roo.XComponent, Roo.util.Observable, {
37870     /**
37871      * @property el
37872      * The created element (with Roo.factory())
37873      * @type {Roo.Layout}
37874      */
37875     el  : false,
37876     
37877     /**
37878      * @property el
37879      * for BC  - use el in new code
37880      * @type {Roo.Layout}
37881      */
37882     panel : false,
37883     
37884     /**
37885      * @property layout
37886      * for BC  - use el in new code
37887      * @type {Roo.Layout}
37888      */
37889     layout : false,
37890     
37891      /**
37892      * @cfg {Function|boolean} disabled
37893      * If this module is disabled by some rule, return true from the funtion
37894      */
37895     disabled : false,
37896     
37897     /**
37898      * @cfg {String} parent 
37899      * Name of parent element which it get xtype added to..
37900      */
37901     parent: false,
37902     
37903     /**
37904      * @cfg {String} order
37905      * Used to set the order in which elements are created (usefull for multiple tabs)
37906      */
37907     
37908     order : false,
37909     /**
37910      * @cfg {String} name
37911      * String to display while loading.
37912      */
37913     name : false,
37914     /**
37915      * @cfg {String} region
37916      * Region to render component to (defaults to center)
37917      */
37918     region : 'center',
37919     
37920     /**
37921      * @cfg {Array} items
37922      * A single item array - the first element is the root of the tree..
37923      * It's done this way to stay compatible with the Xtype system...
37924      */
37925     items : false,
37926     
37927     
37928      /**
37929      * render
37930      * render element to dom or tree
37931      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
37932      */
37933     
37934     render : function(el)
37935     {
37936         
37937         el = el || false;
37938         var hp = this.parent ? 1 : 0;
37939         
37940         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
37941             // if parent is a '#.....' string, then let's use that..
37942             var ename = this.parent.substr(1)
37943             this.parent = false;
37944             el = Roo.get(ename);
37945             if (!el) {
37946                 Roo.log("Warning - element can not be found :#" + ename );
37947                 return;
37948             }
37949         }
37950         
37951         
37952         if (!this.parent) {
37953             
37954             el = el ? Roo.get(el) : false;
37955             
37956             // it's a top level one..
37957             this.parent =  {
37958                 el : new Roo.BorderLayout(el || document.body, {
37959                 
37960                      center: {
37961                          titlebar: false,
37962                          autoScroll:false,
37963                          closeOnTab: true,
37964                          tabPosition: 'top',
37965                           //resizeTabs: true,
37966                          alwaysShowTabs: el && hp? false :  true,
37967                          hideTabs: el || !hp ? true :  false,
37968                          minTabWidth: 140
37969                      }
37970                  })
37971             }
37972         }
37973         
37974         
37975             
37976         var tree = this.tree();
37977         tree.region = tree.region || this.region;
37978         this.el = this.parent.el.addxtype(tree);
37979         this.fireEvent('built', this);
37980         
37981         this.panel = this.el;
37982         this.layout = this.panel.layout;    
37983          
37984     }
37985     
37986 });
37987
37988 Roo.apply(Roo.XComponent, {
37989     
37990     /**
37991      * @property  buildCompleted
37992      * True when the builder has completed building the interface.
37993      * @type Boolean
37994      */
37995     buildCompleted : false,
37996      
37997     /**
37998      * @property  topModule
37999      * the upper most module - uses document.element as it's constructor.
38000      * @type Object
38001      */
38002      
38003     topModule  : false,
38004       
38005     /**
38006      * @property  modules
38007      * array of modules to be created by registration system.
38008      * @type {Array} of Roo.XComponent
38009      */
38010     
38011     modules : [],
38012     /**
38013      * @property  elmodules
38014      * array of modules to be created by which use #ID 
38015      * @type {Array} of Roo.XComponent
38016      */
38017      
38018     elmodules : [],
38019
38020     
38021     /**
38022      * Register components to be built later.
38023      *
38024      * This solves the following issues
38025      * - Building is not done on page load, but after an authentication process has occured.
38026      * - Interface elements are registered on page load
38027      * - Parent Interface elements may not be loaded before child, so this handles that..
38028      * 
38029      *
38030      * example:
38031      * 
38032      * MyApp.register({
38033           order : '000001',
38034           module : 'Pman.Tab.projectMgr',
38035           region : 'center',
38036           parent : 'Pman.layout',
38037           disabled : false,  // or use a function..
38038         })
38039      
38040      * * @param {Object} details about module
38041      */
38042     register : function(obj) {
38043         this.modules.push(obj);
38044          
38045     },
38046     /**
38047      * convert a string to an object..
38048      * eg. 'AAA.BBB' -> finds AAA.BBB
38049
38050      */
38051     
38052     toObject : function(str)
38053     {
38054         if (!str || typeof(str) == 'object') {
38055             return str;
38056         }
38057         if (str.substring(0,1) == '#') {
38058             return str;
38059         }
38060
38061         var ar = str.split('.');
38062         var rt, o;
38063         rt = ar.shift();
38064             /** eval:var:o */
38065         try {
38066             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38067         } catch (e) {
38068             throw "Module not found : " + str;
38069         }
38070         
38071         if (o === false) {
38072             throw "Module not found : " + str;
38073         }
38074         Roo.each(ar, function(e) {
38075             if (typeof(o[e]) == 'undefined') {
38076                 throw "Module not found : " + str;
38077             }
38078             o = o[e];
38079         });
38080         
38081         return o;
38082         
38083     },
38084     
38085     
38086     /**
38087      * move modules into their correct place in the tree..
38088      * 
38089      */
38090     preBuild : function ()
38091     {
38092         var _t = this;
38093         Roo.each(this.modules , function (obj)
38094         {
38095             var opar = obj.parent;
38096             try { 
38097                 obj.parent = this.toObject(opar);
38098             } catch(e) {
38099                 Roo.log(e.toString());
38100                 return;
38101             }
38102             
38103             if (!obj.parent) {
38104                 this.topModule = obj;
38105                 return;
38106             }
38107             if (typeof(obj.parent) == 'string') {
38108                 this.elmodules.push(obj);
38109                 return;
38110             }
38111             if (obj.parent.constructor != Roo.XComponent) {
38112                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
38113             }
38114             if (!obj.parent.modules) {
38115                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38116                     function(o) { return o.order + '' }
38117                 );
38118             }
38119             
38120             obj.parent.modules.add(obj);
38121         }, this);
38122     },
38123     
38124      /**
38125      * make a list of modules to build.
38126      * @return {Array} list of modules. 
38127      */ 
38128     
38129     buildOrder : function()
38130     {
38131         var _this = this;
38132         var cmp = function(a,b) {   
38133             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38134         };
38135         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38136             throw "No top level modules to build";
38137         }
38138         
38139         // make a flat list in order of modules to build.
38140         var mods = this.topModule ? [ this.topModule ] : [];
38141         Roo.each(this.elmodules,function(e) { mods.push(e) });
38142
38143         
38144         // add modules to their parents..
38145         var addMod = function(m) {
38146            // Roo.debug && Roo.log(m.modKey);
38147             
38148             mods.push(m);
38149             if (m.modules) {
38150                 m.modules.keySort('ASC',  cmp );
38151                 m.modules.each(addMod);
38152             }
38153             // not sure if this is used any more..
38154             if (m.finalize) {
38155                 m.finalize.name = m.name + " (clean up) ";
38156                 mods.push(m.finalize);
38157             }
38158             
38159         }
38160         if (this.topModule) { 
38161             this.topModule.modules.keySort('ASC',  cmp );
38162             this.topModule.modules.each(addMod);
38163         }
38164         return mods;
38165     },
38166     
38167      /**
38168      * Build the registered modules.
38169      * @param {Object} parent element.
38170      * @param {Function} optional method to call after module has been added.
38171      * 
38172      */ 
38173    
38174     build : function() 
38175     {
38176         
38177         this.preBuild();
38178         var mods = this.buildOrder();
38179       
38180         //this.allmods = mods;
38181         //Roo.debug && Roo.log(mods);
38182         //return;
38183         if (!mods.length) { // should not happen
38184             throw "NO modules!!!";
38185         }
38186         
38187         
38188         
38189         // flash it up as modal - so we store the mask!?
38190         Roo.MessageBox.show({ title: 'loading' });
38191         Roo.MessageBox.show({
38192            title: "Please wait...",
38193            msg: "Building Interface...",
38194            width:450,
38195            progress:true,
38196            closable:false,
38197            modal: false
38198           
38199         });
38200         var total = mods.length;
38201         
38202         var _this = this;
38203         var progressRun = function() {
38204             if (!mods.length) {
38205                 Roo.debug && Roo.log('hide?');
38206                 Roo.MessageBox.hide();
38207                 if (_this.topModule) { 
38208                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
38209                 }
38210                 // THE END...
38211                 return false;   
38212             }
38213             
38214             var m = mods.shift();
38215             
38216             
38217             Roo.debug && Roo.log(m);
38218             // not sure if this is supported any more.. - modules that are are just function
38219             if (typeof(m) == 'function') { 
38220                 m.call(this);
38221                 return progressRun.defer(10, _this);
38222             } 
38223             
38224             
38225             
38226             Roo.MessageBox.updateProgress(
38227                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
38228                     " of " + total + 
38229                     (m.name ? (' - ' + m.name) : '')
38230                     );
38231             
38232          
38233             // is the module disabled?
38234             var disabled = (typeof(m.disabled) == 'function') ?
38235                 m.disabled.call(m.module.disabled) : m.disabled;    
38236             
38237             
38238             if (disabled) {
38239                 return progressRun(); // we do not update the display!
38240             }
38241             
38242             // now build 
38243             
38244             m.render();
38245             // it's 10 on top level, and 1 on others??? why...
38246             return progressRun.defer(10, _this);
38247              
38248         }
38249         progressRun.defer(1, _this);
38250      
38251         
38252         
38253     }
38254     
38255      
38256    
38257     
38258     
38259 });
38260  //<script type="text/javascript">
38261
38262
38263 /**
38264  * @class Roo.Login
38265  * @extends Roo.LayoutDialog
38266  * A generic Login Dialog..... - only one needed in theory!?!?
38267  *
38268  * Fires XComponent builder on success...
38269  * 
38270  * Sends 
38271  *    username,password, lang = for login actions.
38272  *    check = 1 for periodic checking that sesion is valid.
38273  *    passwordRequest = email request password
38274  *    logout = 1 = to logout
38275  * 
38276  * Affects: (this id="????" elements)
38277  *   loading  (removed) (used to indicate application is loading)
38278  *   loading-mask (hides) (used to hide application when it's building loading)
38279  *   
38280  * 
38281  * Usage: 
38282  *    
38283  * 
38284  * Myapp.login = Roo.Login({
38285      url: xxxx,
38286    
38287      realm : 'Myapp', 
38288      
38289      
38290      method : 'POST',
38291      
38292      
38293      * 
38294  })
38295  * 
38296  * 
38297  * 
38298  **/
38299  
38300 Roo.Login = function(cfg)
38301 {
38302     this.addEvents({
38303         'refreshed' : true
38304     });
38305     
38306     Roo.apply(this,cfg);
38307     
38308     Roo.onReady(function() {
38309         this.onLoad();
38310     }, this);
38311     // call parent..
38312     
38313    
38314     Roo.Login.superclass.constructor.call(this, this);
38315     //this.addxtype(this.items[0]);
38316     
38317     
38318 }
38319
38320
38321 Roo.extend(Roo.Login, Roo.LayoutDialog, {
38322     
38323     /**
38324      * @cfg {String} method
38325      * Method used to query for login details.
38326      */
38327     
38328     method : 'POST',
38329     /**
38330      * @cfg {String} url
38331      * URL to query login data. - eg. baseURL + '/Login.php'
38332      */
38333     url : '',
38334     
38335     /**
38336      * @property user
38337      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
38338      * @type {Object} 
38339      */
38340     user : false,
38341     /**
38342      * @property checkFails
38343      * Number of times we have attempted to get authentication check, and failed.
38344      * @type {Number} 
38345      */
38346     checkFails : 0,
38347       /**
38348      * @property intervalID
38349      * The window interval that does the constant login checking.
38350      * @type {Number} 
38351      */
38352     intervalID : 0,
38353     
38354     
38355     onLoad : function() // called on page load...
38356     {
38357         // load 
38358          
38359         if (Roo.get('loading')) { // clear any loading indicator..
38360             Roo.get('loading').remove();
38361         }
38362         
38363         //this.switchLang('en'); // set the language to english..
38364        
38365         this.check({
38366             success:  function(response, opts)  {  // check successfull...
38367             
38368                 var res = this.processResponse(response);
38369                 this.checkFails =0;
38370                 if (!res.success) { // error!
38371                     this.checkFails = 5;
38372                     //console.log('call failure');
38373                     return this.failure(response,opts);
38374                 }
38375                 
38376                 if (!res.data.id) { // id=0 == login failure.
38377                     return this.show();
38378                 }
38379                 
38380                               
38381                         //console.log(success);
38382                 this.fillAuth(res.data);   
38383                 this.checkFails =0;
38384                 Roo.XComponent.build();
38385             },
38386             failure : this.show
38387         });
38388         
38389     }, 
38390     
38391     
38392     check: function(cfg) // called every so often to refresh cookie etc..
38393     {
38394         if (cfg.again) { // could be undefined..
38395             this.checkFails++;
38396         } else {
38397             this.checkFails = 0;
38398         }
38399         var _this = this;
38400         if (this.sending) {
38401             if ( this.checkFails > 4) {
38402                 Roo.MessageBox.alert("Error",  
38403                     "Error getting authentication status. - try reloading, or wait a while", function() {
38404                         _this.sending = false;
38405                     }); 
38406                 return;
38407             }
38408             cfg.again = true;
38409             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
38410             return;
38411         }
38412         this.sending = true;
38413         
38414         Roo.Ajax.request({  
38415             url: this.url,
38416             params: {
38417                 getAuthUser: true
38418             },  
38419             method: this.method,
38420             success:  cfg.success || this.success,
38421             failure : cfg.failure || this.failure,
38422             scope : this,
38423             callCfg : cfg
38424               
38425         });  
38426     }, 
38427     
38428     
38429     logout: function()
38430     {
38431         window.onbeforeunload = function() { }; // false does not work for IE..
38432         this.user = false;
38433         var _this = this;
38434         
38435         Roo.Ajax.request({  
38436             url: this.url,
38437             params: {
38438                 logout: 1
38439             },  
38440             method: 'GET',
38441             failure : function() {
38442                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
38443                     document.location = document.location.toString() + '?ts=' + Math.random();
38444                 });
38445                 
38446             },
38447             success : function() {
38448                 _this.user = false;
38449                 this.checkFails =0;
38450                 // fixme..
38451                 document.location = document.location.toString() + '?ts=' + Math.random();
38452             }
38453               
38454               
38455         }); 
38456     },
38457     
38458     processResponse : function (response)
38459     {
38460         var res = '';
38461         try {
38462             res = Roo.decode(response.responseText);
38463             // oops...
38464             if (typeof(res) != 'object') {
38465                 res = { success : false, errorMsg : res, errors : true };
38466             }
38467             if (typeof(res.success) == 'undefined') {
38468                 res.success = false;
38469             }
38470             
38471         } catch(e) {
38472             res = { success : false,  errorMsg : response.responseText, errors : true };
38473         }
38474         return res;
38475     },
38476     
38477     success : function(response, opts)  // check successfull...
38478     {  
38479         this.sending = false;
38480         var res = this.processResponse(response);
38481         if (!res.success) {
38482             return this.failure(response, opts);
38483         }
38484         if (!res.data || !res.data.id) {
38485             return this.failure(response,opts);
38486         }
38487         //console.log(res);
38488         this.fillAuth(res.data);
38489         
38490         this.checkFails =0;
38491         
38492     },
38493     
38494     
38495     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
38496     {
38497         this.authUser = -1;
38498         this.sending = false;
38499         var res = this.processResponse(response);
38500         //console.log(res);
38501         if ( this.checkFails > 2) {
38502         
38503             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
38504                 "Error getting authentication status. - try reloading"); 
38505             return;
38506         }
38507         opts.callCfg.again = true;
38508         this.check.defer(1000, this, [ opts.callCfg ]);
38509         return;  
38510     },
38511     
38512     
38513     
38514     fillAuth: function(au) {
38515         this.startAuthCheck();
38516         this.authUserId = au.id;
38517         this.authUser = au;
38518         this.lastChecked = new Date();
38519         this.fireEvent('refreshed', au);
38520         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
38521         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
38522         au.lang = au.lang || 'en';
38523         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
38524         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
38525         this.switchLang(au.lang );
38526         
38527      
38528         // open system... - -on setyp..
38529         if (this.authUserId  < 0) {
38530             Roo.MessageBox.alert("Warning", 
38531                 "This is an open system - please set up a admin user with a password.");  
38532         }
38533          
38534         //Pman.onload(); // which should do nothing if it's a re-auth result...
38535         
38536              
38537     },
38538     
38539     startAuthCheck : function() // starter for timeout checking..
38540     {
38541         if (this.intervalID) { // timer already in place...
38542             return false;
38543         }
38544         var _this = this;
38545         this.intervalID =  window.setInterval(function() {
38546               _this.check(false);
38547             }, 120000); // every 120 secs = 2mins..
38548         
38549         
38550     },
38551          
38552     
38553     switchLang : function (lang) 
38554     {
38555         _T = typeof(_T) == 'undefined' ? false : _T;
38556           if (!_T || !lang.length) {
38557             return;
38558         }
38559         
38560         if (!_T && lang != 'en') {
38561             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38562             return;
38563         }
38564         
38565         if (typeof(_T.en) == 'undefined') {
38566             _T.en = {};
38567             Roo.apply(_T.en, _T);
38568         }
38569         
38570         if (typeof(_T[lang]) == 'undefined') {
38571             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38572             return;
38573         }
38574         
38575         
38576         Roo.apply(_T, _T[lang]);
38577         // just need to set the text values for everything...
38578         var _this = this;
38579         /* this will not work ...
38580         if (this.form) { 
38581             
38582                
38583             function formLabel(name, val) {
38584                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
38585             }
38586             
38587             formLabel('password', "Password"+':');
38588             formLabel('username', "Email Address"+':');
38589             formLabel('lang', "Language"+':');
38590             this.dialog.setTitle("Login");
38591             this.dialog.buttons[0].setText("Forgot Password");
38592             this.dialog.buttons[1].setText("Login");
38593         }
38594         */
38595         
38596         
38597     },
38598     
38599     
38600     title: "Login",
38601     modal: true,
38602     width:  350,
38603     //height: 230,
38604     height: 180,
38605     shadow: true,
38606     minWidth:200,
38607     minHeight:180,
38608     //proxyDrag: true,
38609     closable: false,
38610     draggable: false,
38611     collapsible: false,
38612     resizable: false,
38613     center: {  // needed??
38614         autoScroll:false,
38615         titlebar: false,
38616        // tabPosition: 'top',
38617         hideTabs: true,
38618         closeOnTab: true,
38619         alwaysShowTabs: false
38620     } ,
38621     listeners : {
38622         
38623         show  : function(dlg)
38624         {
38625             //console.log(this);
38626             this.form = this.layout.getRegion('center').activePanel.form;
38627             this.form.dialog = dlg;
38628             this.buttons[0].form = this.form;
38629             this.buttons[0].dialog = dlg;
38630             this.buttons[1].form = this.form;
38631             this.buttons[1].dialog = dlg;
38632            
38633            //this.resizeToLogo.defer(1000,this);
38634             // this is all related to resizing for logos..
38635             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
38636            //// if (!sz) {
38637              //   this.resizeToLogo.defer(1000,this);
38638              //   return;
38639            // }
38640             //var w = Ext.lib.Dom.getViewWidth() - 100;
38641             //var h = Ext.lib.Dom.getViewHeight() - 100;
38642             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
38643             //this.center();
38644             if (this.disabled) {
38645                 this.hide();
38646                 return;
38647             }
38648             
38649             if (this.user.id < 0) { // used for inital setup situations.
38650                 return;
38651             }
38652             
38653             if (this.intervalID) {
38654                 // remove the timer
38655                 window.clearInterval(this.intervalID);
38656                 this.intervalID = false;
38657             }
38658             
38659             
38660             if (Roo.get('loading')) {
38661                 Roo.get('loading').remove();
38662             }
38663             if (Roo.get('loading-mask')) {
38664                 Roo.get('loading-mask').hide();
38665             }
38666             
38667             //incomming._node = tnode;
38668             this.form.reset();
38669             //this.dialog.modal = !modal;
38670             //this.dialog.show();
38671             this.el.unmask(); 
38672             
38673             
38674             this.form.setValues({
38675                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
38676                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
38677             });
38678             
38679             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
38680             if (this.form.findField('username').getValue().length > 0 ){
38681                 this.form.findField('password').focus();
38682             } else {
38683                this.form.findField('username').focus();
38684             }
38685     
38686         }
38687     },
38688     items : [
38689          {
38690        
38691             xtype : 'ContentPanel',
38692             xns : Roo,
38693             region: 'center',
38694             fitToFrame : true,
38695             
38696             items : [
38697     
38698                 {
38699                
38700                     xtype : 'Form',
38701                     xns : Roo.form,
38702                     labelWidth: 100,
38703                     style : 'margin: 10px;',
38704                     
38705                     listeners : {
38706                         actionfailed : function(f, act) {
38707                             // form can return { errors: .... }
38708                                 
38709                             //act.result.errors // invalid form element list...
38710                             //act.result.errorMsg// invalid form element list...
38711                             
38712                             this.dialog.el.unmask();
38713                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
38714                                         "Login failed - communication error - try again.");
38715                                       
38716                         },
38717                         actioncomplete: function(re, act) {
38718                              
38719                             Roo.state.Manager.set(
38720                                 this.dialog.realm + '.username',  
38721                                     this.findField('username').getValue()
38722                             );
38723                             Roo.state.Manager.set(
38724                                 this.dialog.realm + '.lang',  
38725                                 this.findField('lang').getValue() 
38726                             );
38727                             
38728                             this.dialog.fillAuth(act.result.data);
38729                               
38730                             this.dialog.hide();
38731                             
38732                             if (Roo.get('loading-mask')) {
38733                                 Roo.get('loading-mask').show();
38734                             }
38735                             Roo.XComponent.build();
38736                             
38737                              
38738                             
38739                         }
38740                     },
38741                     items : [
38742                         {
38743                             xtype : 'TextField',
38744                             xns : Roo.form,
38745                             fieldLabel: "Email Address",
38746                             name: 'username',
38747                             width:200,
38748                             autoCreate : {tag: "input", type: "text", size: "20"}
38749                         },
38750                         {
38751                             xtype : 'TextField',
38752                             xns : Roo.form,
38753                             fieldLabel: "Password",
38754                             inputType: 'password',
38755                             name: 'password',
38756                             width:200,
38757                             autoCreate : {tag: "input", type: "text", size: "20"},
38758                             listeners : {
38759                                 specialkey : function(e,ev) {
38760                                     if (ev.keyCode == 13) {
38761                                         this.form.dialog.el.mask("Logging in");
38762                                         this.form.doAction('submit', {
38763                                             url: this.form.dialog.url,
38764                                             method: this.form.dialog.method
38765                                         });
38766                                     }
38767                                 }
38768                             }  
38769                         },
38770                         {
38771                             xtype : 'ComboBox',
38772                             xns : Roo.form,
38773                             fieldLabel: "Language",
38774                             name : 'langdisp',
38775                             store: {
38776                                 xtype : 'SimpleStore',
38777                                 fields: ['lang', 'ldisp'],
38778                                 data : [
38779                                     [ 'en', 'English' ],
38780                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
38781                                     [ 'zh_CN', '\u7C21\u4E2D' ]
38782                                 ]
38783                             },
38784                             
38785                             valueField : 'lang',
38786                             hiddenName:  'lang',
38787                             width: 200,
38788                             displayField:'ldisp',
38789                             typeAhead: false,
38790                             editable: false,
38791                             mode: 'local',
38792                             triggerAction: 'all',
38793                             emptyText:'Select a Language...',
38794                             selectOnFocus:true,
38795                             listeners : {
38796                                 select :  function(cb, rec, ix) {
38797                                     this.form.switchLang(rec.data.lang);
38798                                 }
38799                             }
38800                         
38801                         }
38802                     ]
38803                 }
38804                   
38805                 
38806             ]
38807         }
38808     ],
38809     buttons : [
38810         {
38811             xtype : 'Button',
38812             xns : 'Roo',
38813             text : "Forgot Password",
38814             listeners : {
38815                 click : function() {
38816                     //console.log(this);
38817                     var n = this.form.findField('username').getValue();
38818                     if (!n.length) {
38819                         Roo.MessageBox.alert("Error", "Fill in your email address");
38820                         return;
38821                     }
38822                     Roo.Ajax.request({
38823                         url: this.dialog.url,
38824                         params: {
38825                             passwordRequest: n
38826                         },
38827                         method: this.dialog.method,
38828                         success:  function(response, opts)  {  // check successfull...
38829                         
38830                             var res = this.dialog.processResponse(response);
38831                             if (!res.success) { // error!
38832                                Roo.MessageBox.alert("Error" ,
38833                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
38834                                return;
38835                             }
38836                             Roo.MessageBox.alert("Notice" ,
38837                                 "Please check you email for the Password Reset message");
38838                         },
38839                         failure : function() {
38840                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
38841                         }
38842                         
38843                     });
38844                 }
38845             }
38846         },
38847         {
38848             xtype : 'Button',
38849             xns : 'Roo',
38850             text : "Login",
38851             listeners : {
38852                 
38853                 click : function () {
38854                         
38855                     this.dialog.el.mask("Logging in");
38856                     this.form.doAction('submit', {
38857                             url: this.dialog.url,
38858                             method: this.dialog.method
38859                     });
38860                 }
38861             }
38862         }
38863     ]
38864   
38865   
38866 })
38867  
38868
38869
38870