f1fee18a3b89199649f6d9695d1d7e60ee02383b
[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     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079          Roo.log('scroll startproc');
3080         clearProc();
3081         proc.el = el;
3082         proc.dir = dir;
3083         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3084     };
3085     
3086     var onFire = function(e, isDrop){
3087        
3088         if(isDrop || !ddm.dragCurrent){ return; }
3089         var dds = Roo.dd.ScrollManager;
3090         if(!dragEl || dragEl != ddm.dragCurrent){
3091             dragEl = ddm.dragCurrent;
3092             // refresh regions on drag start
3093             dds.refreshCache();
3094         }
3095         
3096         var xy = Roo.lib.Event.getXY(e);
3097         var pt = new Roo.lib.Point(xy[0], xy[1]);
3098         for(var id in els){
3099             var el = els[id], r = el._region;
3100             if(r && r.contains(pt) && el.isScrollable()){
3101                 if(r.bottom - pt.y <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "down");
3104                     }
3105                     return;
3106                 }else if(r.right - pt.x <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "left");
3109                     }
3110                     return;
3111                 }else if(pt.y - r.top <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "up");
3114                     }
3115                     return;
3116                 }else if(pt.x - r.left <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "right");
3119                     }
3120                     return;
3121                 }
3122             }
3123         }
3124         clearProc();
3125     };
3126     
3127     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3129     
3130     return {
3131         /**
3132          * Registers new overflow element(s) to auto scroll
3133          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3134          */
3135         register : function(el){
3136             if(el instanceof Array){
3137                 for(var i = 0, len = el.length; i < len; i++) {
3138                         this.register(el[i]);
3139                 }
3140             }else{
3141                 el = Roo.get(el);
3142                 els[el.id] = el;
3143             }
3144             Roo.dd.ScrollManager.els = els;
3145         },
3146         
3147         /**
3148          * Unregisters overflow element(s) so they are no longer scrolled
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3150          */
3151         unregister : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.unregister(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 delete els[el.id];
3159             }
3160         },
3161         
3162         /**
3163          * The number of pixels from the edge of a container the pointer needs to be to 
3164          * trigger scrolling (defaults to 25)
3165          * @type Number
3166          */
3167         thresh : 25,
3168         
3169         /**
3170          * The number of pixels to scroll in each scroll increment (defaults to 50)
3171          * @type Number
3172          */
3173         increment : 100,
3174         
3175         /**
3176          * The frequency of scrolls in milliseconds (defaults to 500)
3177          * @type Number
3178          */
3179         frequency : 500,
3180         
3181         /**
3182          * True to animate the scroll (defaults to true)
3183          * @type Boolean
3184          */
3185         animate: true,
3186         
3187         /**
3188          * The animation duration in seconds - 
3189          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3190          * @type Number
3191          */
3192         animDuration: .4,
3193         
3194         /**
3195          * Manually trigger a cache refresh.
3196          */
3197         refreshCache : function(){
3198             for(var id in els){
3199                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200                     els[id]._region = els[id].getRegion();
3201                 }
3202             }
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215  
3216
3217 /**
3218  * @class Roo.dd.Registry
3219  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3220  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3221  * @singleton
3222  */
3223 Roo.dd.Registry = function(){
3224     var elements = {}; 
3225     var handles = {}; 
3226     var autoIdSeed = 0;
3227
3228     var getId = function(el, autogen){
3229         if(typeof el == "string"){
3230             return el;
3231         }
3232         var id = el.id;
3233         if(!id && autogen !== false){
3234             id = "roodd-" + (++autoIdSeed);
3235             el.id = id;
3236         }
3237         return id;
3238     };
3239     
3240     return {
3241     /**
3242      * Register a drag drop element
3243      * @param {String|HTMLElement} element The id or DOM node to register
3244      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3246      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247      * populated in the data object (if applicable):
3248      * <pre>
3249 Value      Description<br />
3250 ---------  ------------------------------------------<br />
3251 handles    Array of DOM nodes that trigger dragging<br />
3252            for the element being registered<br />
3253 isHandle   True if the element passed in triggers<br />
3254            dragging itself, else false
3255 </pre>
3256      */
3257         register : function(el, data){
3258             data = data || {};
3259             if(typeof el == "string"){
3260                 el = document.getElementById(el);
3261             }
3262             data.ddel = el;
3263             elements[getId(el)] = data;
3264             if(data.isHandle !== false){
3265                 handles[data.ddel.id] = data;
3266             }
3267             if(data.handles){
3268                 var hs = data.handles;
3269                 for(var i = 0, len = hs.length; i < len; i++){
3270                         handles[getId(hs[i])] = data;
3271                 }
3272             }
3273         },
3274
3275     /**
3276      * Unregister a drag drop element
3277      * @param {String|HTMLElement}  element The id or DOM node to unregister
3278      */
3279         unregister : function(el){
3280             var id = getId(el, false);
3281             var data = elements[id];
3282             if(data){
3283                 delete elements[id];
3284                 if(data.handles){
3285                     var hs = data.handles;
3286                     for(var i = 0, len = hs.length; i < len; i++){
3287                         delete handles[getId(hs[i], false)];
3288                     }
3289                 }
3290             }
3291         },
3292
3293     /**
3294      * Returns the handle registered for a DOM Node by id
3295      * @param {String|HTMLElement} id The DOM node or id to look up
3296      * @return {Object} handle The custom handle data
3297      */
3298         getHandle : function(id){
3299             if(typeof id != "string"){ // must be element?
3300                 id = id.id;
3301             }
3302             return handles[id];
3303         },
3304
3305     /**
3306      * Returns the handle that is registered for the DOM node that is the target of the event
3307      * @param {Event} e The event
3308      * @return {Object} handle The custom handle data
3309      */
3310         getHandleFromEvent : function(e){
3311             var t = Roo.lib.Event.getTarget(e);
3312             return t ? handles[t.id] : null;
3313         },
3314
3315     /**
3316      * Returns a custom data object that is registered for a DOM node by id
3317      * @param {String|HTMLElement} id The DOM node or id to look up
3318      * @return {Object} data The custom data
3319      */
3320         getTarget : function(id){
3321             if(typeof id != "string"){ // must be element?
3322                 id = id.id;
3323             }
3324             return elements[id];
3325         },
3326
3327     /**
3328      * Returns a custom data object that is registered for the DOM node that is the target of the event
3329      * @param {Event} e The event
3330      * @return {Object} data The custom data
3331      */
3332         getTargetFromEvent : function(e){
3333             var t = Roo.lib.Event.getTarget(e);
3334             return t ? elements[t.id] || handles[t.id] : null;
3335         }
3336     };
3337 }();/*
3338  * Based on:
3339  * Ext JS Library 1.1.1
3340  * Copyright(c) 2006-2007, Ext JS, LLC.
3341  *
3342  * Originally Released Under LGPL - original licence link has changed is not relivant.
3343  *
3344  * Fork - LGPL
3345  * <script type="text/javascript">
3346  */
3347  
3348
3349 /**
3350  * @class Roo.dd.StatusProxy
3351  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3352  * default drag proxy used by all Roo.dd components.
3353  * @constructor
3354  * @param {Object} config
3355  */
3356 Roo.dd.StatusProxy = function(config){
3357     Roo.apply(this, config);
3358     this.id = this.id || Roo.id();
3359     this.el = new Roo.Layer({
3360         dh: {
3361             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362                 {tag: "div", cls: "x-dd-drop-icon"},
3363                 {tag: "div", cls: "x-dd-drag-ghost"}
3364             ]
3365         }, 
3366         shadow: !config || config.shadow !== false
3367     });
3368     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369     this.dropStatus = this.dropNotAllowed;
3370 };
3371
3372 Roo.dd.StatusProxy.prototype = {
3373     /**
3374      * @cfg {String} dropAllowed
3375      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3376      */
3377     dropAllowed : "x-dd-drop-ok",
3378     /**
3379      * @cfg {String} dropNotAllowed
3380      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3381      */
3382     dropNotAllowed : "x-dd-drop-nodrop",
3383
3384     /**
3385      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386      * over the current target element.
3387      * @param {String} cssClass The css class for the new drop status indicator image
3388      */
3389     setStatus : function(cssClass){
3390         cssClass = cssClass || this.dropNotAllowed;
3391         if(this.dropStatus != cssClass){
3392             this.el.replaceClass(this.dropStatus, cssClass);
3393             this.dropStatus = cssClass;
3394         }
3395     },
3396
3397     /**
3398      * Resets the status indicator to the default dropNotAllowed value
3399      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3400      */
3401     reset : function(clearGhost){
3402         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403         this.dropStatus = this.dropNotAllowed;
3404         if(clearGhost){
3405             this.ghost.update("");
3406         }
3407     },
3408
3409     /**
3410      * Updates the contents of the ghost element
3411      * @param {String} html The html that will replace the current innerHTML of the ghost element
3412      */
3413     update : function(html){
3414         if(typeof html == "string"){
3415             this.ghost.update(html);
3416         }else{
3417             this.ghost.update("");
3418             html.style.margin = "0";
3419             this.ghost.dom.appendChild(html);
3420         }
3421         // ensure float = none set?? cant remember why though.
3422         var el = this.ghost.dom.firstChild;
3423                 if(el){
3424                         Roo.fly(el).setStyle('float', 'none');
3425                 }
3426     },
3427     
3428     /**
3429      * Returns the underlying proxy {@link Roo.Layer}
3430      * @return {Roo.Layer} el
3431     */
3432     getEl : function(){
3433         return this.el;
3434     },
3435
3436     /**
3437      * Returns the ghost element
3438      * @return {Roo.Element} el
3439      */
3440     getGhost : function(){
3441         return this.ghost;
3442     },
3443
3444     /**
3445      * Hides the proxy
3446      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3447      */
3448     hide : function(clear){
3449         this.el.hide();
3450         if(clear){
3451             this.reset(true);
3452         }
3453     },
3454
3455     /**
3456      * Stops the repair animation if it's currently running
3457      */
3458     stop : function(){
3459         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3460             this.anim.stop();
3461         }
3462     },
3463
3464     /**
3465      * Displays this proxy
3466      */
3467     show : function(){
3468         this.el.show();
3469     },
3470
3471     /**
3472      * Force the Layer to sync its shadow and shim positions to the element
3473      */
3474     sync : function(){
3475         this.el.sync();
3476     },
3477
3478     /**
3479      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3480      * invalid drop operation by the item being dragged.
3481      * @param {Array} xy The XY position of the element ([x, y])
3482      * @param {Function} callback The function to call after the repair is complete
3483      * @param {Object} scope The scope in which to execute the callback
3484      */
3485     repair : function(xy, callback, scope){
3486         this.callback = callback;
3487         this.scope = scope;
3488         if(xy && this.animRepair !== false){
3489             this.el.addClass("x-dd-drag-repair");
3490             this.el.hideUnders(true);
3491             this.anim = this.el.shift({
3492                 duration: this.repairDuration || .5,
3493                 easing: 'easeOut',
3494                 xy: xy,
3495                 stopFx: true,
3496                 callback: this.afterRepair,
3497                 scope: this
3498             });
3499         }else{
3500             this.afterRepair();
3501         }
3502     },
3503
3504     // private
3505     afterRepair : function(){
3506         this.hide(true);
3507         if(typeof this.callback == "function"){
3508             this.callback.call(this.scope || this);
3509         }
3510         this.callback = null;
3511         this.scope = null;
3512     }
3513 };/*
3514  * Based on:
3515  * Ext JS Library 1.1.1
3516  * Copyright(c) 2006-2007, Ext JS, LLC.
3517  *
3518  * Originally Released Under LGPL - original licence link has changed is not relivant.
3519  *
3520  * Fork - LGPL
3521  * <script type="text/javascript">
3522  */
3523
3524 /**
3525  * @class Roo.dd.DragSource
3526  * @extends Roo.dd.DDProxy
3527  * A simple class that provides the basic implementation needed to make any element draggable.
3528  * @constructor
3529  * @param {String/HTMLElement/Element} el The container element
3530  * @param {Object} config
3531  */
3532 Roo.dd.DragSource = function(el, config){
3533     this.el = Roo.get(el);
3534     this.dragData = {};
3535     
3536     Roo.apply(this, config);
3537     
3538     if(!this.proxy){
3539         this.proxy = new Roo.dd.StatusProxy();
3540     }
3541
3542     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3544     
3545     this.dragging = false;
3546 };
3547
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3549     /**
3550      * @cfg {String} dropAllowed
3551      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3552      */
3553     dropAllowed : "x-dd-drop-ok",
3554     /**
3555      * @cfg {String} dropNotAllowed
3556      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3557      */
3558     dropNotAllowed : "x-dd-drop-nodrop",
3559
3560     /**
3561      * Returns the data object associated with this drag source
3562      * @return {Object} data An object containing arbitrary data
3563      */
3564     getDragData : function(e){
3565         return this.dragData;
3566     },
3567
3568     // private
3569     onDragEnter : function(e, id){
3570         var target = Roo.dd.DragDropMgr.getDDById(id);
3571         this.cachedTarget = target;
3572         if(this.beforeDragEnter(target, e, id) !== false){
3573             if(target.isNotifyTarget){
3574                 var status = target.notifyEnter(this, e, this.dragData);
3575                 this.proxy.setStatus(status);
3576             }else{
3577                 this.proxy.setStatus(this.dropAllowed);
3578             }
3579             
3580             if(this.afterDragEnter){
3581                 /**
3582                  * An empty function by default, but provided so that you can perform a custom action
3583                  * when the dragged item enters the drop target by providing an implementation.
3584                  * @param {Roo.dd.DragDrop} target The drop target
3585                  * @param {Event} e The event object
3586                  * @param {String} id The id of the dragged element
3587                  * @method afterDragEnter
3588                  */
3589                 this.afterDragEnter(target, e, id);
3590             }
3591         }
3592     },
3593
3594     /**
3595      * An empty function by default, but provided so that you can perform a custom action
3596      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597      * @param {Roo.dd.DragDrop} target The drop target
3598      * @param {Event} e The event object
3599      * @param {String} id The id of the dragged element
3600      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3601      */
3602     beforeDragEnter : function(target, e, id){
3603         return true;
3604     },
3605
3606     // private
3607     alignElWithMouse: function() {
3608         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3609         this.proxy.sync();
3610     },
3611
3612     // private
3613     onDragOver : function(e, id){
3614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615         if(this.beforeDragOver(target, e, id) !== false){
3616             if(target.isNotifyTarget){
3617                 var status = target.notifyOver(this, e, this.dragData);
3618                 this.proxy.setStatus(status);
3619             }
3620
3621             if(this.afterDragOver){
3622                 /**
3623                  * An empty function by default, but provided so that you can perform a custom action
3624                  * while the dragged item is over the drop target by providing an implementation.
3625                  * @param {Roo.dd.DragDrop} target The drop target
3626                  * @param {Event} e The event object
3627                  * @param {String} id The id of the dragged element
3628                  * @method afterDragOver
3629                  */
3630                 this.afterDragOver(target, e, id);
3631             }
3632         }
3633     },
3634
3635     /**
3636      * An empty function by default, but provided so that you can perform a custom action
3637      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638      * @param {Roo.dd.DragDrop} target The drop target
3639      * @param {Event} e The event object
3640      * @param {String} id The id of the dragged element
3641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3642      */
3643     beforeDragOver : function(target, e, id){
3644         return true;
3645     },
3646
3647     // private
3648     onDragOut : function(e, id){
3649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650         if(this.beforeDragOut(target, e, id) !== false){
3651             if(target.isNotifyTarget){
3652                 target.notifyOut(this, e, this.dragData);
3653             }
3654             this.proxy.reset();
3655             if(this.afterDragOut){
3656                 /**
3657                  * An empty function by default, but provided so that you can perform a custom action
3658                  * after the dragged item is dragged out of the target without dropping.
3659                  * @param {Roo.dd.DragDrop} target The drop target
3660                  * @param {Event} e The event object
3661                  * @param {String} id The id of the dragged element
3662                  * @method afterDragOut
3663                  */
3664                 this.afterDragOut(target, e, id);
3665             }
3666         }
3667         this.cachedTarget = null;
3668     },
3669
3670     /**
3671      * An empty function by default, but provided so that you can perform a custom action before the dragged
3672      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673      * @param {Roo.dd.DragDrop} target The drop target
3674      * @param {Event} e The event object
3675      * @param {String} id The id of the dragged element
3676      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3677      */
3678     beforeDragOut : function(target, e, id){
3679         return true;
3680     },
3681     
3682     // private
3683     onDragDrop : function(e, id){
3684         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685         if(this.beforeDragDrop(target, e, id) !== false){
3686             if(target.isNotifyTarget){
3687                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688                     this.onValidDrop(target, e, id);
3689                 }else{
3690                     this.onInvalidDrop(target, e, id);
3691                 }
3692             }else{
3693                 this.onValidDrop(target, e, id);
3694             }
3695             
3696             if(this.afterDragDrop){
3697                 /**
3698                  * An empty function by default, but provided so that you can perform a custom action
3699                  * after a valid drag drop has occurred by providing an implementation.
3700                  * @param {Roo.dd.DragDrop} target The drop target
3701                  * @param {Event} e The event object
3702                  * @param {String} id The id of the dropped element
3703                  * @method afterDragDrop
3704                  */
3705                 this.afterDragDrop(target, e, id);
3706             }
3707         }
3708         delete this.cachedTarget;
3709     },
3710
3711     /**
3712      * An empty function by default, but provided so that you can perform a custom action before the dragged
3713      * item is dropped onto the target and optionally cancel the onDragDrop.
3714      * @param {Roo.dd.DragDrop} target The drop target
3715      * @param {Event} e The event object
3716      * @param {String} id The id of the dragged element
3717      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3718      */
3719     beforeDragDrop : function(target, e, id){
3720         return true;
3721     },
3722
3723     // private
3724     onValidDrop : function(target, e, id){
3725         this.hideProxy();
3726         if(this.afterValidDrop){
3727             /**
3728              * An empty function by default, but provided so that you can perform a custom action
3729              * after a valid drop has occurred by providing an implementation.
3730              * @param {Object} target The target DD 
3731              * @param {Event} e The event object
3732              * @param {String} id The id of the dropped element
3733              * @method afterInvalidDrop
3734              */
3735             this.afterValidDrop(target, e, id);
3736         }
3737     },
3738
3739     // private
3740     getRepairXY : function(e, data){
3741         return this.el.getXY();  
3742     },
3743
3744     // private
3745     onInvalidDrop : function(target, e, id){
3746         this.beforeInvalidDrop(target, e, id);
3747         if(this.cachedTarget){
3748             if(this.cachedTarget.isNotifyTarget){
3749                 this.cachedTarget.notifyOut(this, e, this.dragData);
3750             }
3751             this.cacheTarget = null;
3752         }
3753         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3754
3755         if(this.afterInvalidDrop){
3756             /**
3757              * An empty function by default, but provided so that you can perform a custom action
3758              * after an invalid drop has occurred by providing an implementation.
3759              * @param {Event} e The event object
3760              * @param {String} id The id of the dropped element
3761              * @method afterInvalidDrop
3762              */
3763             this.afterInvalidDrop(e, id);
3764         }
3765     },
3766
3767     // private
3768     afterRepair : function(){
3769         if(Roo.enableFx){
3770             this.el.highlight(this.hlColor || "c3daf9");
3771         }
3772         this.dragging = false;
3773     },
3774
3775     /**
3776      * An empty function by default, but provided so that you can perform a custom action after an invalid
3777      * drop has occurred.
3778      * @param {Roo.dd.DragDrop} target The drop target
3779      * @param {Event} e The event object
3780      * @param {String} id The id of the dragged element
3781      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3782      */
3783     beforeInvalidDrop : function(target, e, id){
3784         return true;
3785     },
3786
3787     // private
3788     handleMouseDown : function(e){
3789         if(this.dragging) {
3790             return;
3791         }
3792         var data = this.getDragData(e);
3793         if(data && this.onBeforeDrag(data, e) !== false){
3794             this.dragData = data;
3795             this.proxy.stop();
3796             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3797         } 
3798     },
3799
3800     /**
3801      * An empty function by default, but provided so that you can perform a custom action before the initial
3802      * drag event begins and optionally cancel it.
3803      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804      * @param {Event} e The event object
3805      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3806      */
3807     onBeforeDrag : function(data, e){
3808         return true;
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action once the initial
3813      * drag event has begun.  The drag cannot be canceled from this function.
3814      * @param {Number} x The x position of the click on the dragged object
3815      * @param {Number} y The y position of the click on the dragged object
3816      */
3817     onStartDrag : Roo.emptyFn,
3818
3819     // private - YUI override
3820     startDrag : function(x, y){
3821         this.proxy.reset();
3822         this.dragging = true;
3823         this.proxy.update("");
3824         this.onInitDrag(x, y);
3825         this.proxy.show();
3826     },
3827
3828     // private
3829     onInitDrag : function(x, y){
3830         var clone = this.el.dom.cloneNode(true);
3831         clone.id = Roo.id(); // prevent duplicate ids
3832         this.proxy.update(clone);
3833         this.onStartDrag(x, y);
3834         return true;
3835     },
3836
3837     /**
3838      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3840      */
3841     getProxy : function(){
3842         return this.proxy;  
3843     },
3844
3845     /**
3846      * Hides the drag source's {@link Roo.dd.StatusProxy}
3847      */
3848     hideProxy : function(){
3849         this.proxy.hide();  
3850         this.proxy.reset(true);
3851         this.dragging = false;
3852     },
3853
3854     // private
3855     triggerCacheRefresh : function(){
3856         Roo.dd.DDM.refreshCache(this.groups);
3857     },
3858
3859     // private - override to prevent hiding
3860     b4EndDrag: function(e) {
3861     },
3862
3863     // private - override to prevent moving
3864     endDrag : function(e){
3865         this.onEndDrag(this.dragData, e);
3866     },
3867
3868     // private
3869     onEndDrag : function(data, e){
3870     },
3871     
3872     // private - pin to cursor
3873     autoOffset : function(x, y) {
3874         this.setDelta(-12, -20);
3875     }    
3876 });/*
3877  * Based on:
3878  * Ext JS Library 1.1.1
3879  * Copyright(c) 2006-2007, Ext JS, LLC.
3880  *
3881  * Originally Released Under LGPL - original licence link has changed is not relivant.
3882  *
3883  * Fork - LGPL
3884  * <script type="text/javascript">
3885  */
3886
3887
3888 /**
3889  * @class Roo.dd.DropTarget
3890  * @extends Roo.dd.DDTarget
3891  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3893  * @constructor
3894  * @param {String/HTMLElement/Element} el The container element
3895  * @param {Object} config
3896  */
3897 Roo.dd.DropTarget = function(el, config){
3898     this.el = Roo.get(el);
3899     
3900     var listeners = false; ;
3901     if (config && config.listeners) {
3902         listeners= config.listeners;
3903         delete config.listeners;
3904     }
3905     Roo.apply(this, config);
3906     
3907     if(this.containerScroll){
3908         Roo.dd.ScrollManager.register(this.el);
3909     }
3910     this.addEvents( {
3911          /**
3912          * @scope Roo.dd.DropTarget
3913          */
3914          
3915          /**
3916          * @event enter
3917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3920          * 
3921          * IMPORTANT : it should set this.overClass and this.dropAllowed
3922          * 
3923          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924          * @param {Event} e The event
3925          * @param {Object} data An object containing arbitrary data supplied by the drag source
3926          */
3927         "enter" : true,
3928         
3929          /**
3930          * @event over
3931          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932          * This method will be called on every mouse movement while the drag source is over the drop target.
3933          * This default implementation simply returns the dropAllowed config value.
3934          * 
3935          * IMPORTANT : it should set this.dropAllowed
3936          * 
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          
3941          */
3942         "over" : true,
3943         /**
3944          * @event out
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3947          * overClass (if any) from the drop element.
3948          * 
3949          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3950          * @param {Event} e The event
3951          * @param {Object} data An object containing arbitrary data supplied by the drag source
3952          */
3953          "out" : true,
3954          
3955         /**
3956          * @event drop
3957          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3958          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3959          * implementation that does something to process the drop event and returns true so that the drag source's
3960          * repair action does not run.
3961          * 
3962          * IMPORTANT : it should set this.success
3963          * 
3964          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3965          * @param {Event} e The event
3966          * @param {Object} data An object containing arbitrary data supplied by the drag source
3967         */
3968          "drop" : true
3969     });
3970             
3971      
3972     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3973         this.el.dom, 
3974         this.ddGroup || this.group,
3975         {
3976             isTarget: true,
3977             listeners : listeners || {} 
3978            
3979         
3980         }
3981     );
3982
3983 };
3984
3985 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3986     /**
3987      * @cfg {String} overClass
3988      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3989      */
3990      /**
3991      * @cfg {String} ddGroup
3992      * The drag drop group to handle drop events for
3993      */
3994      
3995     /**
3996      * @cfg {String} dropAllowed
3997      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3998      */
3999     dropAllowed : "x-dd-drop-ok",
4000     /**
4001      * @cfg {String} dropNotAllowed
4002      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4003      */
4004     dropNotAllowed : "x-dd-drop-nodrop",
4005     /**
4006      * @cfg {boolean} success
4007      * set this after drop listener.. 
4008      */
4009     success : false,
4010     /**
4011      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4012      * if the drop point is valid for over/enter..
4013      */
4014     valid : false,
4015     // private
4016     isTarget : true,
4017
4018     // private
4019     isNotifyTarget : true,
4020     
4021     /**
4022      * @hide
4023      */
4024     notifyEnter : function(dd, e, data)
4025     {
4026         this.valid = true;
4027         this.fireEvent('enter', dd, e, data);
4028         if(this.overClass){
4029             this.el.addClass(this.overClass);
4030         }
4031         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4032             this.valid ? this.dropAllowed : this.dropNotAllowed
4033         );
4034     },
4035
4036     /**
4037      * @hide
4038      */
4039     notifyOver : function(dd, e, data)
4040     {
4041         this.valid = true;
4042         this.fireEvent('over', dd, e, data);
4043         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4044             this.valid ? this.dropAllowed : this.dropNotAllowed
4045         );
4046     },
4047
4048     /**
4049      * @hide
4050      */
4051     notifyOut : function(dd, e, data)
4052     {
4053         this.fireEvent('out', dd, e, data);
4054         if(this.overClass){
4055             this.el.removeClass(this.overClass);
4056         }
4057     },
4058
4059     /**
4060      * @hide
4061      */
4062     notifyDrop : function(dd, e, data)
4063     {
4064         this.success = false;
4065         this.fireEvent('drop', dd, e, data);
4066         return this.success;
4067     }
4068 });/*
4069  * Based on:
4070  * Ext JS Library 1.1.1
4071  * Copyright(c) 2006-2007, Ext JS, LLC.
4072  *
4073  * Originally Released Under LGPL - original licence link has changed is not relivant.
4074  *
4075  * Fork - LGPL
4076  * <script type="text/javascript">
4077  */
4078
4079
4080 /**
4081  * @class Roo.dd.DragZone
4082  * @extends Roo.dd.DragSource
4083  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4084  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4085  * @constructor
4086  * @param {String/HTMLElement/Element} el The container element
4087  * @param {Object} config
4088  */
4089 Roo.dd.DragZone = function(el, config){
4090     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4091     if(this.containerScroll){
4092         Roo.dd.ScrollManager.register(this.el);
4093     }
4094 };
4095
4096 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4097     /**
4098      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4099      * for auto scrolling during drag operations.
4100      */
4101     /**
4102      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4103      * method after a failed drop (defaults to "c3daf9" - light blue)
4104      */
4105
4106     /**
4107      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4108      * for a valid target to drag based on the mouse down. Override this method
4109      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4110      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4111      * @param {EventObject} e The mouse down event
4112      * @return {Object} The dragData
4113      */
4114     getDragData : function(e){
4115         return Roo.dd.Registry.getHandleFromEvent(e);
4116     },
4117     
4118     /**
4119      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4120      * this.dragData.ddel
4121      * @param {Number} x The x position of the click on the dragged object
4122      * @param {Number} y The y position of the click on the dragged object
4123      * @return {Boolean} true to continue the drag, false to cancel
4124      */
4125     onInitDrag : function(x, y){
4126         this.proxy.update(this.dragData.ddel.cloneNode(true));
4127         this.onStartDrag(x, y);
4128         return true;
4129     },
4130     
4131     /**
4132      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4133      */
4134     afterRepair : function(){
4135         if(Roo.enableFx){
4136             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4137         }
4138         this.dragging = false;
4139     },
4140
4141     /**
4142      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4143      * the XY of this.dragData.ddel
4144      * @param {EventObject} e The mouse up event
4145      * @return {Array} The xy location (e.g. [100, 200])
4146      */
4147     getRepairXY : function(e){
4148         return Roo.Element.fly(this.dragData.ddel).getXY();  
4149     }
4150 });/*
4151  * Based on:
4152  * Ext JS Library 1.1.1
4153  * Copyright(c) 2006-2007, Ext JS, LLC.
4154  *
4155  * Originally Released Under LGPL - original licence link has changed is not relivant.
4156  *
4157  * Fork - LGPL
4158  * <script type="text/javascript">
4159  */
4160 /**
4161  * @class Roo.dd.DropZone
4162  * @extends Roo.dd.DropTarget
4163  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4164  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4165  * @constructor
4166  * @param {String/HTMLElement/Element} el The container element
4167  * @param {Object} config
4168  */
4169 Roo.dd.DropZone = function(el, config){
4170     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4171 };
4172
4173 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4174     /**
4175      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4176      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4177      * provide your own custom lookup.
4178      * @param {Event} e The event
4179      * @return {Object} data The custom data
4180      */
4181     getTargetFromEvent : function(e){
4182         return Roo.dd.Registry.getTargetFromEvent(e);
4183     },
4184
4185     /**
4186      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4187      * that it has registered.  This method has no default implementation and should be overridden to provide
4188      * node-specific processing if necessary.
4189      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4190      * {@link #getTargetFromEvent} for this node)
4191      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4192      * @param {Event} e The event
4193      * @param {Object} data An object containing arbitrary data supplied by the drag source
4194      */
4195     onNodeEnter : function(n, dd, e, data){
4196         
4197     },
4198
4199     /**
4200      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4201      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4202      * overridden to provide the proper feedback.
4203      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4204      * {@link #getTargetFromEvent} for this node)
4205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4206      * @param {Event} e The event
4207      * @param {Object} data An object containing arbitrary data supplied by the drag source
4208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4209      * underlying {@link Roo.dd.StatusProxy} can be updated
4210      */
4211     onNodeOver : function(n, dd, e, data){
4212         return this.dropAllowed;
4213     },
4214
4215     /**
4216      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4217      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4218      * node-specific processing if necessary.
4219      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4220      * {@link #getTargetFromEvent} for this node)
4221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4222      * @param {Event} e The event
4223      * @param {Object} data An object containing arbitrary data supplied by the drag source
4224      */
4225     onNodeOut : function(n, dd, e, data){
4226         
4227     },
4228
4229     /**
4230      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4231      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4232      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4233      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4234      * {@link #getTargetFromEvent} for this node)
4235      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4236      * @param {Event} e The event
4237      * @param {Object} data An object containing arbitrary data supplied by the drag source
4238      * @return {Boolean} True if the drop was valid, else false
4239      */
4240     onNodeDrop : function(n, dd, e, data){
4241         return false;
4242     },
4243
4244     /**
4245      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4246      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4247      * it should be overridden to provide the proper feedback if necessary.
4248      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4249      * @param {Event} e The event
4250      * @param {Object} data An object containing arbitrary data supplied by the drag source
4251      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4252      * underlying {@link Roo.dd.StatusProxy} can be updated
4253      */
4254     onContainerOver : function(dd, e, data){
4255         return this.dropNotAllowed;
4256     },
4257
4258     /**
4259      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4260      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4261      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4262      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4263      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4264      * @param {Event} e The event
4265      * @param {Object} data An object containing arbitrary data supplied by the drag source
4266      * @return {Boolean} True if the drop was valid, else false
4267      */
4268     onContainerDrop : function(dd, e, data){
4269         return false;
4270     },
4271
4272     /**
4273      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4274      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4275      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4276      * you should override this method and provide a custom implementation.
4277      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4278      * @param {Event} e The event
4279      * @param {Object} data An object containing arbitrary data supplied by the drag source
4280      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4281      * underlying {@link Roo.dd.StatusProxy} can be updated
4282      */
4283     notifyEnter : function(dd, e, data){
4284         return this.dropNotAllowed;
4285     },
4286
4287     /**
4288      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4289      * This method will be called on every mouse movement while the drag source is over the drop zone.
4290      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4291      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4292      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4293      * registered node, it will call {@link #onContainerOver}.
4294      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4295      * @param {Event} e The event
4296      * @param {Object} data An object containing arbitrary data supplied by the drag source
4297      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4298      * underlying {@link Roo.dd.StatusProxy} can be updated
4299      */
4300     notifyOver : function(dd, e, data){
4301         var n = this.getTargetFromEvent(e);
4302         if(!n){ // not over valid drop target
4303             if(this.lastOverNode){
4304                 this.onNodeOut(this.lastOverNode, dd, e, data);
4305                 this.lastOverNode = null;
4306             }
4307             return this.onContainerOver(dd, e, data);
4308         }
4309         if(this.lastOverNode != n){
4310             if(this.lastOverNode){
4311                 this.onNodeOut(this.lastOverNode, dd, e, data);
4312             }
4313             this.onNodeEnter(n, dd, e, data);
4314             this.lastOverNode = n;
4315         }
4316         return this.onNodeOver(n, dd, e, data);
4317     },
4318
4319     /**
4320      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4321      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4322      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4323      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4324      * @param {Event} e The event
4325      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4326      */
4327     notifyOut : function(dd, e, data){
4328         if(this.lastOverNode){
4329             this.onNodeOut(this.lastOverNode, dd, e, data);
4330             this.lastOverNode = null;
4331         }
4332     },
4333
4334     /**
4335      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4336      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4337      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4338      * otherwise it will call {@link #onContainerDrop}.
4339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4340      * @param {Event} e The event
4341      * @param {Object} data An object containing arbitrary data supplied by the drag source
4342      * @return {Boolean} True if the drop was valid, else false
4343      */
4344     notifyDrop : function(dd, e, data){
4345         if(this.lastOverNode){
4346             this.onNodeOut(this.lastOverNode, dd, e, data);
4347             this.lastOverNode = null;
4348         }
4349         var n = this.getTargetFromEvent(e);
4350         return n ?
4351             this.onNodeDrop(n, dd, e, data) :
4352             this.onContainerDrop(dd, e, data);
4353     },
4354
4355     // private
4356     triggerCacheRefresh : function(){
4357         Roo.dd.DDM.refreshCache(this.groups);
4358     }  
4359 });/*
4360  * Based on:
4361  * Ext JS Library 1.1.1
4362  * Copyright(c) 2006-2007, Ext JS, LLC.
4363  *
4364  * Originally Released Under LGPL - original licence link has changed is not relivant.
4365  *
4366  * Fork - LGPL
4367  * <script type="text/javascript">
4368  */
4369
4370
4371 /**
4372  * @class Roo.data.SortTypes
4373  * @singleton
4374  * Defines the default sorting (casting?) comparison functions used when sorting data.
4375  */
4376 Roo.data.SortTypes = {
4377     /**
4378      * Default sort that does nothing
4379      * @param {Mixed} s The value being converted
4380      * @return {Mixed} The comparison value
4381      */
4382     none : function(s){
4383         return s;
4384     },
4385     
4386     /**
4387      * The regular expression used to strip tags
4388      * @type {RegExp}
4389      * @property
4390      */
4391     stripTagsRE : /<\/?[^>]+>/gi,
4392     
4393     /**
4394      * Strips all HTML tags to sort on text only
4395      * @param {Mixed} s The value being converted
4396      * @return {String} The comparison value
4397      */
4398     asText : function(s){
4399         return String(s).replace(this.stripTagsRE, "");
4400     },
4401     
4402     /**
4403      * Strips all HTML tags to sort on text only - Case insensitive
4404      * @param {Mixed} s The value being converted
4405      * @return {String} The comparison value
4406      */
4407     asUCText : function(s){
4408         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4409     },
4410     
4411     /**
4412      * Case insensitive string
4413      * @param {Mixed} s The value being converted
4414      * @return {String} The comparison value
4415      */
4416     asUCString : function(s) {
4417         return String(s).toUpperCase();
4418     },
4419     
4420     /**
4421      * Date sorting
4422      * @param {Mixed} s The value being converted
4423      * @return {Number} The comparison value
4424      */
4425     asDate : function(s) {
4426         if(!s){
4427             return 0;
4428         }
4429         if(s instanceof Date){
4430             return s.getTime();
4431         }
4432         return Date.parse(String(s));
4433     },
4434     
4435     /**
4436      * Float sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Float} The comparison value
4439      */
4440     asFloat : function(s) {
4441         var val = parseFloat(String(s).replace(/,/g, ""));
4442         if(isNaN(val)) val = 0;
4443         return val;
4444     },
4445     
4446     /**
4447      * Integer sorting
4448      * @param {Mixed} s The value being converted
4449      * @return {Number} The comparison value
4450      */
4451     asInt : function(s) {
4452         var val = parseInt(String(s).replace(/,/g, ""));
4453         if(isNaN(val)) val = 0;
4454         return val;
4455     }
4456 };/*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466
4467 /**
4468 * @class Roo.data.Record
4469  * Instances of this class encapsulate both record <em>definition</em> information, and record
4470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4471  * to access Records cached in an {@link Roo.data.Store} object.<br>
4472  * <p>
4473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4475  * objects.<br>
4476  * <p>
4477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4478  * @constructor
4479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4480  * {@link #create}. The parameters are the same.
4481  * @param {Array} data An associative Array of data values keyed by the field name.
4482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4484  * not specified an integer id is generated.
4485  */
4486 Roo.data.Record = function(data, id){
4487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4488     this.data = data;
4489 };
4490
4491 /**
4492  * Generate a constructor for a specific record layout.
4493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4495  * Each field definition object may contain the following properties: <ul>
4496  * <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,
4497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4503  * this may be omitted.</p></li>
4504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4505  * <ul><li>auto (Default, implies no conversion)</li>
4506  * <li>string</li>
4507  * <li>int</li>
4508  * <li>float</li>
4509  * <li>boolean</li>
4510  * <li>date</li></ul></p></li>
4511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4514  * by the Reader into an object that will be stored in the Record. It is passed the
4515  * following parameters:<ul>
4516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4517  * </ul></p></li>
4518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4519  * </ul>
4520  * <br>usage:<br><pre><code>
4521 var TopicRecord = Roo.data.Record.create(
4522     {name: 'title', mapping: 'topic_title'},
4523     {name: 'author', mapping: 'username'},
4524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4526     {name: 'lastPoster', mapping: 'user2'},
4527     {name: 'excerpt', mapping: 'post_text'}
4528 );
4529
4530 var myNewRecord = new TopicRecord({
4531     title: 'Do my job please',
4532     author: 'noobie',
4533     totalPosts: 1,
4534     lastPost: new Date(),
4535     lastPoster: 'Animal',
4536     excerpt: 'No way dude!'
4537 });
4538 myStore.add(myNewRecord);
4539 </code></pre>
4540  * @method create
4541  * @static
4542  */
4543 Roo.data.Record.create = function(o){
4544     var f = function(){
4545         f.superclass.constructor.apply(this, arguments);
4546     };
4547     Roo.extend(f, Roo.data.Record);
4548     var p = f.prototype;
4549     p.fields = new Roo.util.MixedCollection(false, function(field){
4550         return field.name;
4551     });
4552     for(var i = 0, len = o.length; i < len; i++){
4553         p.fields.add(new Roo.data.Field(o[i]));
4554     }
4555     f.getField = function(name){
4556         return p.fields.get(name);  
4557     };
4558     return f;
4559 };
4560
4561 Roo.data.Record.AUTO_ID = 1000;
4562 Roo.data.Record.EDIT = 'edit';
4563 Roo.data.Record.REJECT = 'reject';
4564 Roo.data.Record.COMMIT = 'commit';
4565
4566 Roo.data.Record.prototype = {
4567     /**
4568      * Readonly flag - true if this record has been modified.
4569      * @type Boolean
4570      */
4571     dirty : false,
4572     editing : false,
4573     error: null,
4574     modified: null,
4575
4576     // private
4577     join : function(store){
4578         this.store = store;
4579     },
4580
4581     /**
4582      * Set the named field to the specified value.
4583      * @param {String} name The name of the field to set.
4584      * @param {Object} value The value to set the field to.
4585      */
4586     set : function(name, value){
4587         if(this.data[name] == value){
4588             return;
4589         }
4590         this.dirty = true;
4591         if(!this.modified){
4592             this.modified = {};
4593         }
4594         if(typeof this.modified[name] == 'undefined'){
4595             this.modified[name] = this.data[name];
4596         }
4597         this.data[name] = value;
4598         if(!this.editing && this.store){
4599             this.store.afterEdit(this);
4600         }       
4601     },
4602
4603     /**
4604      * Get the value of the named field.
4605      * @param {String} name The name of the field to get the value of.
4606      * @return {Object} The value of the field.
4607      */
4608     get : function(name){
4609         return this.data[name]; 
4610     },
4611
4612     // private
4613     beginEdit : function(){
4614         this.editing = true;
4615         this.modified = {}; 
4616     },
4617
4618     // private
4619     cancelEdit : function(){
4620         this.editing = false;
4621         delete this.modified;
4622     },
4623
4624     // private
4625     endEdit : function(){
4626         this.editing = false;
4627         if(this.dirty && this.store){
4628             this.store.afterEdit(this);
4629         }
4630     },
4631
4632     /**
4633      * Usually called by the {@link Roo.data.Store} which owns the Record.
4634      * Rejects all changes made to the Record since either creation, or the last commit operation.
4635      * Modified fields are reverted to their original values.
4636      * <p>
4637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4638      * of reject operations.
4639      */
4640     reject : function(){
4641         var m = this.modified;
4642         for(var n in m){
4643             if(typeof m[n] != "function"){
4644                 this.data[n] = m[n];
4645             }
4646         }
4647         this.dirty = false;
4648         delete this.modified;
4649         this.editing = false;
4650         if(this.store){
4651             this.store.afterReject(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Commits all changes made to the Record since either creation, or the last commit operation.
4658      * <p>
4659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4660      * of commit operations.
4661      */
4662     commit : function(){
4663         this.dirty = false;
4664         delete this.modified;
4665         this.editing = false;
4666         if(this.store){
4667             this.store.afterCommit(this);
4668         }
4669     },
4670
4671     // private
4672     hasError : function(){
4673         return this.error != null;
4674     },
4675
4676     // private
4677     clearError : function(){
4678         this.error = null;
4679     },
4680
4681     /**
4682      * Creates a copy of this record.
4683      * @param {String} id (optional) A new record id if you don't want to use this record's id
4684      * @return {Record}
4685      */
4686     copy : function(newId) {
4687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4688     }
4689 };/*
4690  * Based on:
4691  * Ext JS Library 1.1.1
4692  * Copyright(c) 2006-2007, Ext JS, LLC.
4693  *
4694  * Originally Released Under LGPL - original licence link has changed is not relivant.
4695  *
4696  * Fork - LGPL
4697  * <script type="text/javascript">
4698  */
4699
4700
4701
4702 /**
4703  * @class Roo.data.Store
4704  * @extends Roo.util.Observable
4705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4707  * <p>
4708  * 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
4709  * has no knowledge of the format of the data returned by the Proxy.<br>
4710  * <p>
4711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4712  * instances from the data object. These records are cached and made available through accessor functions.
4713  * @constructor
4714  * Creates a new Store.
4715  * @param {Object} config A config object containing the objects needed for the Store to access data,
4716  * and read the data into Records.
4717  */
4718 Roo.data.Store = function(config){
4719     this.data = new Roo.util.MixedCollection(false);
4720     this.data.getKey = function(o){
4721         return o.id;
4722     };
4723     this.baseParams = {};
4724     // private
4725     this.paramNames = {
4726         "start" : "start",
4727         "limit" : "limit",
4728         "sort" : "sort",
4729         "dir" : "dir",
4730         "multisort" : "_multisort"
4731     };
4732
4733     if(config && config.data){
4734         this.inlineData = config.data;
4735         delete config.data;
4736     }
4737
4738     Roo.apply(this, config);
4739     
4740     if(this.reader){ // reader passed
4741         this.reader = Roo.factory(this.reader, Roo.data);
4742         this.reader.xmodule = this.xmodule || false;
4743         if(!this.recordType){
4744             this.recordType = this.reader.recordType;
4745         }
4746         if(this.reader.onMetaChange){
4747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4748         }
4749     }
4750
4751     if(this.recordType){
4752         this.fields = this.recordType.prototype.fields;
4753     }
4754     this.modified = [];
4755
4756     this.addEvents({
4757         /**
4758          * @event datachanged
4759          * Fires when the data cache has changed, and a widget which is using this Store
4760          * as a Record cache should refresh its view.
4761          * @param {Store} this
4762          */
4763         datachanged : true,
4764         /**
4765          * @event metachange
4766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4767          * @param {Store} this
4768          * @param {Object} meta The JSON metadata
4769          */
4770         metachange : true,
4771         /**
4772          * @event add
4773          * Fires when Records have been added to the Store
4774          * @param {Store} this
4775          * @param {Roo.data.Record[]} records The array of Records added
4776          * @param {Number} index The index at which the record(s) were added
4777          */
4778         add : true,
4779         /**
4780          * @event remove
4781          * Fires when a Record has been removed from the Store
4782          * @param {Store} this
4783          * @param {Roo.data.Record} record The Record that was removed
4784          * @param {Number} index The index at which the record was removed
4785          */
4786         remove : true,
4787         /**
4788          * @event update
4789          * Fires when a Record has been updated
4790          * @param {Store} this
4791          * @param {Roo.data.Record} record The Record that was updated
4792          * @param {String} operation The update operation being performed.  Value may be one of:
4793          * <pre><code>
4794  Roo.data.Record.EDIT
4795  Roo.data.Record.REJECT
4796  Roo.data.Record.COMMIT
4797          * </code></pre>
4798          */
4799         update : true,
4800         /**
4801          * @event clear
4802          * Fires when the data cache has been cleared.
4803          * @param {Store} this
4804          */
4805         clear : true,
4806         /**
4807          * @event beforeload
4808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4809          * the load action will be canceled.
4810          * @param {Store} this
4811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4812          */
4813         beforeload : true,
4814         /**
4815          * @event load
4816          * Fires after a new set of Records has been loaded.
4817          * @param {Store} this
4818          * @param {Roo.data.Record[]} records The Records that were loaded
4819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4820          */
4821         load : true,
4822         /**
4823          * @event loadexception
4824          * Fires if an exception occurs in the Proxy during loading.
4825          * Called with the signature of the Proxy's "loadexception" event.
4826          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4827          * 
4828          * @param {Proxy} 
4829          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4830          * @param {Object} load options 
4831          * @param {Object} jsonData from your request (normally this contains the Exception)
4832          */
4833         loadexception : true
4834     });
4835     
4836     if(this.proxy){
4837         this.proxy = Roo.factory(this.proxy, Roo.data);
4838         this.proxy.xmodule = this.xmodule || false;
4839         this.relayEvents(this.proxy,  ["loadexception"]);
4840     }
4841     this.sortToggle = {};
4842     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4843
4844     Roo.data.Store.superclass.constructor.call(this);
4845
4846     if(this.inlineData){
4847         this.loadData(this.inlineData);
4848         delete this.inlineData;
4849     }
4850 };
4851 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4852      /**
4853     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4854     * without a remote query - used by combo/forms at present.
4855     */
4856     
4857     /**
4858     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4859     */
4860     /**
4861     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4862     */
4863     /**
4864     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4865     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4866     */
4867     /**
4868     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4869     * on any HTTP request
4870     */
4871     /**
4872     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4873     */
4874     /**
4875     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4876     */
4877     multiSort: false,
4878     /**
4879     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4880     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4881     */
4882     remoteSort : false,
4883
4884     /**
4885     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4886      * loaded or when a record is removed. (defaults to false).
4887     */
4888     pruneModifiedRecords : false,
4889
4890     // private
4891     lastOptions : null,
4892
4893     /**
4894      * Add Records to the Store and fires the add event.
4895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4896      */
4897     add : function(records){
4898         records = [].concat(records);
4899         for(var i = 0, len = records.length; i < len; i++){
4900             records[i].join(this);
4901         }
4902         var index = this.data.length;
4903         this.data.addAll(records);
4904         this.fireEvent("add", this, records, index);
4905     },
4906
4907     /**
4908      * Remove a Record from the Store and fires the remove event.
4909      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4910      */
4911     remove : function(record){
4912         var index = this.data.indexOf(record);
4913         this.data.removeAt(index);
4914         if(this.pruneModifiedRecords){
4915             this.modified.remove(record);
4916         }
4917         this.fireEvent("remove", this, record, index);
4918     },
4919
4920     /**
4921      * Remove all Records from the Store and fires the clear event.
4922      */
4923     removeAll : function(){
4924         this.data.clear();
4925         if(this.pruneModifiedRecords){
4926             this.modified = [];
4927         }
4928         this.fireEvent("clear", this);
4929     },
4930
4931     /**
4932      * Inserts Records to the Store at the given index and fires the add event.
4933      * @param {Number} index The start index at which to insert the passed Records.
4934      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4935      */
4936     insert : function(index, records){
4937         records = [].concat(records);
4938         for(var i = 0, len = records.length; i < len; i++){
4939             this.data.insert(index, records[i]);
4940             records[i].join(this);
4941         }
4942         this.fireEvent("add", this, records, index);
4943     },
4944
4945     /**
4946      * Get the index within the cache of the passed Record.
4947      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4948      * @return {Number} The index of the passed Record. Returns -1 if not found.
4949      */
4950     indexOf : function(record){
4951         return this.data.indexOf(record);
4952     },
4953
4954     /**
4955      * Get the index within the cache of the Record with the passed id.
4956      * @param {String} id The id of the Record to find.
4957      * @return {Number} The index of the Record. Returns -1 if not found.
4958      */
4959     indexOfId : function(id){
4960         return this.data.indexOfKey(id);
4961     },
4962
4963     /**
4964      * Get the Record with the specified id.
4965      * @param {String} id The id of the Record to find.
4966      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4967      */
4968     getById : function(id){
4969         return this.data.key(id);
4970     },
4971
4972     /**
4973      * Get the Record at the specified index.
4974      * @param {Number} index The index of the Record to find.
4975      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4976      */
4977     getAt : function(index){
4978         return this.data.itemAt(index);
4979     },
4980
4981     /**
4982      * Returns a range of Records between specified indices.
4983      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4984      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4985      * @return {Roo.data.Record[]} An array of Records
4986      */
4987     getRange : function(start, end){
4988         return this.data.getRange(start, end);
4989     },
4990
4991     // private
4992     storeOptions : function(o){
4993         o = Roo.apply({}, o);
4994         delete o.callback;
4995         delete o.scope;
4996         this.lastOptions = o;
4997     },
4998
4999     /**
5000      * Loads the Record cache from the configured Proxy using the configured Reader.
5001      * <p>
5002      * If using remote paging, then the first load call must specify the <em>start</em>
5003      * and <em>limit</em> properties in the options.params property to establish the initial
5004      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5005      * <p>
5006      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5007      * and this call will return before the new data has been loaded. Perform any post-processing
5008      * in a callback function, or in a "load" event handler.</strong>
5009      * <p>
5010      * @param {Object} options An object containing properties which control loading options:<ul>
5011      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5012      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5013      * passed the following arguments:<ul>
5014      * <li>r : Roo.data.Record[]</li>
5015      * <li>options: Options object from the load call</li>
5016      * <li>success: Boolean success indicator</li></ul></li>
5017      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5018      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5019      * </ul>
5020      */
5021     load : function(options){
5022         options = options || {};
5023         if(this.fireEvent("beforeload", this, options) !== false){
5024             this.storeOptions(options);
5025             var p = Roo.apply(options.params || {}, this.baseParams);
5026             // if meta was not loaded from remote source.. try requesting it.
5027             if (!this.reader.metaFromRemote) {
5028                 p._requestMeta = 1;
5029             }
5030             if(this.sortInfo && this.remoteSort){
5031                 var pn = this.paramNames;
5032                 p[pn["sort"]] = this.sortInfo.field;
5033                 p[pn["dir"]] = this.sortInfo.direction;
5034             }
5035             if (this.multiSort) {
5036                 var pn = this.paramNames;
5037                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5038             }
5039             
5040             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5041         }
5042     },
5043
5044     /**
5045      * Reloads the Record cache from the configured Proxy using the configured Reader and
5046      * the options from the last load operation performed.
5047      * @param {Object} options (optional) An object containing properties which may override the options
5048      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5049      * the most recently used options are reused).
5050      */
5051     reload : function(options){
5052         this.load(Roo.applyIf(options||{}, this.lastOptions));
5053     },
5054
5055     // private
5056     // Called as a callback by the Reader during a load operation.
5057     loadRecords : function(o, options, success){
5058         if(!o || success === false){
5059             if(success !== false){
5060                 this.fireEvent("load", this, [], options);
5061             }
5062             if(options.callback){
5063                 options.callback.call(options.scope || this, [], options, false);
5064             }
5065             return;
5066         }
5067         // if data returned failure - throw an exception.
5068         if (o.success === false) {
5069             // show a message if no listener is registered.
5070             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5071                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5072             }
5073             // loadmask wil be hooked into this..
5074             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5075             return;
5076         }
5077         var r = o.records, t = o.totalRecords || r.length;
5078         if(!options || options.add !== true){
5079             if(this.pruneModifiedRecords){
5080                 this.modified = [];
5081             }
5082             for(var i = 0, len = r.length; i < len; i++){
5083                 r[i].join(this);
5084             }
5085             if(this.snapshot){
5086                 this.data = this.snapshot;
5087                 delete this.snapshot;
5088             }
5089             this.data.clear();
5090             this.data.addAll(r);
5091             this.totalLength = t;
5092             this.applySort();
5093             this.fireEvent("datachanged", this);
5094         }else{
5095             this.totalLength = Math.max(t, this.data.length+r.length);
5096             this.add(r);
5097         }
5098         this.fireEvent("load", this, r, options);
5099         if(options.callback){
5100             options.callback.call(options.scope || this, r, options, true);
5101         }
5102     },
5103
5104
5105     /**
5106      * Loads data from a passed data block. A Reader which understands the format of the data
5107      * must have been configured in the constructor.
5108      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5109      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5110      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5111      */
5112     loadData : function(o, append){
5113         var r = this.reader.readRecords(o);
5114         this.loadRecords(r, {add: append}, true);
5115     },
5116
5117     /**
5118      * Gets the number of cached records.
5119      * <p>
5120      * <em>If using paging, this may not be the total size of the dataset. If the data object
5121      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5122      * the data set size</em>
5123      */
5124     getCount : function(){
5125         return this.data.length || 0;
5126     },
5127
5128     /**
5129      * Gets the total number of records in the dataset as returned by the server.
5130      * <p>
5131      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5132      * the dataset size</em>
5133      */
5134     getTotalCount : function(){
5135         return this.totalLength || 0;
5136     },
5137
5138     /**
5139      * Returns the sort state of the Store as an object with two properties:
5140      * <pre><code>
5141  field {String} The name of the field by which the Records are sorted
5142  direction {String} The sort order, "ASC" or "DESC"
5143      * </code></pre>
5144      */
5145     getSortState : function(){
5146         return this.sortInfo;
5147     },
5148
5149     // private
5150     applySort : function(){
5151         if(this.sortInfo && !this.remoteSort){
5152             var s = this.sortInfo, f = s.field;
5153             var st = this.fields.get(f).sortType;
5154             var fn = function(r1, r2){
5155                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5156                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5157             };
5158             this.data.sort(s.direction, fn);
5159             if(this.snapshot && this.snapshot != this.data){
5160                 this.snapshot.sort(s.direction, fn);
5161             }
5162         }
5163     },
5164
5165     /**
5166      * Sets the default sort column and order to be used by the next load operation.
5167      * @param {String} fieldName The name of the field to sort by.
5168      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5169      */
5170     setDefaultSort : function(field, dir){
5171         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5172     },
5173
5174     /**
5175      * Sort the Records.
5176      * If remote sorting is used, the sort is performed on the server, and the cache is
5177      * reloaded. If local sorting is used, the cache is sorted internally.
5178      * @param {String} fieldName The name of the field to sort by.
5179      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5180      */
5181     sort : function(fieldName, dir){
5182         var f = this.fields.get(fieldName);
5183         if(!dir){
5184             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5185             
5186             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5187                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5188             }else{
5189                 dir = f.sortDir;
5190             }
5191         }
5192         this.sortToggle[f.name] = dir;
5193         this.sortInfo = {field: f.name, direction: dir};
5194         if(!this.remoteSort){
5195             this.applySort();
5196             this.fireEvent("datachanged", this);
5197         }else{
5198             this.load(this.lastOptions);
5199         }
5200     },
5201
5202     /**
5203      * Calls the specified function for each of the Records in the cache.
5204      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5205      * Returning <em>false</em> aborts and exits the iteration.
5206      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5207      */
5208     each : function(fn, scope){
5209         this.data.each(fn, scope);
5210     },
5211
5212     /**
5213      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5214      * (e.g., during paging).
5215      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5216      */
5217     getModifiedRecords : function(){
5218         return this.modified;
5219     },
5220
5221     // private
5222     createFilterFn : function(property, value, anyMatch){
5223         if(!value.exec){ // not a regex
5224             value = String(value);
5225             if(value.length == 0){
5226                 return false;
5227             }
5228             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5229         }
5230         return function(r){
5231             return value.test(r.data[property]);
5232         };
5233     },
5234
5235     /**
5236      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5237      * @param {String} property A field on your records
5238      * @param {Number} start The record index to start at (defaults to 0)
5239      * @param {Number} end The last record index to include (defaults to length - 1)
5240      * @return {Number} The sum
5241      */
5242     sum : function(property, start, end){
5243         var rs = this.data.items, v = 0;
5244         start = start || 0;
5245         end = (end || end === 0) ? end : rs.length-1;
5246
5247         for(var i = start; i <= end; i++){
5248             v += (rs[i].data[property] || 0);
5249         }
5250         return v;
5251     },
5252
5253     /**
5254      * Filter the records by a specified property.
5255      * @param {String} field A field on your records
5256      * @param {String/RegExp} value Either a string that the field
5257      * should start with or a RegExp to test against the field
5258      * @param {Boolean} anyMatch True to match any part not just the beginning
5259      */
5260     filter : function(property, value, anyMatch){
5261         var fn = this.createFilterFn(property, value, anyMatch);
5262         return fn ? this.filterBy(fn) : this.clearFilter();
5263     },
5264
5265     /**
5266      * Filter by a function. The specified function will be called with each
5267      * record in this data source. If the function returns true the record is included,
5268      * otherwise it is filtered.
5269      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5270      * @param {Object} scope (optional) The scope of the function (defaults to this)
5271      */
5272     filterBy : function(fn, scope){
5273         this.snapshot = this.snapshot || this.data;
5274         this.data = this.queryBy(fn, scope||this);
5275         this.fireEvent("datachanged", this);
5276     },
5277
5278     /**
5279      * Query the records by a specified property.
5280      * @param {String} field A field on your records
5281      * @param {String/RegExp} value Either a string that the field
5282      * should start with or a RegExp to test against the field
5283      * @param {Boolean} anyMatch True to match any part not just the beginning
5284      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5285      */
5286     query : function(property, value, anyMatch){
5287         var fn = this.createFilterFn(property, value, anyMatch);
5288         return fn ? this.queryBy(fn) : this.data.clone();
5289     },
5290
5291     /**
5292      * Query by a function. The specified function will be called with each
5293      * record in this data source. If the function returns true the record is included
5294      * in the results.
5295      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5296      * @param {Object} scope (optional) The scope of the function (defaults to this)
5297       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298      **/
5299     queryBy : function(fn, scope){
5300         var data = this.snapshot || this.data;
5301         return data.filterBy(fn, scope||this);
5302     },
5303
5304     /**
5305      * Collects unique values for a particular dataIndex from this store.
5306      * @param {String} dataIndex The property to collect
5307      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5308      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5309      * @return {Array} An array of the unique values
5310      **/
5311     collect : function(dataIndex, allowNull, bypassFilter){
5312         var d = (bypassFilter === true && this.snapshot) ?
5313                 this.snapshot.items : this.data.items;
5314         var v, sv, r = [], l = {};
5315         for(var i = 0, len = d.length; i < len; i++){
5316             v = d[i].data[dataIndex];
5317             sv = String(v);
5318             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5319                 l[sv] = true;
5320                 r[r.length] = v;
5321             }
5322         }
5323         return r;
5324     },
5325
5326     /**
5327      * Revert to a view of the Record cache with no filtering applied.
5328      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5329      */
5330     clearFilter : function(suppressEvent){
5331         if(this.snapshot && this.snapshot != this.data){
5332             this.data = this.snapshot;
5333             delete this.snapshot;
5334             if(suppressEvent !== true){
5335                 this.fireEvent("datachanged", this);
5336             }
5337         }
5338     },
5339
5340     // private
5341     afterEdit : function(record){
5342         if(this.modified.indexOf(record) == -1){
5343             this.modified.push(record);
5344         }
5345         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5346     },
5347     
5348     // private
5349     afterReject : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5352     },
5353
5354     // private
5355     afterCommit : function(record){
5356         this.modified.remove(record);
5357         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5358     },
5359
5360     /**
5361      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5362      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5363      */
5364     commitChanges : function(){
5365         var m = this.modified.slice(0);
5366         this.modified = [];
5367         for(var i = 0, len = m.length; i < len; i++){
5368             m[i].commit();
5369         }
5370     },
5371
5372     /**
5373      * Cancel outstanding changes on all changed records.
5374      */
5375     rejectChanges : function(){
5376         var m = this.modified.slice(0);
5377         this.modified = [];
5378         for(var i = 0, len = m.length; i < len; i++){
5379             m[i].reject();
5380         }
5381     },
5382
5383     onMetaChange : function(meta, rtype, o){
5384         this.recordType = rtype;
5385         this.fields = rtype.prototype.fields;
5386         delete this.snapshot;
5387         this.sortInfo = meta.sortInfo || this.sortInfo;
5388         this.modified = [];
5389         this.fireEvent('metachange', this, this.reader.meta);
5390     }
5391 });/*
5392  * Based on:
5393  * Ext JS Library 1.1.1
5394  * Copyright(c) 2006-2007, Ext JS, LLC.
5395  *
5396  * Originally Released Under LGPL - original licence link has changed is not relivant.
5397  *
5398  * Fork - LGPL
5399  * <script type="text/javascript">
5400  */
5401
5402 /**
5403  * @class Roo.data.SimpleStore
5404  * @extends Roo.data.Store
5405  * Small helper class to make creating Stores from Array data easier.
5406  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5407  * @cfg {Array} fields An array of field definition objects, or field name strings.
5408  * @cfg {Array} data The multi-dimensional array of data
5409  * @constructor
5410  * @param {Object} config
5411  */
5412 Roo.data.SimpleStore = function(config){
5413     Roo.data.SimpleStore.superclass.constructor.call(this, {
5414         isLocal : true,
5415         reader: new Roo.data.ArrayReader({
5416                 id: config.id
5417             },
5418             Roo.data.Record.create(config.fields)
5419         ),
5420         proxy : new Roo.data.MemoryProxy(config.data)
5421     });
5422     this.load();
5423 };
5424 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5425  * Based on:
5426  * Ext JS Library 1.1.1
5427  * Copyright(c) 2006-2007, Ext JS, LLC.
5428  *
5429  * Originally Released Under LGPL - original licence link has changed is not relivant.
5430  *
5431  * Fork - LGPL
5432  * <script type="text/javascript">
5433  */
5434
5435 /**
5436 /**
5437  * @extends Roo.data.Store
5438  * @class Roo.data.JsonStore
5439  * Small helper class to make creating Stores for JSON data easier. <br/>
5440 <pre><code>
5441 var store = new Roo.data.JsonStore({
5442     url: 'get-images.php',
5443     root: 'images',
5444     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5445 });
5446 </code></pre>
5447  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5448  * JsonReader and HttpProxy (unless inline data is provided).</b>
5449  * @cfg {Array} fields An array of field definition objects, or field name strings.
5450  * @constructor
5451  * @param {Object} config
5452  */
5453 Roo.data.JsonStore = function(c){
5454     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5455         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5456         reader: new Roo.data.JsonReader(c, c.fields)
5457     }));
5458 };
5459 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5460  * Based on:
5461  * Ext JS Library 1.1.1
5462  * Copyright(c) 2006-2007, Ext JS, LLC.
5463  *
5464  * Originally Released Under LGPL - original licence link has changed is not relivant.
5465  *
5466  * Fork - LGPL
5467  * <script type="text/javascript">
5468  */
5469
5470  
5471 Roo.data.Field = function(config){
5472     if(typeof config == "string"){
5473         config = {name: config};
5474     }
5475     Roo.apply(this, config);
5476     
5477     if(!this.type){
5478         this.type = "auto";
5479     }
5480     
5481     var st = Roo.data.SortTypes;
5482     // named sortTypes are supported, here we look them up
5483     if(typeof this.sortType == "string"){
5484         this.sortType = st[this.sortType];
5485     }
5486     
5487     // set default sortType for strings and dates
5488     if(!this.sortType){
5489         switch(this.type){
5490             case "string":
5491                 this.sortType = st.asUCString;
5492                 break;
5493             case "date":
5494                 this.sortType = st.asDate;
5495                 break;
5496             default:
5497                 this.sortType = st.none;
5498         }
5499     }
5500
5501     // define once
5502     var stripRe = /[\$,%]/g;
5503
5504     // prebuilt conversion function for this field, instead of
5505     // switching every time we're reading a value
5506     if(!this.convert){
5507         var cv, dateFormat = this.dateFormat;
5508         switch(this.type){
5509             case "":
5510             case "auto":
5511             case undefined:
5512                 cv = function(v){ return v; };
5513                 break;
5514             case "string":
5515                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5516                 break;
5517             case "int":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5521                     };
5522                 break;
5523             case "float":
5524                 cv = function(v){
5525                     return v !== undefined && v !== null && v !== '' ?
5526                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5527                     };
5528                 break;
5529             case "bool":
5530             case "boolean":
5531                 cv = function(v){ return v === true || v === "true" || v == 1; };
5532                 break;
5533             case "date":
5534                 cv = function(v){
5535                     if(!v){
5536                         return '';
5537                     }
5538                     if(v instanceof Date){
5539                         return v;
5540                     }
5541                     if(dateFormat){
5542                         if(dateFormat == "timestamp"){
5543                             return new Date(v*1000);
5544                         }
5545                         return Date.parseDate(v, dateFormat);
5546                     }
5547                     var parsed = Date.parse(v);
5548                     return parsed ? new Date(parsed) : null;
5549                 };
5550              break;
5551             
5552         }
5553         this.convert = cv;
5554     }
5555 };
5556
5557 Roo.data.Field.prototype = {
5558     dateFormat: null,
5559     defaultValue: "",
5560     mapping: null,
5561     sortType : null,
5562     sortDir : "ASC"
5563 };/*
5564  * Based on:
5565  * Ext JS Library 1.1.1
5566  * Copyright(c) 2006-2007, Ext JS, LLC.
5567  *
5568  * Originally Released Under LGPL - original licence link has changed is not relivant.
5569  *
5570  * Fork - LGPL
5571  * <script type="text/javascript">
5572  */
5573  
5574 // Base class for reading structured data from a data source.  This class is intended to be
5575 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5576
5577 /**
5578  * @class Roo.data.DataReader
5579  * Base class for reading structured data from a data source.  This class is intended to be
5580  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5581  */
5582
5583 Roo.data.DataReader = function(meta, recordType){
5584     
5585     this.meta = meta;
5586     
5587     this.recordType = recordType instanceof Array ? 
5588         Roo.data.Record.create(recordType) : recordType;
5589 };
5590
5591 Roo.data.DataReader.prototype = {
5592      /**
5593      * Create an empty record
5594      * @param {Object} data (optional) - overlay some values
5595      * @return {Roo.data.Record} record created.
5596      */
5597     newRow :  function(d) {
5598         var da =  {};
5599         this.recordType.prototype.fields.each(function(c) {
5600             switch( c.type) {
5601                 case 'int' : da[c.name] = 0; break;
5602                 case 'date' : da[c.name] = new Date(); break;
5603                 case 'float' : da[c.name] = 0.0; break;
5604                 case 'boolean' : da[c.name] = false; break;
5605                 default : da[c.name] = ""; break;
5606             }
5607             
5608         });
5609         return new this.recordType(Roo.apply(da, d));
5610     }
5611     
5612 };/*
5613  * Based on:
5614  * Ext JS Library 1.1.1
5615  * Copyright(c) 2006-2007, Ext JS, LLC.
5616  *
5617  * Originally Released Under LGPL - original licence link has changed is not relivant.
5618  *
5619  * Fork - LGPL
5620  * <script type="text/javascript">
5621  */
5622
5623 /**
5624  * @class Roo.data.DataProxy
5625  * @extends Roo.data.Observable
5626  * This class is an abstract base class for implementations which provide retrieval of
5627  * unformatted data objects.<br>
5628  * <p>
5629  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5630  * (of the appropriate type which knows how to parse the data object) to provide a block of
5631  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5632  * <p>
5633  * Custom implementations must implement the load method as described in
5634  * {@link Roo.data.HttpProxy#load}.
5635  */
5636 Roo.data.DataProxy = function(){
5637     this.addEvents({
5638         /**
5639          * @event beforeload
5640          * Fires before a network request is made to retrieve a data object.
5641          * @param {Object} This DataProxy object.
5642          * @param {Object} params The params parameter to the load function.
5643          */
5644         beforeload : true,
5645         /**
5646          * @event load
5647          * Fires before the load method's callback is called.
5648          * @param {Object} This DataProxy object.
5649          * @param {Object} o The data object.
5650          * @param {Object} arg The callback argument object passed to the load function.
5651          */
5652         load : true,
5653         /**
5654          * @event loadexception
5655          * Fires if an Exception occurs during data retrieval.
5656          * @param {Object} This DataProxy object.
5657          * @param {Object} o The data object.
5658          * @param {Object} arg The callback argument object passed to the load function.
5659          * @param {Object} e The Exception.
5660          */
5661         loadexception : true
5662     });
5663     Roo.data.DataProxy.superclass.constructor.call(this);
5664 };
5665
5666 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5667
5668     /**
5669      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5670      */
5671 /*
5672  * Based on:
5673  * Ext JS Library 1.1.1
5674  * Copyright(c) 2006-2007, Ext JS, LLC.
5675  *
5676  * Originally Released Under LGPL - original licence link has changed is not relivant.
5677  *
5678  * Fork - LGPL
5679  * <script type="text/javascript">
5680  */
5681 /**
5682  * @class Roo.data.MemoryProxy
5683  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5684  * to the Reader when its load method is called.
5685  * @constructor
5686  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5687  */
5688 Roo.data.MemoryProxy = function(data){
5689     if (data.data) {
5690         data = data.data;
5691     }
5692     Roo.data.MemoryProxy.superclass.constructor.call(this);
5693     this.data = data;
5694 };
5695
5696 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5697     /**
5698      * Load data from the requested source (in this case an in-memory
5699      * data object passed to the constructor), read the data object into
5700      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5701      * process that block using the passed callback.
5702      * @param {Object} params This parameter is not used by the MemoryProxy class.
5703      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5704      * object into a block of Roo.data.Records.
5705      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5706      * The function must be passed <ul>
5707      * <li>The Record block object</li>
5708      * <li>The "arg" argument from the load function</li>
5709      * <li>A boolean success indicator</li>
5710      * </ul>
5711      * @param {Object} scope The scope in which to call the callback
5712      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5713      */
5714     load : function(params, reader, callback, scope, arg){
5715         params = params || {};
5716         var result;
5717         try {
5718             result = reader.readRecords(this.data);
5719         }catch(e){
5720             this.fireEvent("loadexception", this, arg, null, e);
5721             callback.call(scope, null, arg, false);
5722             return;
5723         }
5724         callback.call(scope, result, arg, true);
5725     },
5726     
5727     // private
5728     update : function(params, records){
5729         
5730     }
5731 });/*
5732  * Based on:
5733  * Ext JS Library 1.1.1
5734  * Copyright(c) 2006-2007, Ext JS, LLC.
5735  *
5736  * Originally Released Under LGPL - original licence link has changed is not relivant.
5737  *
5738  * Fork - LGPL
5739  * <script type="text/javascript">
5740  */
5741 /**
5742  * @class Roo.data.HttpProxy
5743  * @extends Roo.data.DataProxy
5744  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5745  * configured to reference a certain URL.<br><br>
5746  * <p>
5747  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5748  * from which the running page was served.<br><br>
5749  * <p>
5750  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5751  * <p>
5752  * Be aware that to enable the browser to parse an XML document, the server must set
5753  * the Content-Type header in the HTTP response to "text/xml".
5754  * @constructor
5755  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5756  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5757  * will be used to make the request.
5758  */
5759 Roo.data.HttpProxy = function(conn){
5760     Roo.data.HttpProxy.superclass.constructor.call(this);
5761     // is conn a conn config or a real conn?
5762     this.conn = conn;
5763     this.useAjax = !conn || !conn.events;
5764   
5765 };
5766
5767 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5768     // thse are take from connection...
5769     
5770     /**
5771      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5772      */
5773     /**
5774      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5775      * extra parameters to each request made by this object. (defaults to undefined)
5776      */
5777     /**
5778      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5779      *  to each request made by this object. (defaults to undefined)
5780      */
5781     /**
5782      * @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)
5783      */
5784     /**
5785      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5786      */
5787      /**
5788      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5789      * @type Boolean
5790      */
5791   
5792
5793     /**
5794      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5795      * @type Boolean
5796      */
5797     /**
5798      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5799      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5800      * a finer-grained basis than the DataProxy events.
5801      */
5802     getConnection : function(){
5803         return this.useAjax ? Roo.Ajax : this.conn;
5804     },
5805
5806     /**
5807      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5808      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5809      * process that block using the passed callback.
5810      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5811      * for the request to the remote server.
5812      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5813      * object into a block of Roo.data.Records.
5814      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5815      * The function must be passed <ul>
5816      * <li>The Record block object</li>
5817      * <li>The "arg" argument from the load function</li>
5818      * <li>A boolean success indicator</li>
5819      * </ul>
5820      * @param {Object} scope The scope in which to call the callback
5821      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5822      */
5823     load : function(params, reader, callback, scope, arg){
5824         if(this.fireEvent("beforeload", this, params) !== false){
5825             var  o = {
5826                 params : params || {},
5827                 request: {
5828                     callback : callback,
5829                     scope : scope,
5830                     arg : arg
5831                 },
5832                 reader: reader,
5833                 callback : this.loadResponse,
5834                 scope: this
5835             };
5836             if(this.useAjax){
5837                 Roo.applyIf(o, this.conn);
5838                 if(this.activeRequest){
5839                     Roo.Ajax.abort(this.activeRequest);
5840                 }
5841                 this.activeRequest = Roo.Ajax.request(o);
5842             }else{
5843                 this.conn.request(o);
5844             }
5845         }else{
5846             callback.call(scope||this, null, arg, false);
5847         }
5848     },
5849
5850     // private
5851     loadResponse : function(o, success, response){
5852         delete this.activeRequest;
5853         if(!success){
5854             this.fireEvent("loadexception", this, o, response);
5855             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5856             return;
5857         }
5858         var result;
5859         try {
5860             result = o.reader.read(response);
5861         }catch(e){
5862             this.fireEvent("loadexception", this, o, response, e);
5863             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5864             return;
5865         }
5866         
5867         this.fireEvent("load", this, o, o.request.arg);
5868         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5869     },
5870
5871     // private
5872     update : function(dataSet){
5873
5874     },
5875
5876     // private
5877     updateResponse : function(dataSet){
5878
5879     }
5880 });/*
5881  * Based on:
5882  * Ext JS Library 1.1.1
5883  * Copyright(c) 2006-2007, Ext JS, LLC.
5884  *
5885  * Originally Released Under LGPL - original licence link has changed is not relivant.
5886  *
5887  * Fork - LGPL
5888  * <script type="text/javascript">
5889  */
5890
5891 /**
5892  * @class Roo.data.ScriptTagProxy
5893  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5894  * other than the originating domain of the running page.<br><br>
5895  * <p>
5896  * <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
5897  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5898  * <p>
5899  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5900  * source code that is used as the source inside a &lt;script> tag.<br><br>
5901  * <p>
5902  * In order for the browser to process the returned data, the server must wrap the data object
5903  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5904  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5905  * depending on whether the callback name was passed:
5906  * <p>
5907  * <pre><code>
5908 boolean scriptTag = false;
5909 String cb = request.getParameter("callback");
5910 if (cb != null) {
5911     scriptTag = true;
5912     response.setContentType("text/javascript");
5913 } else {
5914     response.setContentType("application/x-json");
5915 }
5916 Writer out = response.getWriter();
5917 if (scriptTag) {
5918     out.write(cb + "(");
5919 }
5920 out.print(dataBlock.toJsonString());
5921 if (scriptTag) {
5922     out.write(");");
5923 }
5924 </pre></code>
5925  *
5926  * @constructor
5927  * @param {Object} config A configuration object.
5928  */
5929 Roo.data.ScriptTagProxy = function(config){
5930     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5931     Roo.apply(this, config);
5932     this.head = document.getElementsByTagName("head")[0];
5933 };
5934
5935 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5936
5937 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5938     /**
5939      * @cfg {String} url The URL from which to request the data object.
5940      */
5941     /**
5942      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5943      */
5944     timeout : 30000,
5945     /**
5946      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5947      * the server the name of the callback function set up by the load call to process the returned data object.
5948      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5949      * javascript output which calls this named function passing the data object as its only parameter.
5950      */
5951     callbackParam : "callback",
5952     /**
5953      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5954      * name to the request.
5955      */
5956     nocache : true,
5957
5958     /**
5959      * Load data from the configured URL, read the data object into
5960      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5961      * process that block using the passed callback.
5962      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5963      * for the request to the remote server.
5964      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5965      * object into a block of Roo.data.Records.
5966      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5967      * The function must be passed <ul>
5968      * <li>The Record block object</li>
5969      * <li>The "arg" argument from the load function</li>
5970      * <li>A boolean success indicator</li>
5971      * </ul>
5972      * @param {Object} scope The scope in which to call the callback
5973      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5974      */
5975     load : function(params, reader, callback, scope, arg){
5976         if(this.fireEvent("beforeload", this, params) !== false){
5977
5978             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5979
5980             var url = this.url;
5981             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5982             if(this.nocache){
5983                 url += "&_dc=" + (new Date().getTime());
5984             }
5985             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5986             var trans = {
5987                 id : transId,
5988                 cb : "stcCallback"+transId,
5989                 scriptId : "stcScript"+transId,
5990                 params : params,
5991                 arg : arg,
5992                 url : url,
5993                 callback : callback,
5994                 scope : scope,
5995                 reader : reader
5996             };
5997             var conn = this;
5998
5999             window[trans.cb] = function(o){
6000                 conn.handleResponse(o, trans);
6001             };
6002
6003             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6004
6005             if(this.autoAbort !== false){
6006                 this.abort();
6007             }
6008
6009             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6010
6011             var script = document.createElement("script");
6012             script.setAttribute("src", url);
6013             script.setAttribute("type", "text/javascript");
6014             script.setAttribute("id", trans.scriptId);
6015             this.head.appendChild(script);
6016
6017             this.trans = trans;
6018         }else{
6019             callback.call(scope||this, null, arg, false);
6020         }
6021     },
6022
6023     // private
6024     isLoading : function(){
6025         return this.trans ? true : false;
6026     },
6027
6028     /**
6029      * Abort the current server request.
6030      */
6031     abort : function(){
6032         if(this.isLoading()){
6033             this.destroyTrans(this.trans);
6034         }
6035     },
6036
6037     // private
6038     destroyTrans : function(trans, isLoaded){
6039         this.head.removeChild(document.getElementById(trans.scriptId));
6040         clearTimeout(trans.timeoutId);
6041         if(isLoaded){
6042             window[trans.cb] = undefined;
6043             try{
6044                 delete window[trans.cb];
6045             }catch(e){}
6046         }else{
6047             // if hasn't been loaded, wait for load to remove it to prevent script error
6048             window[trans.cb] = function(){
6049                 window[trans.cb] = undefined;
6050                 try{
6051                     delete window[trans.cb];
6052                 }catch(e){}
6053             };
6054         }
6055     },
6056
6057     // private
6058     handleResponse : function(o, trans){
6059         this.trans = false;
6060         this.destroyTrans(trans, true);
6061         var result;
6062         try {
6063             result = trans.reader.readRecords(o);
6064         }catch(e){
6065             this.fireEvent("loadexception", this, o, trans.arg, e);
6066             trans.callback.call(trans.scope||window, null, trans.arg, false);
6067             return;
6068         }
6069         this.fireEvent("load", this, o, trans.arg);
6070         trans.callback.call(trans.scope||window, result, trans.arg, true);
6071     },
6072
6073     // private
6074     handleFailure : function(trans){
6075         this.trans = false;
6076         this.destroyTrans(trans, false);
6077         this.fireEvent("loadexception", this, null, trans.arg);
6078         trans.callback.call(trans.scope||window, null, trans.arg, false);
6079     }
6080 });/*
6081  * Based on:
6082  * Ext JS Library 1.1.1
6083  * Copyright(c) 2006-2007, Ext JS, LLC.
6084  *
6085  * Originally Released Under LGPL - original licence link has changed is not relivant.
6086  *
6087  * Fork - LGPL
6088  * <script type="text/javascript">
6089  */
6090
6091 /**
6092  * @class Roo.data.JsonReader
6093  * @extends Roo.data.DataReader
6094  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6095  * based on mappings in a provided Roo.data.Record constructor.
6096  * 
6097  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6098  * in the reply previously. 
6099  * 
6100  * <p>
6101  * Example code:
6102  * <pre><code>
6103 var RecordDef = Roo.data.Record.create([
6104     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6105     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6106 ]);
6107 var myReader = new Roo.data.JsonReader({
6108     totalProperty: "results",    // The property which contains the total dataset size (optional)
6109     root: "rows",                // The property which contains an Array of row objects
6110     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6111 }, RecordDef);
6112 </code></pre>
6113  * <p>
6114  * This would consume a JSON file like this:
6115  * <pre><code>
6116 { 'results': 2, 'rows': [
6117     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6118     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6119 }
6120 </code></pre>
6121  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6122  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6123  * paged from the remote server.
6124  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6125  * @cfg {String} root name of the property which contains the Array of row objects.
6126  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6127  * @constructor
6128  * Create a new JsonReader
6129  * @param {Object} meta Metadata configuration options
6130  * @param {Object} recordType Either an Array of field definition objects,
6131  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6132  */
6133 Roo.data.JsonReader = function(meta, recordType){
6134     
6135     meta = meta || {};
6136     // set some defaults:
6137     Roo.applyIf(meta, {
6138         totalProperty: 'total',
6139         successProperty : 'success',
6140         root : 'data',
6141         id : 'id'
6142     });
6143     
6144     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6145 };
6146 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6147     
6148     /**
6149      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6150      * Used by Store query builder to append _requestMeta to params.
6151      * 
6152      */
6153     metaFromRemote : false,
6154     /**
6155      * This method is only used by a DataProxy which has retrieved data from a remote server.
6156      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6157      * @return {Object} data A data block which is used by an Roo.data.Store object as
6158      * a cache of Roo.data.Records.
6159      */
6160     read : function(response){
6161         var json = response.responseText;
6162        
6163         var o = /* eval:var:o */ eval("("+json+")");
6164         if(!o) {
6165             throw {message: "JsonReader.read: Json object not found"};
6166         }
6167         
6168         if(o.metaData){
6169             
6170             delete this.ef;
6171             this.metaFromRemote = true;
6172             this.meta = o.metaData;
6173             this.recordType = Roo.data.Record.create(o.metaData.fields);
6174             this.onMetaChange(this.meta, this.recordType, o);
6175         }
6176         return this.readRecords(o);
6177     },
6178
6179     // private function a store will implement
6180     onMetaChange : function(meta, recordType, o){
6181
6182     },
6183
6184     /**
6185          * @ignore
6186          */
6187     simpleAccess: function(obj, subsc) {
6188         return obj[subsc];
6189     },
6190
6191         /**
6192          * @ignore
6193          */
6194     getJsonAccessor: function(){
6195         var re = /[\[\.]/;
6196         return function(expr) {
6197             try {
6198                 return(re.test(expr))
6199                     ? new Function("obj", "return obj." + expr)
6200                     : function(obj){
6201                         return obj[expr];
6202                     };
6203             } catch(e){}
6204             return Roo.emptyFn;
6205         };
6206     }(),
6207
6208     /**
6209      * Create a data block containing Roo.data.Records from an XML document.
6210      * @param {Object} o An object which contains an Array of row objects in the property specified
6211      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6212      * which contains the total size of the dataset.
6213      * @return {Object} data A data block which is used by an Roo.data.Store object as
6214      * a cache of Roo.data.Records.
6215      */
6216     readRecords : function(o){
6217         /**
6218          * After any data loads, the raw JSON data is available for further custom processing.
6219          * @type Object
6220          */
6221         this.jsonData = o;
6222         var s = this.meta, Record = this.recordType,
6223             f = Record.prototype.fields, fi = f.items, fl = f.length;
6224
6225 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6226         if (!this.ef) {
6227             if(s.totalProperty) {
6228                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6229                 }
6230                 if(s.successProperty) {
6231                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6232                 }
6233                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6234                 if (s.id) {
6235                         var g = this.getJsonAccessor(s.id);
6236                         this.getId = function(rec) {
6237                                 var r = g(rec);
6238                                 return (r === undefined || r === "") ? null : r;
6239                         };
6240                 } else {
6241                         this.getId = function(){return null;};
6242                 }
6243             this.ef = [];
6244             for(var jj = 0; jj < fl; jj++){
6245                 f = fi[jj];
6246                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6247                 this.ef[jj] = this.getJsonAccessor(map);
6248             }
6249         }
6250
6251         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6252         if(s.totalProperty){
6253             var vt = parseInt(this.getTotal(o), 10);
6254             if(!isNaN(vt)){
6255                 totalRecords = vt;
6256             }
6257         }
6258         if(s.successProperty){
6259             var vs = this.getSuccess(o);
6260             if(vs === false || vs === 'false'){
6261                 success = false;
6262             }
6263         }
6264         var records = [];
6265             for(var i = 0; i < c; i++){
6266                     var n = root[i];
6267                 var values = {};
6268                 var id = this.getId(n);
6269                 for(var j = 0; j < fl; j++){
6270                     f = fi[j];
6271                 var v = this.ef[j](n);
6272                 if (!f.convert) {
6273                     Roo.log('missing convert for ' + f.name);
6274                     Roo.log(f);
6275                     continue;
6276                 }
6277                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6278                 }
6279                 var record = new Record(values, id);
6280                 record.json = n;
6281                 records[i] = record;
6282             }
6283             return {
6284                 success : success,
6285                 records : records,
6286                 totalRecords : totalRecords
6287             };
6288     }
6289 });/*
6290  * Based on:
6291  * Ext JS Library 1.1.1
6292  * Copyright(c) 2006-2007, Ext JS, LLC.
6293  *
6294  * Originally Released Under LGPL - original licence link has changed is not relivant.
6295  *
6296  * Fork - LGPL
6297  * <script type="text/javascript">
6298  */
6299
6300 /**
6301  * @class Roo.data.XmlReader
6302  * @extends Roo.data.DataReader
6303  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6304  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6305  * <p>
6306  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6307  * header in the HTTP response must be set to "text/xml".</em>
6308  * <p>
6309  * Example code:
6310  * <pre><code>
6311 var RecordDef = Roo.data.Record.create([
6312    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6313    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6314 ]);
6315 var myReader = new Roo.data.XmlReader({
6316    totalRecords: "results", // The element which contains the total dataset size (optional)
6317    record: "row",           // The repeated element which contains row information
6318    id: "id"                 // The element within the row that provides an ID for the record (optional)
6319 }, RecordDef);
6320 </code></pre>
6321  * <p>
6322  * This would consume an XML file like this:
6323  * <pre><code>
6324 &lt;?xml?>
6325 &lt;dataset>
6326  &lt;results>2&lt;/results>
6327  &lt;row>
6328    &lt;id>1&lt;/id>
6329    &lt;name>Bill&lt;/name>
6330    &lt;occupation>Gardener&lt;/occupation>
6331  &lt;/row>
6332  &lt;row>
6333    &lt;id>2&lt;/id>
6334    &lt;name>Ben&lt;/name>
6335    &lt;occupation>Horticulturalist&lt;/occupation>
6336  &lt;/row>
6337 &lt;/dataset>
6338 </code></pre>
6339  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6340  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6341  * paged from the remote server.
6342  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6343  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6344  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6345  * a record identifier value.
6346  * @constructor
6347  * Create a new XmlReader
6348  * @param {Object} meta Metadata configuration options
6349  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6350  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6351  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6352  */
6353 Roo.data.XmlReader = function(meta, recordType){
6354     meta = meta || {};
6355     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6356 };
6357 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6358     /**
6359      * This method is only used by a DataProxy which has retrieved data from a remote server.
6360          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6361          * to contain a method called 'responseXML' that returns an XML document object.
6362      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6363      * a cache of Roo.data.Records.
6364      */
6365     read : function(response){
6366         var doc = response.responseXML;
6367         if(!doc) {
6368             throw {message: "XmlReader.read: XML Document not available"};
6369         }
6370         return this.readRecords(doc);
6371     },
6372
6373     /**
6374      * Create a data block containing Roo.data.Records from an XML document.
6375          * @param {Object} doc A parsed XML document.
6376      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6377      * a cache of Roo.data.Records.
6378      */
6379     readRecords : function(doc){
6380         /**
6381          * After any data loads/reads, the raw XML Document is available for further custom processing.
6382          * @type XMLDocument
6383          */
6384         this.xmlData = doc;
6385         var root = doc.documentElement || doc;
6386         var q = Roo.DomQuery;
6387         var recordType = this.recordType, fields = recordType.prototype.fields;
6388         var sid = this.meta.id;
6389         var totalRecords = 0, success = true;
6390         if(this.meta.totalRecords){
6391             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6392         }
6393         
6394         if(this.meta.success){
6395             var sv = q.selectValue(this.meta.success, root, true);
6396             success = sv !== false && sv !== 'false';
6397         }
6398         var records = [];
6399         var ns = q.select(this.meta.record, root);
6400         for(var i = 0, len = ns.length; i < len; i++) {
6401                 var n = ns[i];
6402                 var values = {};
6403                 var id = sid ? q.selectValue(sid, n) : undefined;
6404                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6405                     var f = fields.items[j];
6406                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6407                     v = f.convert(v);
6408                     values[f.name] = v;
6409                 }
6410                 var record = new recordType(values, id);
6411                 record.node = n;
6412                 records[records.length] = record;
6413             }
6414
6415             return {
6416                 success : success,
6417                 records : records,
6418                 totalRecords : totalRecords || records.length
6419             };
6420     }
6421 });/*
6422  * Based on:
6423  * Ext JS Library 1.1.1
6424  * Copyright(c) 2006-2007, Ext JS, LLC.
6425  *
6426  * Originally Released Under LGPL - original licence link has changed is not relivant.
6427  *
6428  * Fork - LGPL
6429  * <script type="text/javascript">
6430  */
6431
6432 /**
6433  * @class Roo.data.ArrayReader
6434  * @extends Roo.data.DataReader
6435  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6436  * Each element of that Array represents a row of data fields. The
6437  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6438  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6439  * <p>
6440  * Example code:.
6441  * <pre><code>
6442 var RecordDef = Roo.data.Record.create([
6443     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6444     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6445 ]);
6446 var myReader = new Roo.data.ArrayReader({
6447     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6448 }, RecordDef);
6449 </code></pre>
6450  * <p>
6451  * This would consume an Array like this:
6452  * <pre><code>
6453 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6454   </code></pre>
6455  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6456  * @constructor
6457  * Create a new JsonReader
6458  * @param {Object} meta Metadata configuration options.
6459  * @param {Object} recordType Either an Array of field definition objects
6460  * as specified to {@link Roo.data.Record#create},
6461  * or an {@link Roo.data.Record} object
6462  * created using {@link Roo.data.Record#create}.
6463  */
6464 Roo.data.ArrayReader = function(meta, recordType){
6465     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6466 };
6467
6468 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6469     /**
6470      * Create a data block containing Roo.data.Records from an XML document.
6471      * @param {Object} o An Array of row objects which represents the dataset.
6472      * @return {Object} data A data block which is used by an Roo.data.Store object as
6473      * a cache of Roo.data.Records.
6474      */
6475     readRecords : function(o){
6476         var sid = this.meta ? this.meta.id : null;
6477         var recordType = this.recordType, fields = recordType.prototype.fields;
6478         var records = [];
6479         var root = o;
6480             for(var i = 0; i < root.length; i++){
6481                     var n = root[i];
6482                 var values = {};
6483                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6484                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6485                 var f = fields.items[j];
6486                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6487                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6488                 v = f.convert(v);
6489                 values[f.name] = v;
6490             }
6491                 var record = new recordType(values, id);
6492                 record.json = n;
6493                 records[records.length] = record;
6494             }
6495             return {
6496                 records : records,
6497                 totalRecords : records.length
6498             };
6499     }
6500 });/*
6501  * Based on:
6502  * Ext JS Library 1.1.1
6503  * Copyright(c) 2006-2007, Ext JS, LLC.
6504  *
6505  * Originally Released Under LGPL - original licence link has changed is not relivant.
6506  *
6507  * Fork - LGPL
6508  * <script type="text/javascript">
6509  */
6510
6511
6512 /**
6513  * @class Roo.data.Tree
6514  * @extends Roo.util.Observable
6515  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6516  * in the tree have most standard DOM functionality.
6517  * @constructor
6518  * @param {Node} root (optional) The root node
6519  */
6520 Roo.data.Tree = function(root){
6521    this.nodeHash = {};
6522    /**
6523     * The root node for this tree
6524     * @type Node
6525     */
6526    this.root = null;
6527    if(root){
6528        this.setRootNode(root);
6529    }
6530    this.addEvents({
6531        /**
6532         * @event append
6533         * Fires when a new child node is appended to a node in this tree.
6534         * @param {Tree} tree The owner tree
6535         * @param {Node} parent The parent node
6536         * @param {Node} node The newly appended node
6537         * @param {Number} index The index of the newly appended node
6538         */
6539        "append" : true,
6540        /**
6541         * @event remove
6542         * Fires when a child node is removed from a node in this tree.
6543         * @param {Tree} tree The owner tree
6544         * @param {Node} parent The parent node
6545         * @param {Node} node The child node removed
6546         */
6547        "remove" : true,
6548        /**
6549         * @event move
6550         * Fires when a node is moved to a new location in the tree
6551         * @param {Tree} tree The owner tree
6552         * @param {Node} node The node moved
6553         * @param {Node} oldParent The old parent of this node
6554         * @param {Node} newParent The new parent of this node
6555         * @param {Number} index The index it was moved to
6556         */
6557        "move" : true,
6558        /**
6559         * @event insert
6560         * Fires when a new child node is inserted in a node in this tree.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} parent The parent node
6563         * @param {Node} node The child node inserted
6564         * @param {Node} refNode The child node the node was inserted before
6565         */
6566        "insert" : true,
6567        /**
6568         * @event beforeappend
6569         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6570         * @param {Tree} tree The owner tree
6571         * @param {Node} parent The parent node
6572         * @param {Node} node The child node to be appended
6573         */
6574        "beforeappend" : true,
6575        /**
6576         * @event beforeremove
6577         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6578         * @param {Tree} tree The owner tree
6579         * @param {Node} parent The parent node
6580         * @param {Node} node The child node to be removed
6581         */
6582        "beforeremove" : true,
6583        /**
6584         * @event beforemove
6585         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6586         * @param {Tree} tree The owner tree
6587         * @param {Node} node The node being moved
6588         * @param {Node} oldParent The parent of the node
6589         * @param {Node} newParent The new parent the node is moving to
6590         * @param {Number} index The index it is being moved to
6591         */
6592        "beforemove" : true,
6593        /**
6594         * @event beforeinsert
6595         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6596         * @param {Tree} tree The owner tree
6597         * @param {Node} parent The parent node
6598         * @param {Node} node The child node to be inserted
6599         * @param {Node} refNode The child node the node is being inserted before
6600         */
6601        "beforeinsert" : true
6602    });
6603
6604     Roo.data.Tree.superclass.constructor.call(this);
6605 };
6606
6607 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6608     pathSeparator: "/",
6609
6610     proxyNodeEvent : function(){
6611         return this.fireEvent.apply(this, arguments);
6612     },
6613
6614     /**
6615      * Returns the root node for this tree.
6616      * @return {Node}
6617      */
6618     getRootNode : function(){
6619         return this.root;
6620     },
6621
6622     /**
6623      * Sets the root node for this tree.
6624      * @param {Node} node
6625      * @return {Node}
6626      */
6627     setRootNode : function(node){
6628         this.root = node;
6629         node.ownerTree = this;
6630         node.isRoot = true;
6631         this.registerNode(node);
6632         return node;
6633     },
6634
6635     /**
6636      * Gets a node in this tree by its id.
6637      * @param {String} id
6638      * @return {Node}
6639      */
6640     getNodeById : function(id){
6641         return this.nodeHash[id];
6642     },
6643
6644     registerNode : function(node){
6645         this.nodeHash[node.id] = node;
6646     },
6647
6648     unregisterNode : function(node){
6649         delete this.nodeHash[node.id];
6650     },
6651
6652     toString : function(){
6653         return "[Tree"+(this.id?" "+this.id:"")+"]";
6654     }
6655 });
6656
6657 /**
6658  * @class Roo.data.Node
6659  * @extends Roo.util.Observable
6660  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6661  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6662  * @constructor
6663  * @param {Object} attributes The attributes/config for the node
6664  */
6665 Roo.data.Node = function(attributes){
6666     /**
6667      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6668      * @type {Object}
6669      */
6670     this.attributes = attributes || {};
6671     this.leaf = this.attributes.leaf;
6672     /**
6673      * The node id. @type String
6674      */
6675     this.id = this.attributes.id;
6676     if(!this.id){
6677         this.id = Roo.id(null, "ynode-");
6678         this.attributes.id = this.id;
6679     }
6680      
6681     
6682     /**
6683      * All child nodes of this node. @type Array
6684      */
6685     this.childNodes = [];
6686     if(!this.childNodes.indexOf){ // indexOf is a must
6687         this.childNodes.indexOf = function(o){
6688             for(var i = 0, len = this.length; i < len; i++){
6689                 if(this[i] == o) {
6690                     return i;
6691                 }
6692             }
6693             return -1;
6694         };
6695     }
6696     /**
6697      * The parent node for this node. @type Node
6698      */
6699     this.parentNode = null;
6700     /**
6701      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6702      */
6703     this.firstChild = null;
6704     /**
6705      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6706      */
6707     this.lastChild = null;
6708     /**
6709      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6710      */
6711     this.previousSibling = null;
6712     /**
6713      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6714      */
6715     this.nextSibling = null;
6716
6717     this.addEvents({
6718        /**
6719         * @event append
6720         * Fires when a new child node is appended
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The newly appended node
6724         * @param {Number} index The index of the newly appended node
6725         */
6726        "append" : true,
6727        /**
6728         * @event remove
6729         * Fires when a child node is removed
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} node The removed node
6733         */
6734        "remove" : true,
6735        /**
6736         * @event move
6737         * Fires when this node is moved to a new location in the tree
6738         * @param {Tree} tree The owner tree
6739         * @param {Node} this This node
6740         * @param {Node} oldParent The old parent of this node
6741         * @param {Node} newParent The new parent of this node
6742         * @param {Number} index The index it was moved to
6743         */
6744        "move" : true,
6745        /**
6746         * @event insert
6747         * Fires when a new child node is inserted.
6748         * @param {Tree} tree The owner tree
6749         * @param {Node} this This node
6750         * @param {Node} node The child node inserted
6751         * @param {Node} refNode The child node the node was inserted before
6752         */
6753        "insert" : true,
6754        /**
6755         * @event beforeappend
6756         * Fires before a new child is appended, return false to cancel the append.
6757         * @param {Tree} tree The owner tree
6758         * @param {Node} this This node
6759         * @param {Node} node The child node to be appended
6760         */
6761        "beforeappend" : true,
6762        /**
6763         * @event beforeremove
6764         * Fires before a child is removed, return false to cancel the remove.
6765         * @param {Tree} tree The owner tree
6766         * @param {Node} this This node
6767         * @param {Node} node The child node to be removed
6768         */
6769        "beforeremove" : true,
6770        /**
6771         * @event beforemove
6772         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6773         * @param {Tree} tree The owner tree
6774         * @param {Node} this This node
6775         * @param {Node} oldParent The parent of this node
6776         * @param {Node} newParent The new parent this node is moving to
6777         * @param {Number} index The index it is being moved to
6778         */
6779        "beforemove" : true,
6780        /**
6781         * @event beforeinsert
6782         * Fires before a new child is inserted, return false to cancel the insert.
6783         * @param {Tree} tree The owner tree
6784         * @param {Node} this This node
6785         * @param {Node} node The child node to be inserted
6786         * @param {Node} refNode The child node the node is being inserted before
6787         */
6788        "beforeinsert" : true
6789    });
6790     this.listeners = this.attributes.listeners;
6791     Roo.data.Node.superclass.constructor.call(this);
6792 };
6793
6794 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6795     fireEvent : function(evtName){
6796         // first do standard event for this node
6797         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6798             return false;
6799         }
6800         // then bubble it up to the tree if the event wasn't cancelled
6801         var ot = this.getOwnerTree();
6802         if(ot){
6803             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6804                 return false;
6805             }
6806         }
6807         return true;
6808     },
6809
6810     /**
6811      * Returns true if this node is a leaf
6812      * @return {Boolean}
6813      */
6814     isLeaf : function(){
6815         return this.leaf === true;
6816     },
6817
6818     // private
6819     setFirstChild : function(node){
6820         this.firstChild = node;
6821     },
6822
6823     //private
6824     setLastChild : function(node){
6825         this.lastChild = node;
6826     },
6827
6828
6829     /**
6830      * Returns true if this node is the last child of its parent
6831      * @return {Boolean}
6832      */
6833     isLast : function(){
6834        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6835     },
6836
6837     /**
6838      * Returns true if this node is the first child of its parent
6839      * @return {Boolean}
6840      */
6841     isFirst : function(){
6842        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6843     },
6844
6845     hasChildNodes : function(){
6846         return !this.isLeaf() && this.childNodes.length > 0;
6847     },
6848
6849     /**
6850      * Insert node(s) as the last child node of this node.
6851      * @param {Node/Array} node The node or Array of nodes to append
6852      * @return {Node} The appended node if single append, or null if an array was passed
6853      */
6854     appendChild : function(node){
6855         var multi = false;
6856         if(node instanceof Array){
6857             multi = node;
6858         }else if(arguments.length > 1){
6859             multi = arguments;
6860         }
6861         // if passed an array or multiple args do them one by one
6862         if(multi){
6863             for(var i = 0, len = multi.length; i < len; i++) {
6864                 this.appendChild(multi[i]);
6865             }
6866         }else{
6867             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6868                 return false;
6869             }
6870             var index = this.childNodes.length;
6871             var oldParent = node.parentNode;
6872             // it's a move, make sure we move it cleanly
6873             if(oldParent){
6874                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6875                     return false;
6876                 }
6877                 oldParent.removeChild(node);
6878             }
6879             index = this.childNodes.length;
6880             if(index == 0){
6881                 this.setFirstChild(node);
6882             }
6883             this.childNodes.push(node);
6884             node.parentNode = this;
6885             var ps = this.childNodes[index-1];
6886             if(ps){
6887                 node.previousSibling = ps;
6888                 ps.nextSibling = node;
6889             }else{
6890                 node.previousSibling = null;
6891             }
6892             node.nextSibling = null;
6893             this.setLastChild(node);
6894             node.setOwnerTree(this.getOwnerTree());
6895             this.fireEvent("append", this.ownerTree, this, node, index);
6896             if(oldParent){
6897                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6898             }
6899             return node;
6900         }
6901     },
6902
6903     /**
6904      * Removes a child node from this node.
6905      * @param {Node} node The node to remove
6906      * @return {Node} The removed node
6907      */
6908     removeChild : function(node){
6909         var index = this.childNodes.indexOf(node);
6910         if(index == -1){
6911             return false;
6912         }
6913         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6914             return false;
6915         }
6916
6917         // remove it from childNodes collection
6918         this.childNodes.splice(index, 1);
6919
6920         // update siblings
6921         if(node.previousSibling){
6922             node.previousSibling.nextSibling = node.nextSibling;
6923         }
6924         if(node.nextSibling){
6925             node.nextSibling.previousSibling = node.previousSibling;
6926         }
6927
6928         // update child refs
6929         if(this.firstChild == node){
6930             this.setFirstChild(node.nextSibling);
6931         }
6932         if(this.lastChild == node){
6933             this.setLastChild(node.previousSibling);
6934         }
6935
6936         node.setOwnerTree(null);
6937         // clear any references from the node
6938         node.parentNode = null;
6939         node.previousSibling = null;
6940         node.nextSibling = null;
6941         this.fireEvent("remove", this.ownerTree, this, node);
6942         return node;
6943     },
6944
6945     /**
6946      * Inserts the first node before the second node in this nodes childNodes collection.
6947      * @param {Node} node The node to insert
6948      * @param {Node} refNode The node to insert before (if null the node is appended)
6949      * @return {Node} The inserted node
6950      */
6951     insertBefore : function(node, refNode){
6952         if(!refNode){ // like standard Dom, refNode can be null for append
6953             return this.appendChild(node);
6954         }
6955         // nothing to do
6956         if(node == refNode){
6957             return false;
6958         }
6959
6960         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6961             return false;
6962         }
6963         var index = this.childNodes.indexOf(refNode);
6964         var oldParent = node.parentNode;
6965         var refIndex = index;
6966
6967         // when moving internally, indexes will change after remove
6968         if(oldParent == this && this.childNodes.indexOf(node) < index){
6969             refIndex--;
6970         }
6971
6972         // it's a move, make sure we move it cleanly
6973         if(oldParent){
6974             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6975                 return false;
6976             }
6977             oldParent.removeChild(node);
6978         }
6979         if(refIndex == 0){
6980             this.setFirstChild(node);
6981         }
6982         this.childNodes.splice(refIndex, 0, node);
6983         node.parentNode = this;
6984         var ps = this.childNodes[refIndex-1];
6985         if(ps){
6986             node.previousSibling = ps;
6987             ps.nextSibling = node;
6988         }else{
6989             node.previousSibling = null;
6990         }
6991         node.nextSibling = refNode;
6992         refNode.previousSibling = node;
6993         node.setOwnerTree(this.getOwnerTree());
6994         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6995         if(oldParent){
6996             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6997         }
6998         return node;
6999     },
7000
7001     /**
7002      * Returns the child node at the specified index.
7003      * @param {Number} index
7004      * @return {Node}
7005      */
7006     item : function(index){
7007         return this.childNodes[index];
7008     },
7009
7010     /**
7011      * Replaces one child node in this node with another.
7012      * @param {Node} newChild The replacement node
7013      * @param {Node} oldChild The node to replace
7014      * @return {Node} The replaced node
7015      */
7016     replaceChild : function(newChild, oldChild){
7017         this.insertBefore(newChild, oldChild);
7018         this.removeChild(oldChild);
7019         return oldChild;
7020     },
7021
7022     /**
7023      * Returns the index of a child node
7024      * @param {Node} node
7025      * @return {Number} The index of the node or -1 if it was not found
7026      */
7027     indexOf : function(child){
7028         return this.childNodes.indexOf(child);
7029     },
7030
7031     /**
7032      * Returns the tree this node is in.
7033      * @return {Tree}
7034      */
7035     getOwnerTree : function(){
7036         // if it doesn't have one, look for one
7037         if(!this.ownerTree){
7038             var p = this;
7039             while(p){
7040                 if(p.ownerTree){
7041                     this.ownerTree = p.ownerTree;
7042                     break;
7043                 }
7044                 p = p.parentNode;
7045             }
7046         }
7047         return this.ownerTree;
7048     },
7049
7050     /**
7051      * Returns depth of this node (the root node has a depth of 0)
7052      * @return {Number}
7053      */
7054     getDepth : function(){
7055         var depth = 0;
7056         var p = this;
7057         while(p.parentNode){
7058             ++depth;
7059             p = p.parentNode;
7060         }
7061         return depth;
7062     },
7063
7064     // private
7065     setOwnerTree : function(tree){
7066         // if it's move, we need to update everyone
7067         if(tree != this.ownerTree){
7068             if(this.ownerTree){
7069                 this.ownerTree.unregisterNode(this);
7070             }
7071             this.ownerTree = tree;
7072             var cs = this.childNodes;
7073             for(var i = 0, len = cs.length; i < len; i++) {
7074                 cs[i].setOwnerTree(tree);
7075             }
7076             if(tree){
7077                 tree.registerNode(this);
7078             }
7079         }
7080     },
7081
7082     /**
7083      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7084      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7085      * @return {String} The path
7086      */
7087     getPath : function(attr){
7088         attr = attr || "id";
7089         var p = this.parentNode;
7090         var b = [this.attributes[attr]];
7091         while(p){
7092             b.unshift(p.attributes[attr]);
7093             p = p.parentNode;
7094         }
7095         var sep = this.getOwnerTree().pathSeparator;
7096         return sep + b.join(sep);
7097     },
7098
7099     /**
7100      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7101      * function call will be the scope provided or the current node. The arguments to the function
7102      * will be the args provided or the current node. If the function returns false at any point,
7103      * the bubble is stopped.
7104      * @param {Function} fn The function to call
7105      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7106      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7107      */
7108     bubble : function(fn, scope, args){
7109         var p = this;
7110         while(p){
7111             if(fn.call(scope || p, args || p) === false){
7112                 break;
7113             }
7114             p = p.parentNode;
7115         }
7116     },
7117
7118     /**
7119      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7120      * function call will be the scope provided or the current node. The arguments to the function
7121      * will be the args provided or the current node. If the function returns false at any point,
7122      * the cascade is stopped on that branch.
7123      * @param {Function} fn The function to call
7124      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7125      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7126      */
7127     cascade : function(fn, scope, args){
7128         if(fn.call(scope || this, args || this) !== false){
7129             var cs = this.childNodes;
7130             for(var i = 0, len = cs.length; i < len; i++) {
7131                 cs[i].cascade(fn, scope, args);
7132             }
7133         }
7134     },
7135
7136     /**
7137      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7138      * function call will be the scope provided or the current node. The arguments to the function
7139      * will be the args provided or the current node. If the function returns false at any point,
7140      * the iteration stops.
7141      * @param {Function} fn The function to call
7142      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7143      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7144      */
7145     eachChild : function(fn, scope, args){
7146         var cs = this.childNodes;
7147         for(var i = 0, len = cs.length; i < len; i++) {
7148                 if(fn.call(scope || this, args || cs[i]) === false){
7149                     break;
7150                 }
7151         }
7152     },
7153
7154     /**
7155      * Finds the first child that has the attribute with the specified value.
7156      * @param {String} attribute The attribute name
7157      * @param {Mixed} value The value to search for
7158      * @return {Node} The found child or null if none was found
7159      */
7160     findChild : function(attribute, value){
7161         var cs = this.childNodes;
7162         for(var i = 0, len = cs.length; i < len; i++) {
7163                 if(cs[i].attributes[attribute] == value){
7164                     return cs[i];
7165                 }
7166         }
7167         return null;
7168     },
7169
7170     /**
7171      * Finds the first child by a custom function. The child matches if the function passed
7172      * returns true.
7173      * @param {Function} fn
7174      * @param {Object} scope (optional)
7175      * @return {Node} The found child or null if none was found
7176      */
7177     findChildBy : function(fn, scope){
7178         var cs = this.childNodes;
7179         for(var i = 0, len = cs.length; i < len; i++) {
7180                 if(fn.call(scope||cs[i], cs[i]) === true){
7181                     return cs[i];
7182                 }
7183         }
7184         return null;
7185     },
7186
7187     /**
7188      * Sorts this nodes children using the supplied sort function
7189      * @param {Function} fn
7190      * @param {Object} scope (optional)
7191      */
7192     sort : function(fn, scope){
7193         var cs = this.childNodes;
7194         var len = cs.length;
7195         if(len > 0){
7196             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7197             cs.sort(sortFn);
7198             for(var i = 0; i < len; i++){
7199                 var n = cs[i];
7200                 n.previousSibling = cs[i-1];
7201                 n.nextSibling = cs[i+1];
7202                 if(i == 0){
7203                     this.setFirstChild(n);
7204                 }
7205                 if(i == len-1){
7206                     this.setLastChild(n);
7207                 }
7208             }
7209         }
7210     },
7211
7212     /**
7213      * Returns true if this node is an ancestor (at any point) of the passed node.
7214      * @param {Node} node
7215      * @return {Boolean}
7216      */
7217     contains : function(node){
7218         return node.isAncestor(this);
7219     },
7220
7221     /**
7222      * Returns true if the passed node is an ancestor (at any point) of this node.
7223      * @param {Node} node
7224      * @return {Boolean}
7225      */
7226     isAncestor : function(node){
7227         var p = this.parentNode;
7228         while(p){
7229             if(p == node){
7230                 return true;
7231             }
7232             p = p.parentNode;
7233         }
7234         return false;
7235     },
7236
7237     toString : function(){
7238         return "[Node"+(this.id?" "+this.id:"")+"]";
7239     }
7240 });/*
7241  * Based on:
7242  * Ext JS Library 1.1.1
7243  * Copyright(c) 2006-2007, Ext JS, LLC.
7244  *
7245  * Originally Released Under LGPL - original licence link has changed is not relivant.
7246  *
7247  * Fork - LGPL
7248  * <script type="text/javascript">
7249  */
7250  
7251
7252 /**
7253  * @class Roo.ComponentMgr
7254  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7255  * @singleton
7256  */
7257 Roo.ComponentMgr = function(){
7258     var all = new Roo.util.MixedCollection();
7259
7260     return {
7261         /**
7262          * Registers a component.
7263          * @param {Roo.Component} c The component
7264          */
7265         register : function(c){
7266             all.add(c);
7267         },
7268
7269         /**
7270          * Unregisters a component.
7271          * @param {Roo.Component} c The component
7272          */
7273         unregister : function(c){
7274             all.remove(c);
7275         },
7276
7277         /**
7278          * Returns a component by id
7279          * @param {String} id The component id
7280          */
7281         get : function(id){
7282             return all.get(id);
7283         },
7284
7285         /**
7286          * Registers a function that will be called when a specified component is added to ComponentMgr
7287          * @param {String} id The component id
7288          * @param {Funtction} fn The callback function
7289          * @param {Object} scope The scope of the callback
7290          */
7291         onAvailable : function(id, fn, scope){
7292             all.on("add", function(index, o){
7293                 if(o.id == id){
7294                     fn.call(scope || o, o);
7295                     all.un("add", fn, scope);
7296                 }
7297             });
7298         }
7299     };
7300 }();/*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310  
7311 /**
7312  * @class Roo.Component
7313  * @extends Roo.util.Observable
7314  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7315  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7316  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7317  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7318  * All visual components (widgets) that require rendering into a layout should subclass Component.
7319  * @constructor
7320  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7321  * 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
7322  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7323  */
7324 Roo.Component = function(config){
7325     config = config || {};
7326     if(config.tagName || config.dom || typeof config == "string"){ // element object
7327         config = {el: config, id: config.id || config};
7328     }
7329     this.initialConfig = config;
7330
7331     Roo.apply(this, config);
7332     this.addEvents({
7333         /**
7334          * @event disable
7335          * Fires after the component is disabled.
7336              * @param {Roo.Component} this
7337              */
7338         disable : true,
7339         /**
7340          * @event enable
7341          * Fires after the component is enabled.
7342              * @param {Roo.Component} this
7343              */
7344         enable : true,
7345         /**
7346          * @event beforeshow
7347          * Fires before the component is shown.  Return false to stop the show.
7348              * @param {Roo.Component} this
7349              */
7350         beforeshow : true,
7351         /**
7352          * @event show
7353          * Fires after the component is shown.
7354              * @param {Roo.Component} this
7355              */
7356         show : true,
7357         /**
7358          * @event beforehide
7359          * Fires before the component is hidden. Return false to stop the hide.
7360              * @param {Roo.Component} this
7361              */
7362         beforehide : true,
7363         /**
7364          * @event hide
7365          * Fires after the component is hidden.
7366              * @param {Roo.Component} this
7367              */
7368         hide : true,
7369         /**
7370          * @event beforerender
7371          * Fires before the component is rendered. Return false to stop the render.
7372              * @param {Roo.Component} this
7373              */
7374         beforerender : true,
7375         /**
7376          * @event render
7377          * Fires after the component is rendered.
7378              * @param {Roo.Component} this
7379              */
7380         render : true,
7381         /**
7382          * @event beforedestroy
7383          * Fires before the component is destroyed. Return false to stop the destroy.
7384              * @param {Roo.Component} this
7385              */
7386         beforedestroy : true,
7387         /**
7388          * @event destroy
7389          * Fires after the component is destroyed.
7390              * @param {Roo.Component} this
7391              */
7392         destroy : true
7393     });
7394     if(!this.id){
7395         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7396     }
7397     Roo.ComponentMgr.register(this);
7398     Roo.Component.superclass.constructor.call(this);
7399     this.initComponent();
7400     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7401         this.render(this.renderTo);
7402         delete this.renderTo;
7403     }
7404 };
7405
7406 /** @private */
7407 Roo.Component.AUTO_ID = 1000;
7408
7409 Roo.extend(Roo.Component, Roo.util.Observable, {
7410     /**
7411      * @scope Roo.Component.prototype
7412      * @type {Boolean}
7413      * true if this component is hidden. Read-only.
7414      */
7415     hidden : false,
7416     /**
7417      * @type {Boolean}
7418      * true if this component is disabled. Read-only.
7419      */
7420     disabled : false,
7421     /**
7422      * @type {Boolean}
7423      * true if this component has been rendered. Read-only.
7424      */
7425     rendered : false,
7426     
7427     /** @cfg {String} disableClass
7428      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7429      */
7430     disabledClass : "x-item-disabled",
7431         /** @cfg {Boolean} allowDomMove
7432          * Whether the component can move the Dom node when rendering (defaults to true).
7433          */
7434     allowDomMove : true,
7435     /** @cfg {String} hideMode
7436      * How this component should hidden. Supported values are
7437      * "visibility" (css visibility), "offsets" (negative offset position) and
7438      * "display" (css display) - defaults to "display".
7439      */
7440     hideMode: 'display',
7441
7442     /** @private */
7443     ctype : "Roo.Component",
7444
7445     /**
7446      * @cfg {String} actionMode 
7447      * which property holds the element that used for  hide() / show() / disable() / enable()
7448      * default is 'el' 
7449      */
7450     actionMode : "el",
7451
7452     /** @private */
7453     getActionEl : function(){
7454         return this[this.actionMode];
7455     },
7456
7457     initComponent : Roo.emptyFn,
7458     /**
7459      * If this is a lazy rendering component, render it to its container element.
7460      * @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.
7461      */
7462     render : function(container, position){
7463         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7464             if(!container && this.el){
7465                 this.el = Roo.get(this.el);
7466                 container = this.el.dom.parentNode;
7467                 this.allowDomMove = false;
7468             }
7469             this.container = Roo.get(container);
7470             this.rendered = true;
7471             if(position !== undefined){
7472                 if(typeof position == 'number'){
7473                     position = this.container.dom.childNodes[position];
7474                 }else{
7475                     position = Roo.getDom(position);
7476                 }
7477             }
7478             this.onRender(this.container, position || null);
7479             if(this.cls){
7480                 this.el.addClass(this.cls);
7481                 delete this.cls;
7482             }
7483             if(this.style){
7484                 this.el.applyStyles(this.style);
7485                 delete this.style;
7486             }
7487             this.fireEvent("render", this);
7488             this.afterRender(this.container);
7489             if(this.hidden){
7490                 this.hide();
7491             }
7492             if(this.disabled){
7493                 this.disable();
7494             }
7495         }
7496         return this;
7497     },
7498
7499     /** @private */
7500     // default function is not really useful
7501     onRender : function(ct, position){
7502         if(this.el){
7503             this.el = Roo.get(this.el);
7504             if(this.allowDomMove !== false){
7505                 ct.dom.insertBefore(this.el.dom, position);
7506             }
7507         }
7508     },
7509
7510     /** @private */
7511     getAutoCreate : function(){
7512         var cfg = typeof this.autoCreate == "object" ?
7513                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7514         if(this.id && !cfg.id){
7515             cfg.id = this.id;
7516         }
7517         return cfg;
7518     },
7519
7520     /** @private */
7521     afterRender : Roo.emptyFn,
7522
7523     /**
7524      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7525      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7526      */
7527     destroy : function(){
7528         if(this.fireEvent("beforedestroy", this) !== false){
7529             this.purgeListeners();
7530             this.beforeDestroy();
7531             if(this.rendered){
7532                 this.el.removeAllListeners();
7533                 this.el.remove();
7534                 if(this.actionMode == "container"){
7535                     this.container.remove();
7536                 }
7537             }
7538             this.onDestroy();
7539             Roo.ComponentMgr.unregister(this);
7540             this.fireEvent("destroy", this);
7541         }
7542     },
7543
7544         /** @private */
7545     beforeDestroy : function(){
7546
7547     },
7548
7549         /** @private */
7550         onDestroy : function(){
7551
7552     },
7553
7554     /**
7555      * Returns the underlying {@link Roo.Element}.
7556      * @return {Roo.Element} The element
7557      */
7558     getEl : function(){
7559         return this.el;
7560     },
7561
7562     /**
7563      * Returns the id of this component.
7564      * @return {String}
7565      */
7566     getId : function(){
7567         return this.id;
7568     },
7569
7570     /**
7571      * Try to focus this component.
7572      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7573      * @return {Roo.Component} this
7574      */
7575     focus : function(selectText){
7576         if(this.rendered){
7577             this.el.focus();
7578             if(selectText === true){
7579                 this.el.dom.select();
7580             }
7581         }
7582         return this;
7583     },
7584
7585     /** @private */
7586     blur : function(){
7587         if(this.rendered){
7588             this.el.blur();
7589         }
7590         return this;
7591     },
7592
7593     /**
7594      * Disable this component.
7595      * @return {Roo.Component} this
7596      */
7597     disable : function(){
7598         if(this.rendered){
7599             this.onDisable();
7600         }
7601         this.disabled = true;
7602         this.fireEvent("disable", this);
7603         return this;
7604     },
7605
7606         // private
7607     onDisable : function(){
7608         this.getActionEl().addClass(this.disabledClass);
7609         this.el.dom.disabled = true;
7610     },
7611
7612     /**
7613      * Enable this component.
7614      * @return {Roo.Component} this
7615      */
7616     enable : function(){
7617         if(this.rendered){
7618             this.onEnable();
7619         }
7620         this.disabled = false;
7621         this.fireEvent("enable", this);
7622         return this;
7623     },
7624
7625         // private
7626     onEnable : function(){
7627         this.getActionEl().removeClass(this.disabledClass);
7628         this.el.dom.disabled = false;
7629     },
7630
7631     /**
7632      * Convenience function for setting disabled/enabled by boolean.
7633      * @param {Boolean} disabled
7634      */
7635     setDisabled : function(disabled){
7636         this[disabled ? "disable" : "enable"]();
7637     },
7638
7639     /**
7640      * Show this component.
7641      * @return {Roo.Component} this
7642      */
7643     show: function(){
7644         if(this.fireEvent("beforeshow", this) !== false){
7645             this.hidden = false;
7646             if(this.rendered){
7647                 this.onShow();
7648             }
7649             this.fireEvent("show", this);
7650         }
7651         return this;
7652     },
7653
7654     // private
7655     onShow : function(){
7656         var ae = this.getActionEl();
7657         if(this.hideMode == 'visibility'){
7658             ae.dom.style.visibility = "visible";
7659         }else if(this.hideMode == 'offsets'){
7660             ae.removeClass('x-hidden');
7661         }else{
7662             ae.dom.style.display = "";
7663         }
7664     },
7665
7666     /**
7667      * Hide this component.
7668      * @return {Roo.Component} this
7669      */
7670     hide: function(){
7671         if(this.fireEvent("beforehide", this) !== false){
7672             this.hidden = true;
7673             if(this.rendered){
7674                 this.onHide();
7675             }
7676             this.fireEvent("hide", this);
7677         }
7678         return this;
7679     },
7680
7681     // private
7682     onHide : function(){
7683         var ae = this.getActionEl();
7684         if(this.hideMode == 'visibility'){
7685             ae.dom.style.visibility = "hidden";
7686         }else if(this.hideMode == 'offsets'){
7687             ae.addClass('x-hidden');
7688         }else{
7689             ae.dom.style.display = "none";
7690         }
7691     },
7692
7693     /**
7694      * Convenience function to hide or show this component by boolean.
7695      * @param {Boolean} visible True to show, false to hide
7696      * @return {Roo.Component} this
7697      */
7698     setVisible: function(visible){
7699         if(visible) {
7700             this.show();
7701         }else{
7702             this.hide();
7703         }
7704         return this;
7705     },
7706
7707     /**
7708      * Returns true if this component is visible.
7709      */
7710     isVisible : function(){
7711         return this.getActionEl().isVisible();
7712     },
7713
7714     cloneConfig : function(overrides){
7715         overrides = overrides || {};
7716         var id = overrides.id || Roo.id();
7717         var cfg = Roo.applyIf(overrides, this.initialConfig);
7718         cfg.id = id; // prevent dup id
7719         return new this.constructor(cfg);
7720     }
7721 });/*
7722  * Based on:
7723  * Ext JS Library 1.1.1
7724  * Copyright(c) 2006-2007, Ext JS, LLC.
7725  *
7726  * Originally Released Under LGPL - original licence link has changed is not relivant.
7727  *
7728  * Fork - LGPL
7729  * <script type="text/javascript">
7730  */
7731  (function(){ 
7732 /**
7733  * @class Roo.Layer
7734  * @extends Roo.Element
7735  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7736  * automatic maintaining of shadow/shim positions.
7737  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7738  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7739  * you can pass a string with a CSS class name. False turns off the shadow.
7740  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7741  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7742  * @cfg {String} cls CSS class to add to the element
7743  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7744  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7745  * @constructor
7746  * @param {Object} config An object with config options.
7747  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7748  */
7749
7750 Roo.Layer = function(config, existingEl){
7751     config = config || {};
7752     var dh = Roo.DomHelper;
7753     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7754     if(existingEl){
7755         this.dom = Roo.getDom(existingEl);
7756     }
7757     if(!this.dom){
7758         var o = config.dh || {tag: "div", cls: "x-layer"};
7759         this.dom = dh.append(pel, o);
7760     }
7761     if(config.cls){
7762         this.addClass(config.cls);
7763     }
7764     this.constrain = config.constrain !== false;
7765     this.visibilityMode = Roo.Element.VISIBILITY;
7766     if(config.id){
7767         this.id = this.dom.id = config.id;
7768     }else{
7769         this.id = Roo.id(this.dom);
7770     }
7771     this.zindex = config.zindex || this.getZIndex();
7772     this.position("absolute", this.zindex);
7773     if(config.shadow){
7774         this.shadowOffset = config.shadowOffset || 4;
7775         this.shadow = new Roo.Shadow({
7776             offset : this.shadowOffset,
7777             mode : config.shadow
7778         });
7779     }else{
7780         this.shadowOffset = 0;
7781     }
7782     this.useShim = config.shim !== false && Roo.useShims;
7783     this.useDisplay = config.useDisplay;
7784     this.hide();
7785 };
7786
7787 var supr = Roo.Element.prototype;
7788
7789 // shims are shared among layer to keep from having 100 iframes
7790 var shims = [];
7791
7792 Roo.extend(Roo.Layer, Roo.Element, {
7793
7794     getZIndex : function(){
7795         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7796     },
7797
7798     getShim : function(){
7799         if(!this.useShim){
7800             return null;
7801         }
7802         if(this.shim){
7803             return this.shim;
7804         }
7805         var shim = shims.shift();
7806         if(!shim){
7807             shim = this.createShim();
7808             shim.enableDisplayMode('block');
7809             shim.dom.style.display = 'none';
7810             shim.dom.style.visibility = 'visible';
7811         }
7812         var pn = this.dom.parentNode;
7813         if(shim.dom.parentNode != pn){
7814             pn.insertBefore(shim.dom, this.dom);
7815         }
7816         shim.setStyle('z-index', this.getZIndex()-2);
7817         this.shim = shim;
7818         return shim;
7819     },
7820
7821     hideShim : function(){
7822         if(this.shim){
7823             this.shim.setDisplayed(false);
7824             shims.push(this.shim);
7825             delete this.shim;
7826         }
7827     },
7828
7829     disableShadow : function(){
7830         if(this.shadow){
7831             this.shadowDisabled = true;
7832             this.shadow.hide();
7833             this.lastShadowOffset = this.shadowOffset;
7834             this.shadowOffset = 0;
7835         }
7836     },
7837
7838     enableShadow : function(show){
7839         if(this.shadow){
7840             this.shadowDisabled = false;
7841             this.shadowOffset = this.lastShadowOffset;
7842             delete this.lastShadowOffset;
7843             if(show){
7844                 this.sync(true);
7845             }
7846         }
7847     },
7848
7849     // private
7850     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7851     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7852     sync : function(doShow){
7853         var sw = this.shadow;
7854         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7855             var sh = this.getShim();
7856
7857             var w = this.getWidth(),
7858                 h = this.getHeight();
7859
7860             var l = this.getLeft(true),
7861                 t = this.getTop(true);
7862
7863             if(sw && !this.shadowDisabled){
7864                 if(doShow && !sw.isVisible()){
7865                     sw.show(this);
7866                 }else{
7867                     sw.realign(l, t, w, h);
7868                 }
7869                 if(sh){
7870                     if(doShow){
7871                        sh.show();
7872                     }
7873                     // fit the shim behind the shadow, so it is shimmed too
7874                     var a = sw.adjusts, s = sh.dom.style;
7875                     s.left = (Math.min(l, l+a.l))+"px";
7876                     s.top = (Math.min(t, t+a.t))+"px";
7877                     s.width = (w+a.w)+"px";
7878                     s.height = (h+a.h)+"px";
7879                 }
7880             }else if(sh){
7881                 if(doShow){
7882                    sh.show();
7883                 }
7884                 sh.setSize(w, h);
7885                 sh.setLeftTop(l, t);
7886             }
7887             
7888         }
7889     },
7890
7891     // private
7892     destroy : function(){
7893         this.hideShim();
7894         if(this.shadow){
7895             this.shadow.hide();
7896         }
7897         this.removeAllListeners();
7898         var pn = this.dom.parentNode;
7899         if(pn){
7900             pn.removeChild(this.dom);
7901         }
7902         Roo.Element.uncache(this.id);
7903     },
7904
7905     remove : function(){
7906         this.destroy();
7907     },
7908
7909     // private
7910     beginUpdate : function(){
7911         this.updating = true;
7912     },
7913
7914     // private
7915     endUpdate : function(){
7916         this.updating = false;
7917         this.sync(true);
7918     },
7919
7920     // private
7921     hideUnders : function(negOffset){
7922         if(this.shadow){
7923             this.shadow.hide();
7924         }
7925         this.hideShim();
7926     },
7927
7928     // private
7929     constrainXY : function(){
7930         if(this.constrain){
7931             var vw = Roo.lib.Dom.getViewWidth(),
7932                 vh = Roo.lib.Dom.getViewHeight();
7933             var s = Roo.get(document).getScroll();
7934
7935             var xy = this.getXY();
7936             var x = xy[0], y = xy[1];   
7937             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7938             // only move it if it needs it
7939             var moved = false;
7940             // first validate right/bottom
7941             if((x + w) > vw+s.left){
7942                 x = vw - w - this.shadowOffset;
7943                 moved = true;
7944             }
7945             if((y + h) > vh+s.top){
7946                 y = vh - h - this.shadowOffset;
7947                 moved = true;
7948             }
7949             // then make sure top/left isn't negative
7950             if(x < s.left){
7951                 x = s.left;
7952                 moved = true;
7953             }
7954             if(y < s.top){
7955                 y = s.top;
7956                 moved = true;
7957             }
7958             if(moved){
7959                 if(this.avoidY){
7960                     var ay = this.avoidY;
7961                     if(y <= ay && (y+h) >= ay){
7962                         y = ay-h-5;   
7963                     }
7964                 }
7965                 xy = [x, y];
7966                 this.storeXY(xy);
7967                 supr.setXY.call(this, xy);
7968                 this.sync();
7969             }
7970         }
7971     },
7972
7973     isVisible : function(){
7974         return this.visible;    
7975     },
7976
7977     // private
7978     showAction : function(){
7979         this.visible = true; // track visibility to prevent getStyle calls
7980         if(this.useDisplay === true){
7981             this.setDisplayed("");
7982         }else if(this.lastXY){
7983             supr.setXY.call(this, this.lastXY);
7984         }else if(this.lastLT){
7985             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7986         }
7987     },
7988
7989     // private
7990     hideAction : function(){
7991         this.visible = false;
7992         if(this.useDisplay === true){
7993             this.setDisplayed(false);
7994         }else{
7995             this.setLeftTop(-10000,-10000);
7996         }
7997     },
7998
7999     // overridden Element method
8000     setVisible : function(v, a, d, c, e){
8001         if(v){
8002             this.showAction();
8003         }
8004         if(a && v){
8005             var cb = function(){
8006                 this.sync(true);
8007                 if(c){
8008                     c();
8009                 }
8010             }.createDelegate(this);
8011             supr.setVisible.call(this, true, true, d, cb, e);
8012         }else{
8013             if(!v){
8014                 this.hideUnders(true);
8015             }
8016             var cb = c;
8017             if(a){
8018                 cb = function(){
8019                     this.hideAction();
8020                     if(c){
8021                         c();
8022                     }
8023                 }.createDelegate(this);
8024             }
8025             supr.setVisible.call(this, v, a, d, cb, e);
8026             if(v){
8027                 this.sync(true);
8028             }else if(!a){
8029                 this.hideAction();
8030             }
8031         }
8032     },
8033
8034     storeXY : function(xy){
8035         delete this.lastLT;
8036         this.lastXY = xy;
8037     },
8038
8039     storeLeftTop : function(left, top){
8040         delete this.lastXY;
8041         this.lastLT = [left, top];
8042     },
8043
8044     // private
8045     beforeFx : function(){
8046         this.beforeAction();
8047         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8048     },
8049
8050     // private
8051     afterFx : function(){
8052         Roo.Layer.superclass.afterFx.apply(this, arguments);
8053         this.sync(this.isVisible());
8054     },
8055
8056     // private
8057     beforeAction : function(){
8058         if(!this.updating && this.shadow){
8059             this.shadow.hide();
8060         }
8061     },
8062
8063     // overridden Element method
8064     setLeft : function(left){
8065         this.storeLeftTop(left, this.getTop(true));
8066         supr.setLeft.apply(this, arguments);
8067         this.sync();
8068     },
8069
8070     setTop : function(top){
8071         this.storeLeftTop(this.getLeft(true), top);
8072         supr.setTop.apply(this, arguments);
8073         this.sync();
8074     },
8075
8076     setLeftTop : function(left, top){
8077         this.storeLeftTop(left, top);
8078         supr.setLeftTop.apply(this, arguments);
8079         this.sync();
8080     },
8081
8082     setXY : function(xy, a, d, c, e){
8083         this.fixDisplay();
8084         this.beforeAction();
8085         this.storeXY(xy);
8086         var cb = this.createCB(c);
8087         supr.setXY.call(this, xy, a, d, cb, e);
8088         if(!a){
8089             cb();
8090         }
8091     },
8092
8093     // private
8094     createCB : function(c){
8095         var el = this;
8096         return function(){
8097             el.constrainXY();
8098             el.sync(true);
8099             if(c){
8100                 c();
8101             }
8102         };
8103     },
8104
8105     // overridden Element method
8106     setX : function(x, a, d, c, e){
8107         this.setXY([x, this.getY()], a, d, c, e);
8108     },
8109
8110     // overridden Element method
8111     setY : function(y, a, d, c, e){
8112         this.setXY([this.getX(), y], a, d, c, e);
8113     },
8114
8115     // overridden Element method
8116     setSize : function(w, h, a, d, c, e){
8117         this.beforeAction();
8118         var cb = this.createCB(c);
8119         supr.setSize.call(this, w, h, a, d, cb, e);
8120         if(!a){
8121             cb();
8122         }
8123     },
8124
8125     // overridden Element method
8126     setWidth : function(w, a, d, c, e){
8127         this.beforeAction();
8128         var cb = this.createCB(c);
8129         supr.setWidth.call(this, w, a, d, cb, e);
8130         if(!a){
8131             cb();
8132         }
8133     },
8134
8135     // overridden Element method
8136     setHeight : function(h, a, d, c, e){
8137         this.beforeAction();
8138         var cb = this.createCB(c);
8139         supr.setHeight.call(this, h, a, d, cb, e);
8140         if(!a){
8141             cb();
8142         }
8143     },
8144
8145     // overridden Element method
8146     setBounds : function(x, y, w, h, a, d, c, e){
8147         this.beforeAction();
8148         var cb = this.createCB(c);
8149         if(!a){
8150             this.storeXY([x, y]);
8151             supr.setXY.call(this, [x, y]);
8152             supr.setSize.call(this, w, h, a, d, cb, e);
8153             cb();
8154         }else{
8155             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8156         }
8157         return this;
8158     },
8159     
8160     /**
8161      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8162      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8163      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8164      * @param {Number} zindex The new z-index to set
8165      * @return {this} The Layer
8166      */
8167     setZIndex : function(zindex){
8168         this.zindex = zindex;
8169         this.setStyle("z-index", zindex + 2);
8170         if(this.shadow){
8171             this.shadow.setZIndex(zindex + 1);
8172         }
8173         if(this.shim){
8174             this.shim.setStyle("z-index", zindex);
8175         }
8176     }
8177 });
8178 })();/*
8179  * Based on:
8180  * Ext JS Library 1.1.1
8181  * Copyright(c) 2006-2007, Ext JS, LLC.
8182  *
8183  * Originally Released Under LGPL - original licence link has changed is not relivant.
8184  *
8185  * Fork - LGPL
8186  * <script type="text/javascript">
8187  */
8188
8189
8190 /**
8191  * @class Roo.Shadow
8192  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8193  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8194  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8195  * @constructor
8196  * Create a new Shadow
8197  * @param {Object} config The config object
8198  */
8199 Roo.Shadow = function(config){
8200     Roo.apply(this, config);
8201     if(typeof this.mode != "string"){
8202         this.mode = this.defaultMode;
8203     }
8204     var o = this.offset, a = {h: 0};
8205     var rad = Math.floor(this.offset/2);
8206     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8207         case "drop":
8208             a.w = 0;
8209             a.l = a.t = o;
8210             a.t -= 1;
8211             if(Roo.isIE){
8212                 a.l -= this.offset + rad;
8213                 a.t -= this.offset + rad;
8214                 a.w -= rad;
8215                 a.h -= rad;
8216                 a.t += 1;
8217             }
8218         break;
8219         case "sides":
8220             a.w = (o*2);
8221             a.l = -o;
8222             a.t = o-1;
8223             if(Roo.isIE){
8224                 a.l -= (this.offset - rad);
8225                 a.t -= this.offset + rad;
8226                 a.l += 1;
8227                 a.w -= (this.offset - rad)*2;
8228                 a.w -= rad + 1;
8229                 a.h -= 1;
8230             }
8231         break;
8232         case "frame":
8233             a.w = a.h = (o*2);
8234             a.l = a.t = -o;
8235             a.t += 1;
8236             a.h -= 2;
8237             if(Roo.isIE){
8238                 a.l -= (this.offset - rad);
8239                 a.t -= (this.offset - rad);
8240                 a.l += 1;
8241                 a.w -= (this.offset + rad + 1);
8242                 a.h -= (this.offset + rad);
8243                 a.h += 1;
8244             }
8245         break;
8246     };
8247
8248     this.adjusts = a;
8249 };
8250
8251 Roo.Shadow.prototype = {
8252     /**
8253      * @cfg {String} mode
8254      * The shadow display mode.  Supports the following options:<br />
8255      * sides: Shadow displays on both sides and bottom only<br />
8256      * frame: Shadow displays equally on all four sides<br />
8257      * drop: Traditional bottom-right drop shadow (default)
8258      */
8259     /**
8260      * @cfg {String} offset
8261      * The number of pixels to offset the shadow from the element (defaults to 4)
8262      */
8263     offset: 4,
8264
8265     // private
8266     defaultMode: "drop",
8267
8268     /**
8269      * Displays the shadow under the target element
8270      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8271      */
8272     show : function(target){
8273         target = Roo.get(target);
8274         if(!this.el){
8275             this.el = Roo.Shadow.Pool.pull();
8276             if(this.el.dom.nextSibling != target.dom){
8277                 this.el.insertBefore(target);
8278             }
8279         }
8280         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8281         if(Roo.isIE){
8282             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8283         }
8284         this.realign(
8285             target.getLeft(true),
8286             target.getTop(true),
8287             target.getWidth(),
8288             target.getHeight()
8289         );
8290         this.el.dom.style.display = "block";
8291     },
8292
8293     /**
8294      * Returns true if the shadow is visible, else false
8295      */
8296     isVisible : function(){
8297         return this.el ? true : false;  
8298     },
8299
8300     /**
8301      * Direct alignment when values are already available. Show must be called at least once before
8302      * calling this method to ensure it is initialized.
8303      * @param {Number} left The target element left position
8304      * @param {Number} top The target element top position
8305      * @param {Number} width The target element width
8306      * @param {Number} height The target element height
8307      */
8308     realign : function(l, t, w, h){
8309         if(!this.el){
8310             return;
8311         }
8312         var a = this.adjusts, d = this.el.dom, s = d.style;
8313         var iea = 0;
8314         s.left = (l+a.l)+"px";
8315         s.top = (t+a.t)+"px";
8316         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8317  
8318         if(s.width != sws || s.height != shs){
8319             s.width = sws;
8320             s.height = shs;
8321             if(!Roo.isIE){
8322                 var cn = d.childNodes;
8323                 var sww = Math.max(0, (sw-12))+"px";
8324                 cn[0].childNodes[1].style.width = sww;
8325                 cn[1].childNodes[1].style.width = sww;
8326                 cn[2].childNodes[1].style.width = sww;
8327                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8328             }
8329         }
8330     },
8331
8332     /**
8333      * Hides this shadow
8334      */
8335     hide : function(){
8336         if(this.el){
8337             this.el.dom.style.display = "none";
8338             Roo.Shadow.Pool.push(this.el);
8339             delete this.el;
8340         }
8341     },
8342
8343     /**
8344      * Adjust the z-index of this shadow
8345      * @param {Number} zindex The new z-index
8346      */
8347     setZIndex : function(z){
8348         this.zIndex = z;
8349         if(this.el){
8350             this.el.setStyle("z-index", z);
8351         }
8352     }
8353 };
8354
8355 // Private utility class that manages the internal Shadow cache
8356 Roo.Shadow.Pool = function(){
8357     var p = [];
8358     var markup = Roo.isIE ?
8359                  '<div class="x-ie-shadow"></div>' :
8360                  '<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>';
8361     return {
8362         pull : function(){
8363             var sh = p.shift();
8364             if(!sh){
8365                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8366                 sh.autoBoxAdjust = false;
8367             }
8368             return sh;
8369         },
8370
8371         push : function(sh){
8372             p.push(sh);
8373         }
8374     };
8375 }();/*
8376  * Based on:
8377  * Ext JS Library 1.1.1
8378  * Copyright(c) 2006-2007, Ext JS, LLC.
8379  *
8380  * Originally Released Under LGPL - original licence link has changed is not relivant.
8381  *
8382  * Fork - LGPL
8383  * <script type="text/javascript">
8384  */
8385
8386 /**
8387  * @class Roo.BoxComponent
8388  * @extends Roo.Component
8389  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8390  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8391  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8392  * layout containers.
8393  * @constructor
8394  * @param {Roo.Element/String/Object} config The configuration options.
8395  */
8396 Roo.BoxComponent = function(config){
8397     Roo.Component.call(this, config);
8398     this.addEvents({
8399         /**
8400          * @event resize
8401          * Fires after the component is resized.
8402              * @param {Roo.Component} this
8403              * @param {Number} adjWidth The box-adjusted width that was set
8404              * @param {Number} adjHeight The box-adjusted height that was set
8405              * @param {Number} rawWidth The width that was originally specified
8406              * @param {Number} rawHeight The height that was originally specified
8407              */
8408         resize : true,
8409         /**
8410          * @event move
8411          * Fires after the component is moved.
8412              * @param {Roo.Component} this
8413              * @param {Number} x The new x position
8414              * @param {Number} y The new y position
8415              */
8416         move : true
8417     });
8418 };
8419
8420 Roo.extend(Roo.BoxComponent, Roo.Component, {
8421     // private, set in afterRender to signify that the component has been rendered
8422     boxReady : false,
8423     // private, used to defer height settings to subclasses
8424     deferHeight: false,
8425     /** @cfg {Number} width
8426      * width (optional) size of component
8427      */
8428      /** @cfg {Number} height
8429      * height (optional) size of component
8430      */
8431      
8432     /**
8433      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8434      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8435      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8436      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8437      * @return {Roo.BoxComponent} this
8438      */
8439     setSize : function(w, h){
8440         // support for standard size objects
8441         if(typeof w == 'object'){
8442             h = w.height;
8443             w = w.width;
8444         }
8445         // not rendered
8446         if(!this.boxReady){
8447             this.width = w;
8448             this.height = h;
8449             return this;
8450         }
8451
8452         // prevent recalcs when not needed
8453         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8454             return this;
8455         }
8456         this.lastSize = {width: w, height: h};
8457
8458         var adj = this.adjustSize(w, h);
8459         var aw = adj.width, ah = adj.height;
8460         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8461             var rz = this.getResizeEl();
8462             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8463                 rz.setSize(aw, ah);
8464             }else if(!this.deferHeight && ah !== undefined){
8465                 rz.setHeight(ah);
8466             }else if(aw !== undefined){
8467                 rz.setWidth(aw);
8468             }
8469             this.onResize(aw, ah, w, h);
8470             this.fireEvent('resize', this, aw, ah, w, h);
8471         }
8472         return this;
8473     },
8474
8475     /**
8476      * Gets the current size of the component's underlying element.
8477      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8478      */
8479     getSize : function(){
8480         return this.el.getSize();
8481     },
8482
8483     /**
8484      * Gets the current XY position of the component's underlying element.
8485      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8486      * @return {Array} The XY position of the element (e.g., [100, 200])
8487      */
8488     getPosition : function(local){
8489         if(local === true){
8490             return [this.el.getLeft(true), this.el.getTop(true)];
8491         }
8492         return this.xy || this.el.getXY();
8493     },
8494
8495     /**
8496      * Gets the current box measurements of the component's underlying element.
8497      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8498      * @returns {Object} box An object in the format {x, y, width, height}
8499      */
8500     getBox : function(local){
8501         var s = this.el.getSize();
8502         if(local){
8503             s.x = this.el.getLeft(true);
8504             s.y = this.el.getTop(true);
8505         }else{
8506             var xy = this.xy || this.el.getXY();
8507             s.x = xy[0];
8508             s.y = xy[1];
8509         }
8510         return s;
8511     },
8512
8513     /**
8514      * Sets the current box measurements of the component's underlying element.
8515      * @param {Object} box An object in the format {x, y, width, height}
8516      * @returns {Roo.BoxComponent} this
8517      */
8518     updateBox : function(box){
8519         this.setSize(box.width, box.height);
8520         this.setPagePosition(box.x, box.y);
8521         return this;
8522     },
8523
8524     // protected
8525     getResizeEl : function(){
8526         return this.resizeEl || this.el;
8527     },
8528
8529     // protected
8530     getPositionEl : function(){
8531         return this.positionEl || this.el;
8532     },
8533
8534     /**
8535      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8536      * This method fires the move event.
8537      * @param {Number} left The new left
8538      * @param {Number} top The new top
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     setPosition : function(x, y){
8542         this.x = x;
8543         this.y = y;
8544         if(!this.boxReady){
8545             return this;
8546         }
8547         var adj = this.adjustPosition(x, y);
8548         var ax = adj.x, ay = adj.y;
8549
8550         var el = this.getPositionEl();
8551         if(ax !== undefined || ay !== undefined){
8552             if(ax !== undefined && ay !== undefined){
8553                 el.setLeftTop(ax, ay);
8554             }else if(ax !== undefined){
8555                 el.setLeft(ax);
8556             }else if(ay !== undefined){
8557                 el.setTop(ay);
8558             }
8559             this.onPosition(ax, ay);
8560             this.fireEvent('move', this, ax, ay);
8561         }
8562         return this;
8563     },
8564
8565     /**
8566      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8567      * This method fires the move event.
8568      * @param {Number} x The new x position
8569      * @param {Number} y The new y position
8570      * @returns {Roo.BoxComponent} this
8571      */
8572     setPagePosition : function(x, y){
8573         this.pageX = x;
8574         this.pageY = y;
8575         if(!this.boxReady){
8576             return;
8577         }
8578         if(x === undefined || y === undefined){ // cannot translate undefined points
8579             return;
8580         }
8581         var p = this.el.translatePoints(x, y);
8582         this.setPosition(p.left, p.top);
8583         return this;
8584     },
8585
8586     // private
8587     onRender : function(ct, position){
8588         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8589         if(this.resizeEl){
8590             this.resizeEl = Roo.get(this.resizeEl);
8591         }
8592         if(this.positionEl){
8593             this.positionEl = Roo.get(this.positionEl);
8594         }
8595     },
8596
8597     // private
8598     afterRender : function(){
8599         Roo.BoxComponent.superclass.afterRender.call(this);
8600         this.boxReady = true;
8601         this.setSize(this.width, this.height);
8602         if(this.x || this.y){
8603             this.setPosition(this.x, this.y);
8604         }
8605         if(this.pageX || this.pageY){
8606             this.setPagePosition(this.pageX, this.pageY);
8607         }
8608     },
8609
8610     /**
8611      * Force the component's size to recalculate based on the underlying element's current height and width.
8612      * @returns {Roo.BoxComponent} this
8613      */
8614     syncSize : function(){
8615         delete this.lastSize;
8616         this.setSize(this.el.getWidth(), this.el.getHeight());
8617         return this;
8618     },
8619
8620     /**
8621      * Called after the component is resized, this method is empty by default but can be implemented by any
8622      * subclass that needs to perform custom logic after a resize occurs.
8623      * @param {Number} adjWidth The box-adjusted width that was set
8624      * @param {Number} adjHeight The box-adjusted height that was set
8625      * @param {Number} rawWidth The width that was originally specified
8626      * @param {Number} rawHeight The height that was originally specified
8627      */
8628     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8629
8630     },
8631
8632     /**
8633      * Called after the component is moved, this method is empty by default but can be implemented by any
8634      * subclass that needs to perform custom logic after a move occurs.
8635      * @param {Number} x The new x position
8636      * @param {Number} y The new y position
8637      */
8638     onPosition : function(x, y){
8639
8640     },
8641
8642     // private
8643     adjustSize : function(w, h){
8644         if(this.autoWidth){
8645             w = 'auto';
8646         }
8647         if(this.autoHeight){
8648             h = 'auto';
8649         }
8650         return {width : w, height: h};
8651     },
8652
8653     // private
8654     adjustPosition : function(x, y){
8655         return {x : x, y: y};
8656     }
8657 });/*
8658  * Based on:
8659  * Ext JS Library 1.1.1
8660  * Copyright(c) 2006-2007, Ext JS, LLC.
8661  *
8662  * Originally Released Under LGPL - original licence link has changed is not relivant.
8663  *
8664  * Fork - LGPL
8665  * <script type="text/javascript">
8666  */
8667
8668
8669 /**
8670  * @class Roo.SplitBar
8671  * @extends Roo.util.Observable
8672  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8673  * <br><br>
8674  * Usage:
8675  * <pre><code>
8676 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8677                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8678 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8679 split.minSize = 100;
8680 split.maxSize = 600;
8681 split.animate = true;
8682 split.on('moved', splitterMoved);
8683 </code></pre>
8684  * @constructor
8685  * Create a new SplitBar
8686  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8687  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8688  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8689  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8690                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8691                         position of the SplitBar).
8692  */
8693 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8694     
8695     /** @private */
8696     this.el = Roo.get(dragElement, true);
8697     this.el.dom.unselectable = "on";
8698     /** @private */
8699     this.resizingEl = Roo.get(resizingElement, true);
8700
8701     /**
8702      * @private
8703      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8704      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8705      * @type Number
8706      */
8707     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8708     
8709     /**
8710      * The minimum size of the resizing element. (Defaults to 0)
8711      * @type Number
8712      */
8713     this.minSize = 0;
8714     
8715     /**
8716      * The maximum size of the resizing element. (Defaults to 2000)
8717      * @type Number
8718      */
8719     this.maxSize = 2000;
8720     
8721     /**
8722      * Whether to animate the transition to the new size
8723      * @type Boolean
8724      */
8725     this.animate = false;
8726     
8727     /**
8728      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8729      * @type Boolean
8730      */
8731     this.useShim = false;
8732     
8733     /** @private */
8734     this.shim = null;
8735     
8736     if(!existingProxy){
8737         /** @private */
8738         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8739     }else{
8740         this.proxy = Roo.get(existingProxy).dom;
8741     }
8742     /** @private */
8743     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8744     
8745     /** @private */
8746     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8747     
8748     /** @private */
8749     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8750     
8751     /** @private */
8752     this.dragSpecs = {};
8753     
8754     /**
8755      * @private The adapter to use to positon and resize elements
8756      */
8757     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8758     this.adapter.init(this);
8759     
8760     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8761         /** @private */
8762         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8763         this.el.addClass("x-splitbar-h");
8764     }else{
8765         /** @private */
8766         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8767         this.el.addClass("x-splitbar-v");
8768     }
8769     
8770     this.addEvents({
8771         /**
8772          * @event resize
8773          * Fires when the splitter is moved (alias for {@link #event-moved})
8774          * @param {Roo.SplitBar} this
8775          * @param {Number} newSize the new width or height
8776          */
8777         "resize" : true,
8778         /**
8779          * @event moved
8780          * Fires when the splitter is moved
8781          * @param {Roo.SplitBar} this
8782          * @param {Number} newSize the new width or height
8783          */
8784         "moved" : true,
8785         /**
8786          * @event beforeresize
8787          * Fires before the splitter is dragged
8788          * @param {Roo.SplitBar} this
8789          */
8790         "beforeresize" : true,
8791
8792         "beforeapply" : true
8793     });
8794
8795     Roo.util.Observable.call(this);
8796 };
8797
8798 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8799     onStartProxyDrag : function(x, y){
8800         this.fireEvent("beforeresize", this);
8801         if(!this.overlay){
8802             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8803             o.unselectable();
8804             o.enableDisplayMode("block");
8805             // all splitbars share the same overlay
8806             Roo.SplitBar.prototype.overlay = o;
8807         }
8808         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8809         this.overlay.show();
8810         Roo.get(this.proxy).setDisplayed("block");
8811         var size = this.adapter.getElementSize(this);
8812         this.activeMinSize = this.getMinimumSize();;
8813         this.activeMaxSize = this.getMaximumSize();;
8814         var c1 = size - this.activeMinSize;
8815         var c2 = Math.max(this.activeMaxSize - size, 0);
8816         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8817             this.dd.resetConstraints();
8818             this.dd.setXConstraint(
8819                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8820                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8821             );
8822             this.dd.setYConstraint(0, 0);
8823         }else{
8824             this.dd.resetConstraints();
8825             this.dd.setXConstraint(0, 0);
8826             this.dd.setYConstraint(
8827                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8828                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8829             );
8830          }
8831         this.dragSpecs.startSize = size;
8832         this.dragSpecs.startPoint = [x, y];
8833         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8834     },
8835     
8836     /** 
8837      * @private Called after the drag operation by the DDProxy
8838      */
8839     onEndProxyDrag : function(e){
8840         Roo.get(this.proxy).setDisplayed(false);
8841         var endPoint = Roo.lib.Event.getXY(e);
8842         if(this.overlay){
8843             this.overlay.hide();
8844         }
8845         var newSize;
8846         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8847             newSize = this.dragSpecs.startSize + 
8848                 (this.placement == Roo.SplitBar.LEFT ?
8849                     endPoint[0] - this.dragSpecs.startPoint[0] :
8850                     this.dragSpecs.startPoint[0] - endPoint[0]
8851                 );
8852         }else{
8853             newSize = this.dragSpecs.startSize + 
8854                 (this.placement == Roo.SplitBar.TOP ?
8855                     endPoint[1] - this.dragSpecs.startPoint[1] :
8856                     this.dragSpecs.startPoint[1] - endPoint[1]
8857                 );
8858         }
8859         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8860         if(newSize != this.dragSpecs.startSize){
8861             if(this.fireEvent('beforeapply', this, newSize) !== false){
8862                 this.adapter.setElementSize(this, newSize);
8863                 this.fireEvent("moved", this, newSize);
8864                 this.fireEvent("resize", this, newSize);
8865             }
8866         }
8867     },
8868     
8869     /**
8870      * Get the adapter this SplitBar uses
8871      * @return The adapter object
8872      */
8873     getAdapter : function(){
8874         return this.adapter;
8875     },
8876     
8877     /**
8878      * Set the adapter this SplitBar uses
8879      * @param {Object} adapter A SplitBar adapter object
8880      */
8881     setAdapter : function(adapter){
8882         this.adapter = adapter;
8883         this.adapter.init(this);
8884     },
8885     
8886     /**
8887      * Gets the minimum size for the resizing element
8888      * @return {Number} The minimum size
8889      */
8890     getMinimumSize : function(){
8891         return this.minSize;
8892     },
8893     
8894     /**
8895      * Sets the minimum size for the resizing element
8896      * @param {Number} minSize The minimum size
8897      */
8898     setMinimumSize : function(minSize){
8899         this.minSize = minSize;
8900     },
8901     
8902     /**
8903      * Gets the maximum size for the resizing element
8904      * @return {Number} The maximum size
8905      */
8906     getMaximumSize : function(){
8907         return this.maxSize;
8908     },
8909     
8910     /**
8911      * Sets the maximum size for the resizing element
8912      * @param {Number} maxSize The maximum size
8913      */
8914     setMaximumSize : function(maxSize){
8915         this.maxSize = maxSize;
8916     },
8917     
8918     /**
8919      * Sets the initialize size for the resizing element
8920      * @param {Number} size The initial size
8921      */
8922     setCurrentSize : function(size){
8923         var oldAnimate = this.animate;
8924         this.animate = false;
8925         this.adapter.setElementSize(this, size);
8926         this.animate = oldAnimate;
8927     },
8928     
8929     /**
8930      * Destroy this splitbar. 
8931      * @param {Boolean} removeEl True to remove the element
8932      */
8933     destroy : function(removeEl){
8934         if(this.shim){
8935             this.shim.remove();
8936         }
8937         this.dd.unreg();
8938         this.proxy.parentNode.removeChild(this.proxy);
8939         if(removeEl){
8940             this.el.remove();
8941         }
8942     }
8943 });
8944
8945 /**
8946  * @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.
8947  */
8948 Roo.SplitBar.createProxy = function(dir){
8949     var proxy = new Roo.Element(document.createElement("div"));
8950     proxy.unselectable();
8951     var cls = 'x-splitbar-proxy';
8952     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8953     document.body.appendChild(proxy.dom);
8954     return proxy.dom;
8955 };
8956
8957 /** 
8958  * @class Roo.SplitBar.BasicLayoutAdapter
8959  * Default Adapter. It assumes the splitter and resizing element are not positioned
8960  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8961  */
8962 Roo.SplitBar.BasicLayoutAdapter = function(){
8963 };
8964
8965 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8966     // do nothing for now
8967     init : function(s){
8968     
8969     },
8970     /**
8971      * Called before drag operations to get the current size of the resizing element. 
8972      * @param {Roo.SplitBar} s The SplitBar using this adapter
8973      */
8974      getElementSize : function(s){
8975         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8976             return s.resizingEl.getWidth();
8977         }else{
8978             return s.resizingEl.getHeight();
8979         }
8980     },
8981     
8982     /**
8983      * Called after drag operations to set the size of the resizing element.
8984      * @param {Roo.SplitBar} s The SplitBar using this adapter
8985      * @param {Number} newSize The new size to set
8986      * @param {Function} onComplete A function to be invoked when resizing is complete
8987      */
8988     setElementSize : function(s, newSize, onComplete){
8989         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990             if(!s.animate){
8991                 s.resizingEl.setWidth(newSize);
8992                 if(onComplete){
8993                     onComplete(s, newSize);
8994                 }
8995             }else{
8996                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8997             }
8998         }else{
8999             
9000             if(!s.animate){
9001                 s.resizingEl.setHeight(newSize);
9002                 if(onComplete){
9003                     onComplete(s, newSize);
9004                 }
9005             }else{
9006                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9007             }
9008         }
9009     }
9010 };
9011
9012 /** 
9013  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9014  * @extends Roo.SplitBar.BasicLayoutAdapter
9015  * Adapter that  moves the splitter element to align with the resized sizing element. 
9016  * Used with an absolute positioned SplitBar.
9017  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9018  * document.body, make sure you assign an id to the body element.
9019  */
9020 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9021     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9022     this.container = Roo.get(container);
9023 };
9024
9025 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9026     init : function(s){
9027         this.basic.init(s);
9028     },
9029     
9030     getElementSize : function(s){
9031         return this.basic.getElementSize(s);
9032     },
9033     
9034     setElementSize : function(s, newSize, onComplete){
9035         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9036     },
9037     
9038     moveSplitter : function(s){
9039         var yes = Roo.SplitBar;
9040         switch(s.placement){
9041             case yes.LEFT:
9042                 s.el.setX(s.resizingEl.getRight());
9043                 break;
9044             case yes.RIGHT:
9045                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9046                 break;
9047             case yes.TOP:
9048                 s.el.setY(s.resizingEl.getBottom());
9049                 break;
9050             case yes.BOTTOM:
9051                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9052                 break;
9053         }
9054     }
9055 };
9056
9057 /**
9058  * Orientation constant - Create a vertical SplitBar
9059  * @static
9060  * @type Number
9061  */
9062 Roo.SplitBar.VERTICAL = 1;
9063
9064 /**
9065  * Orientation constant - Create a horizontal SplitBar
9066  * @static
9067  * @type Number
9068  */
9069 Roo.SplitBar.HORIZONTAL = 2;
9070
9071 /**
9072  * Placement constant - The resizing element is to the left of the splitter element
9073  * @static
9074  * @type Number
9075  */
9076 Roo.SplitBar.LEFT = 1;
9077
9078 /**
9079  * Placement constant - The resizing element is to the right of the splitter element
9080  * @static
9081  * @type Number
9082  */
9083 Roo.SplitBar.RIGHT = 2;
9084
9085 /**
9086  * Placement constant - The resizing element is positioned above the splitter element
9087  * @static
9088  * @type Number
9089  */
9090 Roo.SplitBar.TOP = 3;
9091
9092 /**
9093  * Placement constant - The resizing element is positioned under splitter element
9094  * @static
9095  * @type Number
9096  */
9097 Roo.SplitBar.BOTTOM = 4;
9098 /*
9099  * Based on:
9100  * Ext JS Library 1.1.1
9101  * Copyright(c) 2006-2007, Ext JS, LLC.
9102  *
9103  * Originally Released Under LGPL - original licence link has changed is not relivant.
9104  *
9105  * Fork - LGPL
9106  * <script type="text/javascript">
9107  */
9108
9109 /**
9110  * @class Roo.View
9111  * @extends Roo.util.Observable
9112  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9113  * This class also supports single and multi selection modes. <br>
9114  * Create a data model bound view:
9115  <pre><code>
9116  var store = new Roo.data.Store(...);
9117
9118  var view = new Roo.View({
9119     el : "my-element",
9120     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9121  
9122     singleSelect: true,
9123     selectedClass: "ydataview-selected",
9124     store: store
9125  });
9126
9127  // listen for node click?
9128  view.on("click", function(vw, index, node, e){
9129  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9130  });
9131
9132  // load XML data
9133  dataModel.load("foobar.xml");
9134  </code></pre>
9135  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9136  * <br><br>
9137  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9138  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9139  * 
9140  * Note: old style constructor is still suported (container, template, config)
9141  * 
9142  * @constructor
9143  * Create a new View
9144  * @param {Object} config The config object
9145  * 
9146  */
9147 Roo.View = function(config, depreciated_tpl, depreciated_config){
9148     
9149     if (typeof(depreciated_tpl) == 'undefined') {
9150         // new way.. - universal constructor.
9151         Roo.apply(this, config);
9152         this.el  = Roo.get(this.el);
9153     } else {
9154         // old format..
9155         this.el  = Roo.get(config);
9156         this.tpl = depreciated_tpl;
9157         Roo.apply(this, depreciated_config);
9158     }
9159      
9160     
9161     if(typeof(this.tpl) == "string"){
9162         this.tpl = new Roo.Template(this.tpl);
9163     } else {
9164         // support xtype ctors..
9165         this.tpl = new Roo.factory(this.tpl, Roo);
9166     }
9167     
9168     
9169     this.tpl.compile();
9170    
9171
9172      
9173     /** @private */
9174     this.addEvents({
9175         /**
9176          * @event beforeclick
9177          * Fires before a click is processed. Returns false to cancel the default action.
9178          * @param {Roo.View} this
9179          * @param {Number} index The index of the target node
9180          * @param {HTMLElement} node The target node
9181          * @param {Roo.EventObject} e The raw event object
9182          */
9183             "beforeclick" : true,
9184         /**
9185          * @event click
9186          * Fires when a template node is clicked.
9187          * @param {Roo.View} this
9188          * @param {Number} index The index of the target node
9189          * @param {HTMLElement} node The target node
9190          * @param {Roo.EventObject} e The raw event object
9191          */
9192             "click" : true,
9193         /**
9194          * @event dblclick
9195          * Fires when a template node is double clicked.
9196          * @param {Roo.View} this
9197          * @param {Number} index The index of the target node
9198          * @param {HTMLElement} node The target node
9199          * @param {Roo.EventObject} e The raw event object
9200          */
9201             "dblclick" : true,
9202         /**
9203          * @event contextmenu
9204          * Fires when a template node is right clicked.
9205          * @param {Roo.View} this
9206          * @param {Number} index The index of the target node
9207          * @param {HTMLElement} node The target node
9208          * @param {Roo.EventObject} e The raw event object
9209          */
9210             "contextmenu" : true,
9211         /**
9212          * @event selectionchange
9213          * Fires when the selected nodes change.
9214          * @param {Roo.View} this
9215          * @param {Array} selections Array of the selected nodes
9216          */
9217             "selectionchange" : true,
9218     
9219         /**
9220          * @event beforeselect
9221          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9222          * @param {Roo.View} this
9223          * @param {HTMLElement} node The node to be selected
9224          * @param {Array} selections Array of currently selected nodes
9225          */
9226             "beforeselect" : true,
9227         /**
9228          * @event preparedata
9229          * Fires on every row to render, to allow you to change the data.
9230          * @param {Roo.View} this
9231          * @param {Object} data to be rendered (change this)
9232          */
9233           "preparedata" : true
9234         });
9235
9236     this.el.on({
9237         "click": this.onClick,
9238         "dblclick": this.onDblClick,
9239         "contextmenu": this.onContextMenu,
9240         scope:this
9241     });
9242
9243     this.selections = [];
9244     this.nodes = [];
9245     this.cmp = new Roo.CompositeElementLite([]);
9246     if(this.store){
9247         this.store = Roo.factory(this.store, Roo.data);
9248         this.setStore(this.store, true);
9249     }
9250     Roo.View.superclass.constructor.call(this);
9251 };
9252
9253 Roo.extend(Roo.View, Roo.util.Observable, {
9254     
9255      /**
9256      * @cfg {Roo.data.Store} store Data store to load data from.
9257      */
9258     store : false,
9259     
9260     /**
9261      * @cfg {String|Roo.Element} el The container element.
9262      */
9263     el : '',
9264     
9265     /**
9266      * @cfg {String|Roo.Template} tpl The template used by this View 
9267      */
9268     tpl : false,
9269     /**
9270      * @cfg {String} dataName the named area of the template to use as the data area
9271      *                          Works with domtemplates roo-name="name"
9272      */
9273     dataName: false,
9274     /**
9275      * @cfg {String} selectedClass The css class to add to selected nodes
9276      */
9277     selectedClass : "x-view-selected",
9278      /**
9279      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9280      */
9281     emptyText : "",
9282     /**
9283      * @cfg {Boolean} multiSelect Allow multiple selection
9284      */
9285     multiSelect : false,
9286     /**
9287      * @cfg {Boolean} singleSelect Allow single selection
9288      */
9289     singleSelect:  false,
9290     
9291     /**
9292      * @cfg {Boolean} toggleSelect - selecting 
9293      */
9294     toggleSelect : false,
9295     
9296     /**
9297      * Returns the element this view is bound to.
9298      * @return {Roo.Element}
9299      */
9300     getEl : function(){
9301         return this.el;
9302     },
9303
9304     /**
9305      * Refreshes the view.
9306      */
9307     refresh : function(){
9308         var t = this.tpl;
9309         
9310         // if we are using something like 'domtemplate', then
9311         // the what gets used is:
9312         // t.applySubtemplate(NAME, data, wrapping data..)
9313         // the outer template then get' applied with
9314         //     the store 'extra data'
9315         // and the body get's added to the
9316         //      roo-name="data" node?
9317         //      <span class='roo-tpl-{name}'></span> ?????
9318         
9319         
9320         
9321         this.clearSelections();
9322         this.el.update("");
9323         var html = [];
9324         var records = this.store.getRange();
9325         if(records.length < 1) {
9326             
9327             // is this valid??  = should it render a template??
9328             
9329             this.el.update(this.emptyText);
9330             return;
9331         }
9332         var el = this.el;
9333         if (this.dataName) {
9334             this.el.update(t.apply(this.store.meta)); //????
9335             el = this.el.child('.roo-tpl-' + this.dataName);
9336         }
9337         
9338         for(var i = 0, len = records.length; i < len; i++){
9339             var data = this.prepareData(records[i].data, i, records[i]);
9340             this.fireEvent("preparedata", this, data, i, records[i]);
9341             html[html.length] = Roo.util.Format.trim(
9342                 this.dataName ?
9343                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9344                     t.apply(data)
9345             );
9346         }
9347         
9348         
9349         
9350         el.update(html.join(""));
9351         this.nodes = el.dom.childNodes;
9352         this.updateIndexes(0);
9353     },
9354
9355     /**
9356      * Function to override to reformat the data that is sent to
9357      * the template for each node.
9358      * DEPRICATED - use the preparedata event handler.
9359      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9360      * a JSON object for an UpdateManager bound view).
9361      */
9362     prepareData : function(data, index, record)
9363     {
9364         this.fireEvent("preparedata", this, data, index, record);
9365         return data;
9366     },
9367
9368     onUpdate : function(ds, record){
9369         this.clearSelections();
9370         var index = this.store.indexOf(record);
9371         var n = this.nodes[index];
9372         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9373         n.parentNode.removeChild(n);
9374         this.updateIndexes(index, index);
9375     },
9376
9377     
9378     
9379 // --------- FIXME     
9380     onAdd : function(ds, records, index)
9381     {
9382         this.clearSelections();
9383         if(this.nodes.length == 0){
9384             this.refresh();
9385             return;
9386         }
9387         var n = this.nodes[index];
9388         for(var i = 0, len = records.length; i < len; i++){
9389             var d = this.prepareData(records[i].data, i, records[i]);
9390             if(n){
9391                 this.tpl.insertBefore(n, d);
9392             }else{
9393                 
9394                 this.tpl.append(this.el, d);
9395             }
9396         }
9397         this.updateIndexes(index);
9398     },
9399
9400     onRemove : function(ds, record, index){
9401         this.clearSelections();
9402         var el = this.dataName  ?
9403             this.el.child('.roo-tpl-' + this.dataName) :
9404             this.el; 
9405         el.dom.removeChild(this.nodes[index]);
9406         this.updateIndexes(index);
9407     },
9408
9409     /**
9410      * Refresh an individual node.
9411      * @param {Number} index
9412      */
9413     refreshNode : function(index){
9414         this.onUpdate(this.store, this.store.getAt(index));
9415     },
9416
9417     updateIndexes : function(startIndex, endIndex){
9418         var ns = this.nodes;
9419         startIndex = startIndex || 0;
9420         endIndex = endIndex || ns.length - 1;
9421         for(var i = startIndex; i <= endIndex; i++){
9422             ns[i].nodeIndex = i;
9423         }
9424     },
9425
9426     /**
9427      * Changes the data store this view uses and refresh the view.
9428      * @param {Store} store
9429      */
9430     setStore : function(store, initial){
9431         if(!initial && this.store){
9432             this.store.un("datachanged", this.refresh);
9433             this.store.un("add", this.onAdd);
9434             this.store.un("remove", this.onRemove);
9435             this.store.un("update", this.onUpdate);
9436             this.store.un("clear", this.refresh);
9437         }
9438         if(store){
9439           
9440             store.on("datachanged", this.refresh, this);
9441             store.on("add", this.onAdd, this);
9442             store.on("remove", this.onRemove, this);
9443             store.on("update", this.onUpdate, this);
9444             store.on("clear", this.refresh, this);
9445         }
9446         
9447         if(store){
9448             this.refresh();
9449         }
9450     },
9451
9452     /**
9453      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9454      * @param {HTMLElement} node
9455      * @return {HTMLElement} The template node
9456      */
9457     findItemFromChild : function(node){
9458         var el = this.dataName  ?
9459             this.el.child('.roo-tpl-' + this.dataName,true) :
9460             this.el.dom; 
9461         
9462         if(!node || node.parentNode == el){
9463                     return node;
9464             }
9465             var p = node.parentNode;
9466             while(p && p != el){
9467             if(p.parentNode == el){
9468                 return p;
9469             }
9470             p = p.parentNode;
9471         }
9472             return null;
9473     },
9474
9475     /** @ignore */
9476     onClick : function(e){
9477         var item = this.findItemFromChild(e.getTarget());
9478         if(item){
9479             var index = this.indexOf(item);
9480             if(this.onItemClick(item, index, e) !== false){
9481                 this.fireEvent("click", this, index, item, e);
9482             }
9483         }else{
9484             this.clearSelections();
9485         }
9486     },
9487
9488     /** @ignore */
9489     onContextMenu : function(e){
9490         var item = this.findItemFromChild(e.getTarget());
9491         if(item){
9492             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9493         }
9494     },
9495
9496     /** @ignore */
9497     onDblClick : function(e){
9498         var item = this.findItemFromChild(e.getTarget());
9499         if(item){
9500             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9501         }
9502     },
9503
9504     onItemClick : function(item, index, e)
9505     {
9506         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9507             return false;
9508         }
9509         if (this.toggleSelect) {
9510             var m = this.isSelected(item) ? 'unselect' : 'select';
9511             Roo.log(m);
9512             var _t = this;
9513             _t[m](item, true, false);
9514             return true;
9515         }
9516         if(this.multiSelect || this.singleSelect){
9517             if(this.multiSelect && e.shiftKey && this.lastSelection){
9518                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9519             }else{
9520                 this.select(item, this.multiSelect && e.ctrlKey);
9521                 this.lastSelection = item;
9522             }
9523             e.preventDefault();
9524         }
9525         return true;
9526     },
9527
9528     /**
9529      * Get the number of selected nodes.
9530      * @return {Number}
9531      */
9532     getSelectionCount : function(){
9533         return this.selections.length;
9534     },
9535
9536     /**
9537      * Get the currently selected nodes.
9538      * @return {Array} An array of HTMLElements
9539      */
9540     getSelectedNodes : function(){
9541         return this.selections;
9542     },
9543
9544     /**
9545      * Get the indexes of the selected nodes.
9546      * @return {Array}
9547      */
9548     getSelectedIndexes : function(){
9549         var indexes = [], s = this.selections;
9550         for(var i = 0, len = s.length; i < len; i++){
9551             indexes.push(s[i].nodeIndex);
9552         }
9553         return indexes;
9554     },
9555
9556     /**
9557      * Clear all selections
9558      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9559      */
9560     clearSelections : function(suppressEvent){
9561         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9562             this.cmp.elements = this.selections;
9563             this.cmp.removeClass(this.selectedClass);
9564             this.selections = [];
9565             if(!suppressEvent){
9566                 this.fireEvent("selectionchange", this, this.selections);
9567             }
9568         }
9569     },
9570
9571     /**
9572      * Returns true if the passed node is selected
9573      * @param {HTMLElement/Number} node The node or node index
9574      * @return {Boolean}
9575      */
9576     isSelected : function(node){
9577         var s = this.selections;
9578         if(s.length < 1){
9579             return false;
9580         }
9581         node = this.getNode(node);
9582         return s.indexOf(node) !== -1;
9583     },
9584
9585     /**
9586      * Selects nodes.
9587      * @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
9588      * @param {Boolean} keepExisting (optional) true to keep existing selections
9589      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9590      */
9591     select : function(nodeInfo, keepExisting, suppressEvent){
9592         if(nodeInfo instanceof Array){
9593             if(!keepExisting){
9594                 this.clearSelections(true);
9595             }
9596             for(var i = 0, len = nodeInfo.length; i < len; i++){
9597                 this.select(nodeInfo[i], true, true);
9598             }
9599             return;
9600         } 
9601         var node = this.getNode(nodeInfo);
9602         if(!node || this.isSelected(node)){
9603             return; // already selected.
9604         }
9605         if(!keepExisting){
9606             this.clearSelections(true);
9607         }
9608         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9609             Roo.fly(node).addClass(this.selectedClass);
9610             this.selections.push(node);
9611             if(!suppressEvent){
9612                 this.fireEvent("selectionchange", this, this.selections);
9613             }
9614         }
9615         
9616         
9617     },
9618       /**
9619      * Unselects nodes.
9620      * @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
9621      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9623      */
9624     unselect : function(nodeInfo, keepExisting, suppressEvent)
9625     {
9626         if(nodeInfo instanceof Array){
9627             Roo.each(this.selections, function(s) {
9628                 this.unselect(s, nodeInfo);
9629             }, this);
9630             return;
9631         }
9632         var node = this.getNode(nodeInfo);
9633         if(!node || !this.isSelected(node)){
9634             Roo.log("not selected");
9635             return; // not selected.
9636         }
9637         // fireevent???
9638         var ns = [];
9639         Roo.each(this.selections, function(s) {
9640             if (s == node ) {
9641                 Roo.fly(node).removeClass(this.selectedClass);
9642
9643                 return;
9644             }
9645             ns.push(s);
9646         },this);
9647         
9648         this.selections= ns;
9649         this.fireEvent("selectionchange", this, this.selections);
9650     },
9651
9652     /**
9653      * Gets a template node.
9654      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9655      * @return {HTMLElement} The node or null if it wasn't found
9656      */
9657     getNode : function(nodeInfo){
9658         if(typeof nodeInfo == "string"){
9659             return document.getElementById(nodeInfo);
9660         }else if(typeof nodeInfo == "number"){
9661             return this.nodes[nodeInfo];
9662         }
9663         return nodeInfo;
9664     },
9665
9666     /**
9667      * Gets a range template nodes.
9668      * @param {Number} startIndex
9669      * @param {Number} endIndex
9670      * @return {Array} An array of nodes
9671      */
9672     getNodes : function(start, end){
9673         var ns = this.nodes;
9674         start = start || 0;
9675         end = typeof end == "undefined" ? ns.length - 1 : end;
9676         var nodes = [];
9677         if(start <= end){
9678             for(var i = start; i <= end; i++){
9679                 nodes.push(ns[i]);
9680             }
9681         } else{
9682             for(var i = start; i >= end; i--){
9683                 nodes.push(ns[i]);
9684             }
9685         }
9686         return nodes;
9687     },
9688
9689     /**
9690      * Finds the index of the passed node
9691      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9692      * @return {Number} The index of the node or -1
9693      */
9694     indexOf : function(node){
9695         node = this.getNode(node);
9696         if(typeof node.nodeIndex == "number"){
9697             return node.nodeIndex;
9698         }
9699         var ns = this.nodes;
9700         for(var i = 0, len = ns.length; i < len; i++){
9701             if(ns[i] == node){
9702                 return i;
9703             }
9704         }
9705         return -1;
9706     }
9707 });
9708 /*
9709  * Based on:
9710  * Ext JS Library 1.1.1
9711  * Copyright(c) 2006-2007, Ext JS, LLC.
9712  *
9713  * Originally Released Under LGPL - original licence link has changed is not relivant.
9714  *
9715  * Fork - LGPL
9716  * <script type="text/javascript">
9717  */
9718
9719 /**
9720  * @class Roo.JsonView
9721  * @extends Roo.View
9722  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9723 <pre><code>
9724 var view = new Roo.JsonView({
9725     container: "my-element",
9726     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9727     multiSelect: true, 
9728     jsonRoot: "data" 
9729 });
9730
9731 // listen for node click?
9732 view.on("click", function(vw, index, node, e){
9733     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9734 });
9735
9736 // direct load of JSON data
9737 view.load("foobar.php");
9738
9739 // Example from my blog list
9740 var tpl = new Roo.Template(
9741     '&lt;div class="entry"&gt;' +
9742     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9743     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9744     "&lt;/div&gt;&lt;hr /&gt;"
9745 );
9746
9747 var moreView = new Roo.JsonView({
9748     container :  "entry-list", 
9749     template : tpl,
9750     jsonRoot: "posts"
9751 });
9752 moreView.on("beforerender", this.sortEntries, this);
9753 moreView.load({
9754     url: "/blog/get-posts.php",
9755     params: "allposts=true",
9756     text: "Loading Blog Entries..."
9757 });
9758 </code></pre>
9759
9760 * Note: old code is supported with arguments : (container, template, config)
9761
9762
9763  * @constructor
9764  * Create a new JsonView
9765  * 
9766  * @param {Object} config The config object
9767  * 
9768  */
9769 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9770     
9771     
9772     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9773
9774     var um = this.el.getUpdateManager();
9775     um.setRenderer(this);
9776     um.on("update", this.onLoad, this);
9777     um.on("failure", this.onLoadException, this);
9778
9779     /**
9780      * @event beforerender
9781      * Fires before rendering of the downloaded JSON data.
9782      * @param {Roo.JsonView} this
9783      * @param {Object} data The JSON data loaded
9784      */
9785     /**
9786      * @event load
9787      * Fires when data is loaded.
9788      * @param {Roo.JsonView} this
9789      * @param {Object} data The JSON data loaded
9790      * @param {Object} response The raw Connect response object
9791      */
9792     /**
9793      * @event loadexception
9794      * Fires when loading fails.
9795      * @param {Roo.JsonView} this
9796      * @param {Object} response The raw Connect response object
9797      */
9798     this.addEvents({
9799         'beforerender' : true,
9800         'load' : true,
9801         'loadexception' : true
9802     });
9803 };
9804 Roo.extend(Roo.JsonView, Roo.View, {
9805     /**
9806      * @type {String} The root property in the loaded JSON object that contains the data
9807      */
9808     jsonRoot : "",
9809
9810     /**
9811      * Refreshes the view.
9812      */
9813     refresh : function(){
9814         this.clearSelections();
9815         this.el.update("");
9816         var html = [];
9817         var o = this.jsonData;
9818         if(o && o.length > 0){
9819             for(var i = 0, len = o.length; i < len; i++){
9820                 var data = this.prepareData(o[i], i, o);
9821                 html[html.length] = this.tpl.apply(data);
9822             }
9823         }else{
9824             html.push(this.emptyText);
9825         }
9826         this.el.update(html.join(""));
9827         this.nodes = this.el.dom.childNodes;
9828         this.updateIndexes(0);
9829     },
9830
9831     /**
9832      * 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.
9833      * @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:
9834      <pre><code>
9835      view.load({
9836          url: "your-url.php",
9837          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9838          callback: yourFunction,
9839          scope: yourObject, //(optional scope)
9840          discardUrl: false,
9841          nocache: false,
9842          text: "Loading...",
9843          timeout: 30,
9844          scripts: false
9845      });
9846      </code></pre>
9847      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9848      * 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.
9849      * @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}
9850      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9851      * @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.
9852      */
9853     load : function(){
9854         var um = this.el.getUpdateManager();
9855         um.update.apply(um, arguments);
9856     },
9857
9858     render : function(el, response){
9859         this.clearSelections();
9860         this.el.update("");
9861         var o;
9862         try{
9863             o = Roo.util.JSON.decode(response.responseText);
9864             if(this.jsonRoot){
9865                 
9866                 o = o[this.jsonRoot];
9867             }
9868         } catch(e){
9869         }
9870         /**
9871          * The current JSON data or null
9872          */
9873         this.jsonData = o;
9874         this.beforeRender();
9875         this.refresh();
9876     },
9877
9878 /**
9879  * Get the number of records in the current JSON dataset
9880  * @return {Number}
9881  */
9882     getCount : function(){
9883         return this.jsonData ? this.jsonData.length : 0;
9884     },
9885
9886 /**
9887  * Returns the JSON object for the specified node(s)
9888  * @param {HTMLElement/Array} node The node or an array of nodes
9889  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9890  * you get the JSON object for the node
9891  */
9892     getNodeData : function(node){
9893         if(node instanceof Array){
9894             var data = [];
9895             for(var i = 0, len = node.length; i < len; i++){
9896                 data.push(this.getNodeData(node[i]));
9897             }
9898             return data;
9899         }
9900         return this.jsonData[this.indexOf(node)] || null;
9901     },
9902
9903     beforeRender : function(){
9904         this.snapshot = this.jsonData;
9905         if(this.sortInfo){
9906             this.sort.apply(this, this.sortInfo);
9907         }
9908         this.fireEvent("beforerender", this, this.jsonData);
9909     },
9910
9911     onLoad : function(el, o){
9912         this.fireEvent("load", this, this.jsonData, o);
9913     },
9914
9915     onLoadException : function(el, o){
9916         this.fireEvent("loadexception", this, o);
9917     },
9918
9919 /**
9920  * Filter the data by a specific property.
9921  * @param {String} property A property on your JSON objects
9922  * @param {String/RegExp} value Either string that the property values
9923  * should start with, or a RegExp to test against the property
9924  */
9925     filter : function(property, value){
9926         if(this.jsonData){
9927             var data = [];
9928             var ss = this.snapshot;
9929             if(typeof value == "string"){
9930                 var vlen = value.length;
9931                 if(vlen == 0){
9932                     this.clearFilter();
9933                     return;
9934                 }
9935                 value = value.toLowerCase();
9936                 for(var i = 0, len = ss.length; i < len; i++){
9937                     var o = ss[i];
9938                     if(o[property].substr(0, vlen).toLowerCase() == value){
9939                         data.push(o);
9940                     }
9941                 }
9942             } else if(value.exec){ // regex?
9943                 for(var i = 0, len = ss.length; i < len; i++){
9944                     var o = ss[i];
9945                     if(value.test(o[property])){
9946                         data.push(o);
9947                     }
9948                 }
9949             } else{
9950                 return;
9951             }
9952             this.jsonData = data;
9953             this.refresh();
9954         }
9955     },
9956
9957 /**
9958  * Filter by a function. The passed function will be called with each
9959  * object in the current dataset. If the function returns true the value is kept,
9960  * otherwise it is filtered.
9961  * @param {Function} fn
9962  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9963  */
9964     filterBy : function(fn, scope){
9965         if(this.jsonData){
9966             var data = [];
9967             var ss = this.snapshot;
9968             for(var i = 0, len = ss.length; i < len; i++){
9969                 var o = ss[i];
9970                 if(fn.call(scope || this, o)){
9971                     data.push(o);
9972                 }
9973             }
9974             this.jsonData = data;
9975             this.refresh();
9976         }
9977     },
9978
9979 /**
9980  * Clears the current filter.
9981  */
9982     clearFilter : function(){
9983         if(this.snapshot && this.jsonData != this.snapshot){
9984             this.jsonData = this.snapshot;
9985             this.refresh();
9986         }
9987     },
9988
9989
9990 /**
9991  * Sorts the data for this view and refreshes it.
9992  * @param {String} property A property on your JSON objects to sort on
9993  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9994  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9995  */
9996     sort : function(property, dir, sortType){
9997         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9998         if(this.jsonData){
9999             var p = property;
10000             var dsc = dir && dir.toLowerCase() == "desc";
10001             var f = function(o1, o2){
10002                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10003                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10004                 ;
10005                 if(v1 < v2){
10006                     return dsc ? +1 : -1;
10007                 } else if(v1 > v2){
10008                     return dsc ? -1 : +1;
10009                 } else{
10010                     return 0;
10011                 }
10012             };
10013             this.jsonData.sort(f);
10014             this.refresh();
10015             if(this.jsonData != this.snapshot){
10016                 this.snapshot.sort(f);
10017             }
10018         }
10019     }
10020 });/*
10021  * Based on:
10022  * Ext JS Library 1.1.1
10023  * Copyright(c) 2006-2007, Ext JS, LLC.
10024  *
10025  * Originally Released Under LGPL - original licence link has changed is not relivant.
10026  *
10027  * Fork - LGPL
10028  * <script type="text/javascript">
10029  */
10030  
10031
10032 /**
10033  * @class Roo.ColorPalette
10034  * @extends Roo.Component
10035  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10036  * Here's an example of typical usage:
10037  * <pre><code>
10038 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10039 cp.render('my-div');
10040
10041 cp.on('select', function(palette, selColor){
10042     // do something with selColor
10043 });
10044 </code></pre>
10045  * @constructor
10046  * Create a new ColorPalette
10047  * @param {Object} config The config object
10048  */
10049 Roo.ColorPalette = function(config){
10050     Roo.ColorPalette.superclass.constructor.call(this, config);
10051     this.addEvents({
10052         /**
10053              * @event select
10054              * Fires when a color is selected
10055              * @param {ColorPalette} this
10056              * @param {String} color The 6-digit color hex code (without the # symbol)
10057              */
10058         select: true
10059     });
10060
10061     if(this.handler){
10062         this.on("select", this.handler, this.scope, true);
10063     }
10064 };
10065 Roo.extend(Roo.ColorPalette, Roo.Component, {
10066     /**
10067      * @cfg {String} itemCls
10068      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10069      */
10070     itemCls : "x-color-palette",
10071     /**
10072      * @cfg {String} value
10073      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10074      * the hex codes are case-sensitive.
10075      */
10076     value : null,
10077     clickEvent:'click',
10078     // private
10079     ctype: "Roo.ColorPalette",
10080
10081     /**
10082      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10083      */
10084     allowReselect : false,
10085
10086     /**
10087      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10088      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10089      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10090      * of colors with the width setting until the box is symmetrical.</p>
10091      * <p>You can override individual colors if needed:</p>
10092      * <pre><code>
10093 var cp = new Roo.ColorPalette();
10094 cp.colors[0] = "FF0000";  // change the first box to red
10095 </code></pre>
10096
10097 Or you can provide a custom array of your own for complete control:
10098 <pre><code>
10099 var cp = new Roo.ColorPalette();
10100 cp.colors = ["000000", "993300", "333300"];
10101 </code></pre>
10102      * @type Array
10103      */
10104     colors : [
10105         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10106         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10107         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10108         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10109         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10110     ],
10111
10112     // private
10113     onRender : function(container, position){
10114         var t = new Roo.MasterTemplate(
10115             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10116         );
10117         var c = this.colors;
10118         for(var i = 0, len = c.length; i < len; i++){
10119             t.add([c[i]]);
10120         }
10121         var el = document.createElement("div");
10122         el.className = this.itemCls;
10123         t.overwrite(el);
10124         container.dom.insertBefore(el, position);
10125         this.el = Roo.get(el);
10126         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10127         if(this.clickEvent != 'click'){
10128             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10129         }
10130     },
10131
10132     // private
10133     afterRender : function(){
10134         Roo.ColorPalette.superclass.afterRender.call(this);
10135         if(this.value){
10136             var s = this.value;
10137             this.value = null;
10138             this.select(s);
10139         }
10140     },
10141
10142     // private
10143     handleClick : function(e, t){
10144         e.preventDefault();
10145         if(!this.disabled){
10146             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10147             this.select(c.toUpperCase());
10148         }
10149     },
10150
10151     /**
10152      * Selects the specified color in the palette (fires the select event)
10153      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10154      */
10155     select : function(color){
10156         color = color.replace("#", "");
10157         if(color != this.value || this.allowReselect){
10158             var el = this.el;
10159             if(this.value){
10160                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10161             }
10162             el.child("a.color-"+color).addClass("x-color-palette-sel");
10163             this.value = color;
10164             this.fireEvent("select", this, color);
10165         }
10166     }
10167 });/*
10168  * Based on:
10169  * Ext JS Library 1.1.1
10170  * Copyright(c) 2006-2007, Ext JS, LLC.
10171  *
10172  * Originally Released Under LGPL - original licence link has changed is not relivant.
10173  *
10174  * Fork - LGPL
10175  * <script type="text/javascript">
10176  */
10177  
10178 /**
10179  * @class Roo.DatePicker
10180  * @extends Roo.Component
10181  * Simple date picker class.
10182  * @constructor
10183  * Create a new DatePicker
10184  * @param {Object} config The config object
10185  */
10186 Roo.DatePicker = function(config){
10187     Roo.DatePicker.superclass.constructor.call(this, config);
10188
10189     this.value = config && config.value ?
10190                  config.value.clearTime() : new Date().clearTime();
10191
10192     this.addEvents({
10193         /**
10194              * @event select
10195              * Fires when a date is selected
10196              * @param {DatePicker} this
10197              * @param {Date} date The selected date
10198              */
10199         'select': true,
10200         /**
10201              * @event monthchange
10202              * Fires when the displayed month changes 
10203              * @param {DatePicker} this
10204              * @param {Date} date The selected month
10205              */
10206         'monthchange': true
10207     });
10208
10209     if(this.handler){
10210         this.on("select", this.handler,  this.scope || this);
10211     }
10212     // build the disabledDatesRE
10213     if(!this.disabledDatesRE && this.disabledDates){
10214         var dd = this.disabledDates;
10215         var re = "(?:";
10216         for(var i = 0; i < dd.length; i++){
10217             re += dd[i];
10218             if(i != dd.length-1) re += "|";
10219         }
10220         this.disabledDatesRE = new RegExp(re + ")");
10221     }
10222 };
10223
10224 Roo.extend(Roo.DatePicker, Roo.Component, {
10225     /**
10226      * @cfg {String} todayText
10227      * The text to display on the button that selects the current date (defaults to "Today")
10228      */
10229     todayText : "Today",
10230     /**
10231      * @cfg {String} okText
10232      * The text to display on the ok button
10233      */
10234     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10235     /**
10236      * @cfg {String} cancelText
10237      * The text to display on the cancel button
10238      */
10239     cancelText : "Cancel",
10240     /**
10241      * @cfg {String} todayTip
10242      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10243      */
10244     todayTip : "{0} (Spacebar)",
10245     /**
10246      * @cfg {Date} minDate
10247      * Minimum allowable date (JavaScript date object, defaults to null)
10248      */
10249     minDate : null,
10250     /**
10251      * @cfg {Date} maxDate
10252      * Maximum allowable date (JavaScript date object, defaults to null)
10253      */
10254     maxDate : null,
10255     /**
10256      * @cfg {String} minText
10257      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10258      */
10259     minText : "This date is before the minimum date",
10260     /**
10261      * @cfg {String} maxText
10262      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10263      */
10264     maxText : "This date is after the maximum date",
10265     /**
10266      * @cfg {String} format
10267      * The default date format string which can be overriden for localization support.  The format must be
10268      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10269      */
10270     format : "m/d/y",
10271     /**
10272      * @cfg {Array} disabledDays
10273      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10274      */
10275     disabledDays : null,
10276     /**
10277      * @cfg {String} disabledDaysText
10278      * The tooltip to display when the date falls on a disabled day (defaults to "")
10279      */
10280     disabledDaysText : "",
10281     /**
10282      * @cfg {RegExp} disabledDatesRE
10283      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10284      */
10285     disabledDatesRE : null,
10286     /**
10287      * @cfg {String} disabledDatesText
10288      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10289      */
10290     disabledDatesText : "",
10291     /**
10292      * @cfg {Boolean} constrainToViewport
10293      * True to constrain the date picker to the viewport (defaults to true)
10294      */
10295     constrainToViewport : true,
10296     /**
10297      * @cfg {Array} monthNames
10298      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10299      */
10300     monthNames : Date.monthNames,
10301     /**
10302      * @cfg {Array} dayNames
10303      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10304      */
10305     dayNames : Date.dayNames,
10306     /**
10307      * @cfg {String} nextText
10308      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10309      */
10310     nextText: 'Next Month (Control+Right)',
10311     /**
10312      * @cfg {String} prevText
10313      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10314      */
10315     prevText: 'Previous Month (Control+Left)',
10316     /**
10317      * @cfg {String} monthYearText
10318      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10319      */
10320     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10321     /**
10322      * @cfg {Number} startDay
10323      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10324      */
10325     startDay : 0,
10326     /**
10327      * @cfg {Bool} showClear
10328      * Show a clear button (usefull for date form elements that can be blank.)
10329      */
10330     
10331     showClear: false,
10332     
10333     /**
10334      * Sets the value of the date field
10335      * @param {Date} value The date to set
10336      */
10337     setValue : function(value){
10338         var old = this.value;
10339         if (typeof(value) == 'string') {
10340             value = Date.parseDate(value, this.format);
10341         }
10342         
10343         this.value = value.clearTime(true);
10344         if(this.el){
10345             this.update(this.value);
10346         }
10347     },
10348
10349     /**
10350      * Gets the current selected value of the date field
10351      * @return {Date} The selected date
10352      */
10353     getValue : function(){
10354         return this.value;
10355     },
10356
10357     // private
10358     focus : function(){
10359         if(this.el){
10360             this.update(this.activeDate);
10361         }
10362     },
10363
10364     // private
10365     onRender : function(container, position){
10366         
10367         var m = [
10368              '<table cellspacing="0">',
10369                 '<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>',
10370                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10371         var dn = this.dayNames;
10372         for(var i = 0; i < 7; i++){
10373             var d = this.startDay+i;
10374             if(d > 6){
10375                 d = d-7;
10376             }
10377             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10378         }
10379         m[m.length] = "</tr></thead><tbody><tr>";
10380         for(var i = 0; i < 42; i++) {
10381             if(i % 7 == 0 && i != 0){
10382                 m[m.length] = "</tr><tr>";
10383             }
10384             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10385         }
10386         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10387             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10388
10389         var el = document.createElement("div");
10390         el.className = "x-date-picker";
10391         el.innerHTML = m.join("");
10392
10393         container.dom.insertBefore(el, position);
10394
10395         this.el = Roo.get(el);
10396         this.eventEl = Roo.get(el.firstChild);
10397
10398         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10399             handler: this.showPrevMonth,
10400             scope: this,
10401             preventDefault:true,
10402             stopDefault:true
10403         });
10404
10405         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10406             handler: this.showNextMonth,
10407             scope: this,
10408             preventDefault:true,
10409             stopDefault:true
10410         });
10411
10412         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10413
10414         this.monthPicker = this.el.down('div.x-date-mp');
10415         this.monthPicker.enableDisplayMode('block');
10416         
10417         var kn = new Roo.KeyNav(this.eventEl, {
10418             "left" : function(e){
10419                 e.ctrlKey ?
10420                     this.showPrevMonth() :
10421                     this.update(this.activeDate.add("d", -1));
10422             },
10423
10424             "right" : function(e){
10425                 e.ctrlKey ?
10426                     this.showNextMonth() :
10427                     this.update(this.activeDate.add("d", 1));
10428             },
10429
10430             "up" : function(e){
10431                 e.ctrlKey ?
10432                     this.showNextYear() :
10433                     this.update(this.activeDate.add("d", -7));
10434             },
10435
10436             "down" : function(e){
10437                 e.ctrlKey ?
10438                     this.showPrevYear() :
10439                     this.update(this.activeDate.add("d", 7));
10440             },
10441
10442             "pageUp" : function(e){
10443                 this.showNextMonth();
10444             },
10445
10446             "pageDown" : function(e){
10447                 this.showPrevMonth();
10448             },
10449
10450             "enter" : function(e){
10451                 e.stopPropagation();
10452                 return true;
10453             },
10454
10455             scope : this
10456         });
10457
10458         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10459
10460         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10461
10462         this.el.unselectable();
10463         
10464         this.cells = this.el.select("table.x-date-inner tbody td");
10465         this.textNodes = this.el.query("table.x-date-inner tbody span");
10466
10467         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10468             text: "&#160;",
10469             tooltip: this.monthYearText
10470         });
10471
10472         this.mbtn.on('click', this.showMonthPicker, this);
10473         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10474
10475
10476         var today = (new Date()).dateFormat(this.format);
10477         
10478         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10479         if (this.showClear) {
10480             baseTb.add( new Roo.Toolbar.Fill());
10481         }
10482         baseTb.add({
10483             text: String.format(this.todayText, today),
10484             tooltip: String.format(this.todayTip, today),
10485             handler: this.selectToday,
10486             scope: this
10487         });
10488         
10489         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10490             
10491         //});
10492         if (this.showClear) {
10493             
10494             baseTb.add( new Roo.Toolbar.Fill());
10495             baseTb.add({
10496                 text: '&#160;',
10497                 cls: 'x-btn-icon x-btn-clear',
10498                 handler: function() {
10499                     //this.value = '';
10500                     this.fireEvent("select", this, '');
10501                 },
10502                 scope: this
10503             });
10504         }
10505         
10506         
10507         if(Roo.isIE){
10508             this.el.repaint();
10509         }
10510         this.update(this.value);
10511     },
10512
10513     createMonthPicker : function(){
10514         if(!this.monthPicker.dom.firstChild){
10515             var buf = ['<table border="0" cellspacing="0">'];
10516             for(var i = 0; i < 6; i++){
10517                 buf.push(
10518                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10519                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10520                     i == 0 ?
10521                     '<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>' :
10522                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10523                 );
10524             }
10525             buf.push(
10526                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10527                     this.okText,
10528                     '</button><button type="button" class="x-date-mp-cancel">',
10529                     this.cancelText,
10530                     '</button></td></tr>',
10531                 '</table>'
10532             );
10533             this.monthPicker.update(buf.join(''));
10534             this.monthPicker.on('click', this.onMonthClick, this);
10535             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10536
10537             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10538             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10539
10540             this.mpMonths.each(function(m, a, i){
10541                 i += 1;
10542                 if((i%2) == 0){
10543                     m.dom.xmonth = 5 + Math.round(i * .5);
10544                 }else{
10545                     m.dom.xmonth = Math.round((i-1) * .5);
10546                 }
10547             });
10548         }
10549     },
10550
10551     showMonthPicker : function(){
10552         this.createMonthPicker();
10553         var size = this.el.getSize();
10554         this.monthPicker.setSize(size);
10555         this.monthPicker.child('table').setSize(size);
10556
10557         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10558         this.updateMPMonth(this.mpSelMonth);
10559         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10560         this.updateMPYear(this.mpSelYear);
10561
10562         this.monthPicker.slideIn('t', {duration:.2});
10563     },
10564
10565     updateMPYear : function(y){
10566         this.mpyear = y;
10567         var ys = this.mpYears.elements;
10568         for(var i = 1; i <= 10; i++){
10569             var td = ys[i-1], y2;
10570             if((i%2) == 0){
10571                 y2 = y + Math.round(i * .5);
10572                 td.firstChild.innerHTML = y2;
10573                 td.xyear = y2;
10574             }else{
10575                 y2 = y - (5-Math.round(i * .5));
10576                 td.firstChild.innerHTML = y2;
10577                 td.xyear = y2;
10578             }
10579             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10580         }
10581     },
10582
10583     updateMPMonth : function(sm){
10584         this.mpMonths.each(function(m, a, i){
10585             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10586         });
10587     },
10588
10589     selectMPMonth: function(m){
10590         
10591     },
10592
10593     onMonthClick : function(e, t){
10594         e.stopEvent();
10595         var el = new Roo.Element(t), pn;
10596         if(el.is('button.x-date-mp-cancel')){
10597             this.hideMonthPicker();
10598         }
10599         else if(el.is('button.x-date-mp-ok')){
10600             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10601             this.hideMonthPicker();
10602         }
10603         else if(pn = el.up('td.x-date-mp-month', 2)){
10604             this.mpMonths.removeClass('x-date-mp-sel');
10605             pn.addClass('x-date-mp-sel');
10606             this.mpSelMonth = pn.dom.xmonth;
10607         }
10608         else if(pn = el.up('td.x-date-mp-year', 2)){
10609             this.mpYears.removeClass('x-date-mp-sel');
10610             pn.addClass('x-date-mp-sel');
10611             this.mpSelYear = pn.dom.xyear;
10612         }
10613         else if(el.is('a.x-date-mp-prev')){
10614             this.updateMPYear(this.mpyear-10);
10615         }
10616         else if(el.is('a.x-date-mp-next')){
10617             this.updateMPYear(this.mpyear+10);
10618         }
10619     },
10620
10621     onMonthDblClick : function(e, t){
10622         e.stopEvent();
10623         var el = new Roo.Element(t), pn;
10624         if(pn = el.up('td.x-date-mp-month', 2)){
10625             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10626             this.hideMonthPicker();
10627         }
10628         else if(pn = el.up('td.x-date-mp-year', 2)){
10629             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10630             this.hideMonthPicker();
10631         }
10632     },
10633
10634     hideMonthPicker : function(disableAnim){
10635         if(this.monthPicker){
10636             if(disableAnim === true){
10637                 this.monthPicker.hide();
10638             }else{
10639                 this.monthPicker.slideOut('t', {duration:.2});
10640             }
10641         }
10642     },
10643
10644     // private
10645     showPrevMonth : function(e){
10646         this.update(this.activeDate.add("mo", -1));
10647     },
10648
10649     // private
10650     showNextMonth : function(e){
10651         this.update(this.activeDate.add("mo", 1));
10652     },
10653
10654     // private
10655     showPrevYear : function(){
10656         this.update(this.activeDate.add("y", -1));
10657     },
10658
10659     // private
10660     showNextYear : function(){
10661         this.update(this.activeDate.add("y", 1));
10662     },
10663
10664     // private
10665     handleMouseWheel : function(e){
10666         var delta = e.getWheelDelta();
10667         if(delta > 0){
10668             this.showPrevMonth();
10669             e.stopEvent();
10670         } else if(delta < 0){
10671             this.showNextMonth();
10672             e.stopEvent();
10673         }
10674     },
10675
10676     // private
10677     handleDateClick : function(e, t){
10678         e.stopEvent();
10679         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10680             this.setValue(new Date(t.dateValue));
10681             this.fireEvent("select", this, this.value);
10682         }
10683     },
10684
10685     // private
10686     selectToday : function(){
10687         this.setValue(new Date().clearTime());
10688         this.fireEvent("select", this, this.value);
10689     },
10690
10691     // private
10692     update : function(date)
10693     {
10694         var vd = this.activeDate;
10695         this.activeDate = date;
10696         if(vd && this.el){
10697             var t = date.getTime();
10698             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10699                 this.cells.removeClass("x-date-selected");
10700                 this.cells.each(function(c){
10701                    if(c.dom.firstChild.dateValue == t){
10702                        c.addClass("x-date-selected");
10703                        setTimeout(function(){
10704                             try{c.dom.firstChild.focus();}catch(e){}
10705                        }, 50);
10706                        return false;
10707                    }
10708                 });
10709                 return;
10710             }
10711         }
10712         
10713         var days = date.getDaysInMonth();
10714         var firstOfMonth = date.getFirstDateOfMonth();
10715         var startingPos = firstOfMonth.getDay()-this.startDay;
10716
10717         if(startingPos <= this.startDay){
10718             startingPos += 7;
10719         }
10720
10721         var pm = date.add("mo", -1);
10722         var prevStart = pm.getDaysInMonth()-startingPos;
10723
10724         var cells = this.cells.elements;
10725         var textEls = this.textNodes;
10726         days += startingPos;
10727
10728         // convert everything to numbers so it's fast
10729         var day = 86400000;
10730         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10731         var today = new Date().clearTime().getTime();
10732         var sel = date.clearTime().getTime();
10733         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10734         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10735         var ddMatch = this.disabledDatesRE;
10736         var ddText = this.disabledDatesText;
10737         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10738         var ddaysText = this.disabledDaysText;
10739         var format = this.format;
10740
10741         var setCellClass = function(cal, cell){
10742             cell.title = "";
10743             var t = d.getTime();
10744             cell.firstChild.dateValue = t;
10745             if(t == today){
10746                 cell.className += " x-date-today";
10747                 cell.title = cal.todayText;
10748             }
10749             if(t == sel){
10750                 cell.className += " x-date-selected";
10751                 setTimeout(function(){
10752                     try{cell.firstChild.focus();}catch(e){}
10753                 }, 50);
10754             }
10755             // disabling
10756             if(t < min) {
10757                 cell.className = " x-date-disabled";
10758                 cell.title = cal.minText;
10759                 return;
10760             }
10761             if(t > max) {
10762                 cell.className = " x-date-disabled";
10763                 cell.title = cal.maxText;
10764                 return;
10765             }
10766             if(ddays){
10767                 if(ddays.indexOf(d.getDay()) != -1){
10768                     cell.title = ddaysText;
10769                     cell.className = " x-date-disabled";
10770                 }
10771             }
10772             if(ddMatch && format){
10773                 var fvalue = d.dateFormat(format);
10774                 if(ddMatch.test(fvalue)){
10775                     cell.title = ddText.replace("%0", fvalue);
10776                     cell.className = " x-date-disabled";
10777                 }
10778             }
10779         };
10780
10781         var i = 0;
10782         for(; i < startingPos; i++) {
10783             textEls[i].innerHTML = (++prevStart);
10784             d.setDate(d.getDate()+1);
10785             cells[i].className = "x-date-prevday";
10786             setCellClass(this, cells[i]);
10787         }
10788         for(; i < days; i++){
10789             intDay = i - startingPos + 1;
10790             textEls[i].innerHTML = (intDay);
10791             d.setDate(d.getDate()+1);
10792             cells[i].className = "x-date-active";
10793             setCellClass(this, cells[i]);
10794         }
10795         var extraDays = 0;
10796         for(; i < 42; i++) {
10797              textEls[i].innerHTML = (++extraDays);
10798              d.setDate(d.getDate()+1);
10799              cells[i].className = "x-date-nextday";
10800              setCellClass(this, cells[i]);
10801         }
10802
10803         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10804         this.fireEvent('monthchange', this, date);
10805         
10806         if(!this.internalRender){
10807             var main = this.el.dom.firstChild;
10808             var w = main.offsetWidth;
10809             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10810             Roo.fly(main).setWidth(w);
10811             this.internalRender = true;
10812             // opera does not respect the auto grow header center column
10813             // then, after it gets a width opera refuses to recalculate
10814             // without a second pass
10815             if(Roo.isOpera && !this.secondPass){
10816                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10817                 this.secondPass = true;
10818                 this.update.defer(10, this, [date]);
10819             }
10820         }
10821         
10822         
10823     }
10824 });        /*
10825  * Based on:
10826  * Ext JS Library 1.1.1
10827  * Copyright(c) 2006-2007, Ext JS, LLC.
10828  *
10829  * Originally Released Under LGPL - original licence link has changed is not relivant.
10830  *
10831  * Fork - LGPL
10832  * <script type="text/javascript">
10833  */
10834 /**
10835  * @class Roo.TabPanel
10836  * @extends Roo.util.Observable
10837  * A lightweight tab container.
10838  * <br><br>
10839  * Usage:
10840  * <pre><code>
10841 // basic tabs 1, built from existing content
10842 var tabs = new Roo.TabPanel("tabs1");
10843 tabs.addTab("script", "View Script");
10844 tabs.addTab("markup", "View Markup");
10845 tabs.activate("script");
10846
10847 // more advanced tabs, built from javascript
10848 var jtabs = new Roo.TabPanel("jtabs");
10849 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10850
10851 // set up the UpdateManager
10852 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10853 var updater = tab2.getUpdateManager();
10854 updater.setDefaultUrl("ajax1.htm");
10855 tab2.on('activate', updater.refresh, updater, true);
10856
10857 // Use setUrl for Ajax loading
10858 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10859 tab3.setUrl("ajax2.htm", null, true);
10860
10861 // Disabled tab
10862 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10863 tab4.disable();
10864
10865 jtabs.activate("jtabs-1");
10866  * </code></pre>
10867  * @constructor
10868  * Create a new TabPanel.
10869  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10870  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10871  */
10872 Roo.TabPanel = function(container, config){
10873     /**
10874     * The container element for this TabPanel.
10875     * @type Roo.Element
10876     */
10877     this.el = Roo.get(container, true);
10878     if(config){
10879         if(typeof config == "boolean"){
10880             this.tabPosition = config ? "bottom" : "top";
10881         }else{
10882             Roo.apply(this, config);
10883         }
10884     }
10885     if(this.tabPosition == "bottom"){
10886         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10887         this.el.addClass("x-tabs-bottom");
10888     }
10889     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10890     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10891     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10892     if(Roo.isIE){
10893         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10894     }
10895     if(this.tabPosition != "bottom"){
10896         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10897          * @type Roo.Element
10898          */
10899         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10900         this.el.addClass("x-tabs-top");
10901     }
10902     this.items = [];
10903
10904     this.bodyEl.setStyle("position", "relative");
10905
10906     this.active = null;
10907     this.activateDelegate = this.activate.createDelegate(this);
10908
10909     this.addEvents({
10910         /**
10911          * @event tabchange
10912          * Fires when the active tab changes
10913          * @param {Roo.TabPanel} this
10914          * @param {Roo.TabPanelItem} activePanel The new active tab
10915          */
10916         "tabchange": true,
10917         /**
10918          * @event beforetabchange
10919          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10920          * @param {Roo.TabPanel} this
10921          * @param {Object} e Set cancel to true on this object to cancel the tab change
10922          * @param {Roo.TabPanelItem} tab The tab being changed to
10923          */
10924         "beforetabchange" : true
10925     });
10926
10927     Roo.EventManager.onWindowResize(this.onResize, this);
10928     this.cpad = this.el.getPadding("lr");
10929     this.hiddenCount = 0;
10930
10931
10932     // toolbar on the tabbar support...
10933     if (this.toolbar) {
10934         var tcfg = this.toolbar;
10935         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10936         this.toolbar = new Roo.Toolbar(tcfg);
10937         if (Roo.isSafari) {
10938             var tbl = tcfg.container.child('table', true);
10939             tbl.setAttribute('width', '100%');
10940         }
10941         
10942     }
10943    
10944
10945
10946     Roo.TabPanel.superclass.constructor.call(this);
10947 };
10948
10949 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10950     /*
10951      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10952      */
10953     tabPosition : "top",
10954     /*
10955      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10956      */
10957     currentTabWidth : 0,
10958     /*
10959      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10960      */
10961     minTabWidth : 40,
10962     /*
10963      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10964      */
10965     maxTabWidth : 250,
10966     /*
10967      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10968      */
10969     preferredTabWidth : 175,
10970     /*
10971      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10972      */
10973     resizeTabs : false,
10974     /*
10975      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10976      */
10977     monitorResize : true,
10978     /*
10979      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10980      */
10981     toolbar : false,
10982
10983     /**
10984      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10985      * @param {String} id The id of the div to use <b>or create</b>
10986      * @param {String} text The text for the tab
10987      * @param {String} content (optional) Content to put in the TabPanelItem body
10988      * @param {Boolean} closable (optional) True to create a close icon on the tab
10989      * @return {Roo.TabPanelItem} The created TabPanelItem
10990      */
10991     addTab : function(id, text, content, closable){
10992         var item = new Roo.TabPanelItem(this, id, text, closable);
10993         this.addTabItem(item);
10994         if(content){
10995             item.setContent(content);
10996         }
10997         return item;
10998     },
10999
11000     /**
11001      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11002      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11003      * @return {Roo.TabPanelItem}
11004      */
11005     getTab : function(id){
11006         return this.items[id];
11007     },
11008
11009     /**
11010      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11011      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11012      */
11013     hideTab : function(id){
11014         var t = this.items[id];
11015         if(!t.isHidden()){
11016            t.setHidden(true);
11017            this.hiddenCount++;
11018            this.autoSizeTabs();
11019         }
11020     },
11021
11022     /**
11023      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11024      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11025      */
11026     unhideTab : function(id){
11027         var t = this.items[id];
11028         if(t.isHidden()){
11029            t.setHidden(false);
11030            this.hiddenCount--;
11031            this.autoSizeTabs();
11032         }
11033     },
11034
11035     /**
11036      * Adds an existing {@link Roo.TabPanelItem}.
11037      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11038      */
11039     addTabItem : function(item){
11040         this.items[item.id] = item;
11041         this.items.push(item);
11042         if(this.resizeTabs){
11043            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11044            this.autoSizeTabs();
11045         }else{
11046             item.autoSize();
11047         }
11048     },
11049
11050     /**
11051      * Removes a {@link Roo.TabPanelItem}.
11052      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11053      */
11054     removeTab : function(id){
11055         var items = this.items;
11056         var tab = items[id];
11057         if(!tab) { return; }
11058         var index = items.indexOf(tab);
11059         if(this.active == tab && items.length > 1){
11060             var newTab = this.getNextAvailable(index);
11061             if(newTab) {
11062                 newTab.activate();
11063             }
11064         }
11065         this.stripEl.dom.removeChild(tab.pnode.dom);
11066         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11067             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11068         }
11069         items.splice(index, 1);
11070         delete this.items[tab.id];
11071         tab.fireEvent("close", tab);
11072         tab.purgeListeners();
11073         this.autoSizeTabs();
11074     },
11075
11076     getNextAvailable : function(start){
11077         var items = this.items;
11078         var index = start;
11079         // look for a next tab that will slide over to
11080         // replace the one being removed
11081         while(index < items.length){
11082             var item = items[++index];
11083             if(item && !item.isHidden()){
11084                 return item;
11085             }
11086         }
11087         // if one isn't found select the previous tab (on the left)
11088         index = start;
11089         while(index >= 0){
11090             var item = items[--index];
11091             if(item && !item.isHidden()){
11092                 return item;
11093             }
11094         }
11095         return null;
11096     },
11097
11098     /**
11099      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11100      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11101      */
11102     disableTab : function(id){
11103         var tab = this.items[id];
11104         if(tab && this.active != tab){
11105             tab.disable();
11106         }
11107     },
11108
11109     /**
11110      * Enables a {@link Roo.TabPanelItem} that is disabled.
11111      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11112      */
11113     enableTab : function(id){
11114         var tab = this.items[id];
11115         tab.enable();
11116     },
11117
11118     /**
11119      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11120      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11121      * @return {Roo.TabPanelItem} The TabPanelItem.
11122      */
11123     activate : function(id){
11124         var tab = this.items[id];
11125         if(!tab){
11126             return null;
11127         }
11128         if(tab == this.active || tab.disabled){
11129             return tab;
11130         }
11131         var e = {};
11132         this.fireEvent("beforetabchange", this, e, tab);
11133         if(e.cancel !== true && !tab.disabled){
11134             if(this.active){
11135                 this.active.hide();
11136             }
11137             this.active = this.items[id];
11138             this.active.show();
11139             this.fireEvent("tabchange", this, this.active);
11140         }
11141         return tab;
11142     },
11143
11144     /**
11145      * Gets the active {@link Roo.TabPanelItem}.
11146      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11147      */
11148     getActiveTab : function(){
11149         return this.active;
11150     },
11151
11152     /**
11153      * Updates the tab body element to fit the height of the container element
11154      * for overflow scrolling
11155      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11156      */
11157     syncHeight : function(targetHeight){
11158         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11159         var bm = this.bodyEl.getMargins();
11160         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11161         this.bodyEl.setHeight(newHeight);
11162         return newHeight;
11163     },
11164
11165     onResize : function(){
11166         if(this.monitorResize){
11167             this.autoSizeTabs();
11168         }
11169     },
11170
11171     /**
11172      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11173      */
11174     beginUpdate : function(){
11175         this.updating = true;
11176     },
11177
11178     /**
11179      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11180      */
11181     endUpdate : function(){
11182         this.updating = false;
11183         this.autoSizeTabs();
11184     },
11185
11186     /**
11187      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11188      */
11189     autoSizeTabs : function(){
11190         var count = this.items.length;
11191         var vcount = count - this.hiddenCount;
11192         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11193         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11194         var availWidth = Math.floor(w / vcount);
11195         var b = this.stripBody;
11196         if(b.getWidth() > w){
11197             var tabs = this.items;
11198             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11199             if(availWidth < this.minTabWidth){
11200                 /*if(!this.sleft){    // incomplete scrolling code
11201                     this.createScrollButtons();
11202                 }
11203                 this.showScroll();
11204                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11205             }
11206         }else{
11207             if(this.currentTabWidth < this.preferredTabWidth){
11208                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11209             }
11210         }
11211     },
11212
11213     /**
11214      * Returns the number of tabs in this TabPanel.
11215      * @return {Number}
11216      */
11217      getCount : function(){
11218          return this.items.length;
11219      },
11220
11221     /**
11222      * Resizes all the tabs to the passed width
11223      * @param {Number} The new width
11224      */
11225     setTabWidth : function(width){
11226         this.currentTabWidth = width;
11227         for(var i = 0, len = this.items.length; i < len; i++) {
11228                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11229         }
11230     },
11231
11232     /**
11233      * Destroys this TabPanel
11234      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11235      */
11236     destroy : function(removeEl){
11237         Roo.EventManager.removeResizeListener(this.onResize, this);
11238         for(var i = 0, len = this.items.length; i < len; i++){
11239             this.items[i].purgeListeners();
11240         }
11241         if(removeEl === true){
11242             this.el.update("");
11243             this.el.remove();
11244         }
11245     }
11246 });
11247
11248 /**
11249  * @class Roo.TabPanelItem
11250  * @extends Roo.util.Observable
11251  * Represents an individual item (tab plus body) in a TabPanel.
11252  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11253  * @param {String} id The id of this TabPanelItem
11254  * @param {String} text The text for the tab of this TabPanelItem
11255  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11256  */
11257 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11258     /**
11259      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11260      * @type Roo.TabPanel
11261      */
11262     this.tabPanel = tabPanel;
11263     /**
11264      * The id for this TabPanelItem
11265      * @type String
11266      */
11267     this.id = id;
11268     /** @private */
11269     this.disabled = false;
11270     /** @private */
11271     this.text = text;
11272     /** @private */
11273     this.loaded = false;
11274     this.closable = closable;
11275
11276     /**
11277      * The body element for this TabPanelItem.
11278      * @type Roo.Element
11279      */
11280     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11281     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11282     this.bodyEl.setStyle("display", "block");
11283     this.bodyEl.setStyle("zoom", "1");
11284     this.hideAction();
11285
11286     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11287     /** @private */
11288     this.el = Roo.get(els.el, true);
11289     this.inner = Roo.get(els.inner, true);
11290     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11291     this.pnode = Roo.get(els.el.parentNode, true);
11292     this.el.on("mousedown", this.onTabMouseDown, this);
11293     this.el.on("click", this.onTabClick, this);
11294     /** @private */
11295     if(closable){
11296         var c = Roo.get(els.close, true);
11297         c.dom.title = this.closeText;
11298         c.addClassOnOver("close-over");
11299         c.on("click", this.closeClick, this);
11300      }
11301
11302     this.addEvents({
11303          /**
11304          * @event activate
11305          * Fires when this tab becomes the active tab.
11306          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11307          * @param {Roo.TabPanelItem} this
11308          */
11309         "activate": true,
11310         /**
11311          * @event beforeclose
11312          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11313          * @param {Roo.TabPanelItem} this
11314          * @param {Object} e Set cancel to true on this object to cancel the close.
11315          */
11316         "beforeclose": true,
11317         /**
11318          * @event close
11319          * Fires when this tab is closed.
11320          * @param {Roo.TabPanelItem} this
11321          */
11322          "close": true,
11323         /**
11324          * @event deactivate
11325          * Fires when this tab is no longer the active tab.
11326          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11327          * @param {Roo.TabPanelItem} this
11328          */
11329          "deactivate" : true
11330     });
11331     this.hidden = false;
11332
11333     Roo.TabPanelItem.superclass.constructor.call(this);
11334 };
11335
11336 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11337     purgeListeners : function(){
11338        Roo.util.Observable.prototype.purgeListeners.call(this);
11339        this.el.removeAllListeners();
11340     },
11341     /**
11342      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11343      */
11344     show : function(){
11345         this.pnode.addClass("on");
11346         this.showAction();
11347         if(Roo.isOpera){
11348             this.tabPanel.stripWrap.repaint();
11349         }
11350         this.fireEvent("activate", this.tabPanel, this);
11351     },
11352
11353     /**
11354      * Returns true if this tab is the active tab.
11355      * @return {Boolean}
11356      */
11357     isActive : function(){
11358         return this.tabPanel.getActiveTab() == this;
11359     },
11360
11361     /**
11362      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11363      */
11364     hide : function(){
11365         this.pnode.removeClass("on");
11366         this.hideAction();
11367         this.fireEvent("deactivate", this.tabPanel, this);
11368     },
11369
11370     hideAction : function(){
11371         this.bodyEl.hide();
11372         this.bodyEl.setStyle("position", "absolute");
11373         this.bodyEl.setLeft("-20000px");
11374         this.bodyEl.setTop("-20000px");
11375     },
11376
11377     showAction : function(){
11378         this.bodyEl.setStyle("position", "relative");
11379         this.bodyEl.setTop("");
11380         this.bodyEl.setLeft("");
11381         this.bodyEl.show();
11382     },
11383
11384     /**
11385      * Set the tooltip for the tab.
11386      * @param {String} tooltip The tab's tooltip
11387      */
11388     setTooltip : function(text){
11389         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11390             this.textEl.dom.qtip = text;
11391             this.textEl.dom.removeAttribute('title');
11392         }else{
11393             this.textEl.dom.title = text;
11394         }
11395     },
11396
11397     onTabClick : function(e){
11398         e.preventDefault();
11399         this.tabPanel.activate(this.id);
11400     },
11401
11402     onTabMouseDown : function(e){
11403         e.preventDefault();
11404         this.tabPanel.activate(this.id);
11405     },
11406
11407     getWidth : function(){
11408         return this.inner.getWidth();
11409     },
11410
11411     setWidth : function(width){
11412         var iwidth = width - this.pnode.getPadding("lr");
11413         this.inner.setWidth(iwidth);
11414         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11415         this.pnode.setWidth(width);
11416     },
11417
11418     /**
11419      * Show or hide the tab
11420      * @param {Boolean} hidden True to hide or false to show.
11421      */
11422     setHidden : function(hidden){
11423         this.hidden = hidden;
11424         this.pnode.setStyle("display", hidden ? "none" : "");
11425     },
11426
11427     /**
11428      * Returns true if this tab is "hidden"
11429      * @return {Boolean}
11430      */
11431     isHidden : function(){
11432         return this.hidden;
11433     },
11434
11435     /**
11436      * Returns the text for this tab
11437      * @return {String}
11438      */
11439     getText : function(){
11440         return this.text;
11441     },
11442
11443     autoSize : function(){
11444         //this.el.beginMeasure();
11445         this.textEl.setWidth(1);
11446         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11447         //this.el.endMeasure();
11448     },
11449
11450     /**
11451      * Sets the text for the tab (Note: this also sets the tooltip text)
11452      * @param {String} text The tab's text and tooltip
11453      */
11454     setText : function(text){
11455         this.text = text;
11456         this.textEl.update(text);
11457         this.setTooltip(text);
11458         if(!this.tabPanel.resizeTabs){
11459             this.autoSize();
11460         }
11461     },
11462     /**
11463      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11464      */
11465     activate : function(){
11466         this.tabPanel.activate(this.id);
11467     },
11468
11469     /**
11470      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11471      */
11472     disable : function(){
11473         if(this.tabPanel.active != this){
11474             this.disabled = true;
11475             this.pnode.addClass("disabled");
11476         }
11477     },
11478
11479     /**
11480      * Enables this TabPanelItem if it was previously disabled.
11481      */
11482     enable : function(){
11483         this.disabled = false;
11484         this.pnode.removeClass("disabled");
11485     },
11486
11487     /**
11488      * Sets the content for this TabPanelItem.
11489      * @param {String} content The content
11490      * @param {Boolean} loadScripts true to look for and load scripts
11491      */
11492     setContent : function(content, loadScripts){
11493         this.bodyEl.update(content, loadScripts);
11494     },
11495
11496     /**
11497      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11498      * @return {Roo.UpdateManager} The UpdateManager
11499      */
11500     getUpdateManager : function(){
11501         return this.bodyEl.getUpdateManager();
11502     },
11503
11504     /**
11505      * Set a URL to be used to load the content for this TabPanelItem.
11506      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11507      * @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)
11508      * @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)
11509      * @return {Roo.UpdateManager} The UpdateManager
11510      */
11511     setUrl : function(url, params, loadOnce){
11512         if(this.refreshDelegate){
11513             this.un('activate', this.refreshDelegate);
11514         }
11515         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11516         this.on("activate", this.refreshDelegate);
11517         return this.bodyEl.getUpdateManager();
11518     },
11519
11520     /** @private */
11521     _handleRefresh : function(url, params, loadOnce){
11522         if(!loadOnce || !this.loaded){
11523             var updater = this.bodyEl.getUpdateManager();
11524             updater.update(url, params, this._setLoaded.createDelegate(this));
11525         }
11526     },
11527
11528     /**
11529      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11530      *   Will fail silently if the setUrl method has not been called.
11531      *   This does not activate the panel, just updates its content.
11532      */
11533     refresh : function(){
11534         if(this.refreshDelegate){
11535            this.loaded = false;
11536            this.refreshDelegate();
11537         }
11538     },
11539
11540     /** @private */
11541     _setLoaded : function(){
11542         this.loaded = true;
11543     },
11544
11545     /** @private */
11546     closeClick : function(e){
11547         var o = {};
11548         e.stopEvent();
11549         this.fireEvent("beforeclose", this, o);
11550         if(o.cancel !== true){
11551             this.tabPanel.removeTab(this.id);
11552         }
11553     },
11554     /**
11555      * The text displayed in the tooltip for the close icon.
11556      * @type String
11557      */
11558     closeText : "Close this tab"
11559 });
11560
11561 /** @private */
11562 Roo.TabPanel.prototype.createStrip = function(container){
11563     var strip = document.createElement("div");
11564     strip.className = "x-tabs-wrap";
11565     container.appendChild(strip);
11566     return strip;
11567 };
11568 /** @private */
11569 Roo.TabPanel.prototype.createStripList = function(strip){
11570     // div wrapper for retard IE
11571     // returns the "tr" element.
11572     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11573         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11574         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11575     return strip.firstChild.firstChild.firstChild.firstChild;
11576 };
11577 /** @private */
11578 Roo.TabPanel.prototype.createBody = function(container){
11579     var body = document.createElement("div");
11580     Roo.id(body, "tab-body");
11581     Roo.fly(body).addClass("x-tabs-body");
11582     container.appendChild(body);
11583     return body;
11584 };
11585 /** @private */
11586 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11587     var body = Roo.getDom(id);
11588     if(!body){
11589         body = document.createElement("div");
11590         body.id = id;
11591     }
11592     Roo.fly(body).addClass("x-tabs-item-body");
11593     bodyEl.insertBefore(body, bodyEl.firstChild);
11594     return body;
11595 };
11596 /** @private */
11597 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11598     var td = document.createElement("td");
11599     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11600     //stripEl.appendChild(td);
11601     if(closable){
11602         td.className = "x-tabs-closable";
11603         if(!this.closeTpl){
11604             this.closeTpl = new Roo.Template(
11605                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11606                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11607                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11608             );
11609         }
11610         var el = this.closeTpl.overwrite(td, {"text": text});
11611         var close = el.getElementsByTagName("div")[0];
11612         var inner = el.getElementsByTagName("em")[0];
11613         return {"el": el, "close": close, "inner": inner};
11614     } else {
11615         if(!this.tabTpl){
11616             this.tabTpl = new Roo.Template(
11617                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11618                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11619             );
11620         }
11621         var el = this.tabTpl.overwrite(td, {"text": text});
11622         var inner = el.getElementsByTagName("em")[0];
11623         return {"el": el, "inner": inner};
11624     }
11625 };/*
11626  * Based on:
11627  * Ext JS Library 1.1.1
11628  * Copyright(c) 2006-2007, Ext JS, LLC.
11629  *
11630  * Originally Released Under LGPL - original licence link has changed is not relivant.
11631  *
11632  * Fork - LGPL
11633  * <script type="text/javascript">
11634  */
11635
11636 /**
11637  * @class Roo.Button
11638  * @extends Roo.util.Observable
11639  * Simple Button class
11640  * @cfg {String} text The button text
11641  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11642  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11643  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11644  * @cfg {Object} scope The scope of the handler
11645  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11646  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11647  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11648  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11649  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11650  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11651    applies if enableToggle = true)
11652  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11653  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11654   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11655  * @constructor
11656  * Create a new button
11657  * @param {Object} config The config object
11658  */
11659 Roo.Button = function(renderTo, config)
11660 {
11661     if (!config) {
11662         config = renderTo;
11663         renderTo = config.renderTo || false;
11664     }
11665     
11666     Roo.apply(this, config);
11667     this.addEvents({
11668         /**
11669              * @event click
11670              * Fires when this button is clicked
11671              * @param {Button} this
11672              * @param {EventObject} e The click event
11673              */
11674             "click" : true,
11675         /**
11676              * @event toggle
11677              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11678              * @param {Button} this
11679              * @param {Boolean} pressed
11680              */
11681             "toggle" : true,
11682         /**
11683              * @event mouseover
11684              * Fires when the mouse hovers over the button
11685              * @param {Button} this
11686              * @param {Event} e The event object
11687              */
11688         'mouseover' : true,
11689         /**
11690              * @event mouseout
11691              * Fires when the mouse exits the button
11692              * @param {Button} this
11693              * @param {Event} e The event object
11694              */
11695         'mouseout': true,
11696          /**
11697              * @event render
11698              * Fires when the button is rendered
11699              * @param {Button} this
11700              */
11701         'render': true
11702     });
11703     if(this.menu){
11704         this.menu = Roo.menu.MenuMgr.get(this.menu);
11705     }
11706     // register listeners first!!  - so render can be captured..
11707     Roo.util.Observable.call(this);
11708     if(renderTo){
11709         this.render(renderTo);
11710     }
11711     
11712   
11713 };
11714
11715 Roo.extend(Roo.Button, Roo.util.Observable, {
11716     /**
11717      * 
11718      */
11719     
11720     /**
11721      * Read-only. True if this button is hidden
11722      * @type Boolean
11723      */
11724     hidden : false,
11725     /**
11726      * Read-only. True if this button is disabled
11727      * @type Boolean
11728      */
11729     disabled : false,
11730     /**
11731      * Read-only. True if this button is pressed (only if enableToggle = true)
11732      * @type Boolean
11733      */
11734     pressed : false,
11735
11736     /**
11737      * @cfg {Number} tabIndex 
11738      * The DOM tabIndex for this button (defaults to undefined)
11739      */
11740     tabIndex : undefined,
11741
11742     /**
11743      * @cfg {Boolean} enableToggle
11744      * True to enable pressed/not pressed toggling (defaults to false)
11745      */
11746     enableToggle: false,
11747     /**
11748      * @cfg {Mixed} menu
11749      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11750      */
11751     menu : undefined,
11752     /**
11753      * @cfg {String} menuAlign
11754      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11755      */
11756     menuAlign : "tl-bl?",
11757
11758     /**
11759      * @cfg {String} iconCls
11760      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11761      */
11762     iconCls : undefined,
11763     /**
11764      * @cfg {String} type
11765      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11766      */
11767     type : 'button',
11768
11769     // private
11770     menuClassTarget: 'tr',
11771
11772     /**
11773      * @cfg {String} clickEvent
11774      * The type of event to map to the button's event handler (defaults to 'click')
11775      */
11776     clickEvent : 'click',
11777
11778     /**
11779      * @cfg {Boolean} handleMouseEvents
11780      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11781      */
11782     handleMouseEvents : true,
11783
11784     /**
11785      * @cfg {String} tooltipType
11786      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11787      */
11788     tooltipType : 'qtip',
11789
11790     /**
11791      * @cfg {String} cls
11792      * A CSS class to apply to the button's main element.
11793      */
11794     
11795     /**
11796      * @cfg {Roo.Template} template (Optional)
11797      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11798      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11799      * require code modifications if required elements (e.g. a button) aren't present.
11800      */
11801
11802     // private
11803     render : function(renderTo){
11804         var btn;
11805         if(this.hideParent){
11806             this.parentEl = Roo.get(renderTo);
11807         }
11808         if(!this.dhconfig){
11809             if(!this.template){
11810                 if(!Roo.Button.buttonTemplate){
11811                     // hideous table template
11812                     Roo.Button.buttonTemplate = new Roo.Template(
11813                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11814                         '<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>',
11815                         "</tr></tbody></table>");
11816                 }
11817                 this.template = Roo.Button.buttonTemplate;
11818             }
11819             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11820             var btnEl = btn.child("button:first");
11821             btnEl.on('focus', this.onFocus, this);
11822             btnEl.on('blur', this.onBlur, this);
11823             if(this.cls){
11824                 btn.addClass(this.cls);
11825             }
11826             if(this.icon){
11827                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11828             }
11829             if(this.iconCls){
11830                 btnEl.addClass(this.iconCls);
11831                 if(!this.cls){
11832                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11833                 }
11834             }
11835             if(this.tabIndex !== undefined){
11836                 btnEl.dom.tabIndex = this.tabIndex;
11837             }
11838             if(this.tooltip){
11839                 if(typeof this.tooltip == 'object'){
11840                     Roo.QuickTips.tips(Roo.apply({
11841                           target: btnEl.id
11842                     }, this.tooltip));
11843                 } else {
11844                     btnEl.dom[this.tooltipType] = this.tooltip;
11845                 }
11846             }
11847         }else{
11848             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11849         }
11850         this.el = btn;
11851         if(this.id){
11852             this.el.dom.id = this.el.id = this.id;
11853         }
11854         if(this.menu){
11855             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11856             this.menu.on("show", this.onMenuShow, this);
11857             this.menu.on("hide", this.onMenuHide, this);
11858         }
11859         btn.addClass("x-btn");
11860         if(Roo.isIE && !Roo.isIE7){
11861             this.autoWidth.defer(1, this);
11862         }else{
11863             this.autoWidth();
11864         }
11865         if(this.handleMouseEvents){
11866             btn.on("mouseover", this.onMouseOver, this);
11867             btn.on("mouseout", this.onMouseOut, this);
11868             btn.on("mousedown", this.onMouseDown, this);
11869         }
11870         btn.on(this.clickEvent, this.onClick, this);
11871         //btn.on("mouseup", this.onMouseUp, this);
11872         if(this.hidden){
11873             this.hide();
11874         }
11875         if(this.disabled){
11876             this.disable();
11877         }
11878         Roo.ButtonToggleMgr.register(this);
11879         if(this.pressed){
11880             this.el.addClass("x-btn-pressed");
11881         }
11882         if(this.repeat){
11883             var repeater = new Roo.util.ClickRepeater(btn,
11884                 typeof this.repeat == "object" ? this.repeat : {}
11885             );
11886             repeater.on("click", this.onClick,  this);
11887         }
11888         
11889         this.fireEvent('render', this);
11890         
11891     },
11892     /**
11893      * Returns the button's underlying element
11894      * @return {Roo.Element} The element
11895      */
11896     getEl : function(){
11897         return this.el;  
11898     },
11899     
11900     /**
11901      * Destroys this Button and removes any listeners.
11902      */
11903     destroy : function(){
11904         Roo.ButtonToggleMgr.unregister(this);
11905         this.el.removeAllListeners();
11906         this.purgeListeners();
11907         this.el.remove();
11908     },
11909
11910     // private
11911     autoWidth : function(){
11912         if(this.el){
11913             this.el.setWidth("auto");
11914             if(Roo.isIE7 && Roo.isStrict){
11915                 var ib = this.el.child('button');
11916                 if(ib && ib.getWidth() > 20){
11917                     ib.clip();
11918                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11919                 }
11920             }
11921             if(this.minWidth){
11922                 if(this.hidden){
11923                     this.el.beginMeasure();
11924                 }
11925                 if(this.el.getWidth() < this.minWidth){
11926                     this.el.setWidth(this.minWidth);
11927                 }
11928                 if(this.hidden){
11929                     this.el.endMeasure();
11930                 }
11931             }
11932         }
11933     },
11934
11935     /**
11936      * Assigns this button's click handler
11937      * @param {Function} handler The function to call when the button is clicked
11938      * @param {Object} scope (optional) Scope for the function passed in
11939      */
11940     setHandler : function(handler, scope){
11941         this.handler = handler;
11942         this.scope = scope;  
11943     },
11944     
11945     /**
11946      * Sets this button's text
11947      * @param {String} text The button text
11948      */
11949     setText : function(text){
11950         this.text = text;
11951         if(this.el){
11952             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11953         }
11954         this.autoWidth();
11955     },
11956     
11957     /**
11958      * Gets the text for this button
11959      * @return {String} The button text
11960      */
11961     getText : function(){
11962         return this.text;  
11963     },
11964     
11965     /**
11966      * Show this button
11967      */
11968     show: function(){
11969         this.hidden = false;
11970         if(this.el){
11971             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11972         }
11973     },
11974     
11975     /**
11976      * Hide this button
11977      */
11978     hide: function(){
11979         this.hidden = true;
11980         if(this.el){
11981             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11982         }
11983     },
11984     
11985     /**
11986      * Convenience function for boolean show/hide
11987      * @param {Boolean} visible True to show, false to hide
11988      */
11989     setVisible: function(visible){
11990         if(visible) {
11991             this.show();
11992         }else{
11993             this.hide();
11994         }
11995     },
11996     
11997     /**
11998      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11999      * @param {Boolean} state (optional) Force a particular state
12000      */
12001     toggle : function(state){
12002         state = state === undefined ? !this.pressed : state;
12003         if(state != this.pressed){
12004             if(state){
12005                 this.el.addClass("x-btn-pressed");
12006                 this.pressed = true;
12007                 this.fireEvent("toggle", this, true);
12008             }else{
12009                 this.el.removeClass("x-btn-pressed");
12010                 this.pressed = false;
12011                 this.fireEvent("toggle", this, false);
12012             }
12013             if(this.toggleHandler){
12014                 this.toggleHandler.call(this.scope || this, this, state);
12015             }
12016         }
12017     },
12018     
12019     /**
12020      * Focus the button
12021      */
12022     focus : function(){
12023         this.el.child('button:first').focus();
12024     },
12025     
12026     /**
12027      * Disable this button
12028      */
12029     disable : function(){
12030         if(this.el){
12031             this.el.addClass("x-btn-disabled");
12032         }
12033         this.disabled = true;
12034     },
12035     
12036     /**
12037      * Enable this button
12038      */
12039     enable : function(){
12040         if(this.el){
12041             this.el.removeClass("x-btn-disabled");
12042         }
12043         this.disabled = false;
12044     },
12045
12046     /**
12047      * Convenience function for boolean enable/disable
12048      * @param {Boolean} enabled True to enable, false to disable
12049      */
12050     setDisabled : function(v){
12051         this[v !== true ? "enable" : "disable"]();
12052     },
12053
12054     // private
12055     onClick : function(e){
12056         if(e){
12057             e.preventDefault();
12058         }
12059         if(e.button != 0){
12060             return;
12061         }
12062         if(!this.disabled){
12063             if(this.enableToggle){
12064                 this.toggle();
12065             }
12066             if(this.menu && !this.menu.isVisible()){
12067                 this.menu.show(this.el, this.menuAlign);
12068             }
12069             this.fireEvent("click", this, e);
12070             if(this.handler){
12071                 this.el.removeClass("x-btn-over");
12072                 this.handler.call(this.scope || this, this, e);
12073             }
12074         }
12075     },
12076     // private
12077     onMouseOver : function(e){
12078         if(!this.disabled){
12079             this.el.addClass("x-btn-over");
12080             this.fireEvent('mouseover', this, e);
12081         }
12082     },
12083     // private
12084     onMouseOut : function(e){
12085         if(!e.within(this.el,  true)){
12086             this.el.removeClass("x-btn-over");
12087             this.fireEvent('mouseout', this, e);
12088         }
12089     },
12090     // private
12091     onFocus : function(e){
12092         if(!this.disabled){
12093             this.el.addClass("x-btn-focus");
12094         }
12095     },
12096     // private
12097     onBlur : function(e){
12098         this.el.removeClass("x-btn-focus");
12099     },
12100     // private
12101     onMouseDown : function(e){
12102         if(!this.disabled && e.button == 0){
12103             this.el.addClass("x-btn-click");
12104             Roo.get(document).on('mouseup', this.onMouseUp, this);
12105         }
12106     },
12107     // private
12108     onMouseUp : function(e){
12109         if(e.button == 0){
12110             this.el.removeClass("x-btn-click");
12111             Roo.get(document).un('mouseup', this.onMouseUp, this);
12112         }
12113     },
12114     // private
12115     onMenuShow : function(e){
12116         this.el.addClass("x-btn-menu-active");
12117     },
12118     // private
12119     onMenuHide : function(e){
12120         this.el.removeClass("x-btn-menu-active");
12121     }   
12122 });
12123
12124 // Private utility class used by Button
12125 Roo.ButtonToggleMgr = function(){
12126    var groups = {};
12127    
12128    function toggleGroup(btn, state){
12129        if(state){
12130            var g = groups[btn.toggleGroup];
12131            for(var i = 0, l = g.length; i < l; i++){
12132                if(g[i] != btn){
12133                    g[i].toggle(false);
12134                }
12135            }
12136        }
12137    }
12138    
12139    return {
12140        register : function(btn){
12141            if(!btn.toggleGroup){
12142                return;
12143            }
12144            var g = groups[btn.toggleGroup];
12145            if(!g){
12146                g = groups[btn.toggleGroup] = [];
12147            }
12148            g.push(btn);
12149            btn.on("toggle", toggleGroup);
12150        },
12151        
12152        unregister : function(btn){
12153            if(!btn.toggleGroup){
12154                return;
12155            }
12156            var g = groups[btn.toggleGroup];
12157            if(g){
12158                g.remove(btn);
12159                btn.un("toggle", toggleGroup);
12160            }
12161        }
12162    };
12163 }();/*
12164  * Based on:
12165  * Ext JS Library 1.1.1
12166  * Copyright(c) 2006-2007, Ext JS, LLC.
12167  *
12168  * Originally Released Under LGPL - original licence link has changed is not relivant.
12169  *
12170  * Fork - LGPL
12171  * <script type="text/javascript">
12172  */
12173  
12174 /**
12175  * @class Roo.SplitButton
12176  * @extends Roo.Button
12177  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12178  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12179  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12180  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12181  * @cfg {String} arrowTooltip The title attribute of the arrow
12182  * @constructor
12183  * Create a new menu button
12184  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12185  * @param {Object} config The config object
12186  */
12187 Roo.SplitButton = function(renderTo, config){
12188     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12189     /**
12190      * @event arrowclick
12191      * Fires when this button's arrow is clicked
12192      * @param {SplitButton} this
12193      * @param {EventObject} e The click event
12194      */
12195     this.addEvents({"arrowclick":true});
12196 };
12197
12198 Roo.extend(Roo.SplitButton, Roo.Button, {
12199     render : function(renderTo){
12200         // this is one sweet looking template!
12201         var tpl = new Roo.Template(
12202             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12203             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12204             '<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>',
12205             "</tbody></table></td><td>",
12206             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12207             '<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>',
12208             "</tbody></table></td></tr></table>"
12209         );
12210         var btn = tpl.append(renderTo, [this.text, this.type], true);
12211         var btnEl = btn.child("button");
12212         if(this.cls){
12213             btn.addClass(this.cls);
12214         }
12215         if(this.icon){
12216             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12217         }
12218         if(this.iconCls){
12219             btnEl.addClass(this.iconCls);
12220             if(!this.cls){
12221                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12222             }
12223         }
12224         this.el = btn;
12225         if(this.handleMouseEvents){
12226             btn.on("mouseover", this.onMouseOver, this);
12227             btn.on("mouseout", this.onMouseOut, this);
12228             btn.on("mousedown", this.onMouseDown, this);
12229             btn.on("mouseup", this.onMouseUp, this);
12230         }
12231         btn.on(this.clickEvent, this.onClick, this);
12232         if(this.tooltip){
12233             if(typeof this.tooltip == 'object'){
12234                 Roo.QuickTips.tips(Roo.apply({
12235                       target: btnEl.id
12236                 }, this.tooltip));
12237             } else {
12238                 btnEl.dom[this.tooltipType] = this.tooltip;
12239             }
12240         }
12241         if(this.arrowTooltip){
12242             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12243         }
12244         if(this.hidden){
12245             this.hide();
12246         }
12247         if(this.disabled){
12248             this.disable();
12249         }
12250         if(this.pressed){
12251             this.el.addClass("x-btn-pressed");
12252         }
12253         if(Roo.isIE && !Roo.isIE7){
12254             this.autoWidth.defer(1, this);
12255         }else{
12256             this.autoWidth();
12257         }
12258         if(this.menu){
12259             this.menu.on("show", this.onMenuShow, this);
12260             this.menu.on("hide", this.onMenuHide, this);
12261         }
12262         this.fireEvent('render', this);
12263     },
12264
12265     // private
12266     autoWidth : function(){
12267         if(this.el){
12268             var tbl = this.el.child("table:first");
12269             var tbl2 = this.el.child("table:last");
12270             this.el.setWidth("auto");
12271             tbl.setWidth("auto");
12272             if(Roo.isIE7 && Roo.isStrict){
12273                 var ib = this.el.child('button:first');
12274                 if(ib && ib.getWidth() > 20){
12275                     ib.clip();
12276                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12277                 }
12278             }
12279             if(this.minWidth){
12280                 if(this.hidden){
12281                     this.el.beginMeasure();
12282                 }
12283                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12284                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12285                 }
12286                 if(this.hidden){
12287                     this.el.endMeasure();
12288                 }
12289             }
12290             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12291         } 
12292     },
12293     /**
12294      * Sets this button's click handler
12295      * @param {Function} handler The function to call when the button is clicked
12296      * @param {Object} scope (optional) Scope for the function passed above
12297      */
12298     setHandler : function(handler, scope){
12299         this.handler = handler;
12300         this.scope = scope;  
12301     },
12302     
12303     /**
12304      * Sets this button's arrow click handler
12305      * @param {Function} handler The function to call when the arrow is clicked
12306      * @param {Object} scope (optional) Scope for the function passed above
12307      */
12308     setArrowHandler : function(handler, scope){
12309         this.arrowHandler = handler;
12310         this.scope = scope;  
12311     },
12312     
12313     /**
12314      * Focus the button
12315      */
12316     focus : function(){
12317         if(this.el){
12318             this.el.child("button:first").focus();
12319         }
12320     },
12321
12322     // private
12323     onClick : function(e){
12324         e.preventDefault();
12325         if(!this.disabled){
12326             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12327                 if(this.menu && !this.menu.isVisible()){
12328                     this.menu.show(this.el, this.menuAlign);
12329                 }
12330                 this.fireEvent("arrowclick", this, e);
12331                 if(this.arrowHandler){
12332                     this.arrowHandler.call(this.scope || this, this, e);
12333                 }
12334             }else{
12335                 this.fireEvent("click", this, e);
12336                 if(this.handler){
12337                     this.handler.call(this.scope || this, this, e);
12338                 }
12339             }
12340         }
12341     },
12342     // private
12343     onMouseDown : function(e){
12344         if(!this.disabled){
12345             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12346         }
12347     },
12348     // private
12349     onMouseUp : function(e){
12350         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12351     }   
12352 });
12353
12354
12355 // backwards compat
12356 Roo.MenuButton = Roo.SplitButton;/*
12357  * Based on:
12358  * Ext JS Library 1.1.1
12359  * Copyright(c) 2006-2007, Ext JS, LLC.
12360  *
12361  * Originally Released Under LGPL - original licence link has changed is not relivant.
12362  *
12363  * Fork - LGPL
12364  * <script type="text/javascript">
12365  */
12366
12367 /**
12368  * @class Roo.Toolbar
12369  * Basic Toolbar class.
12370  * @constructor
12371  * Creates a new Toolbar
12372  * @param {Object} container The config object
12373  */ 
12374 Roo.Toolbar = function(container, buttons, config)
12375 {
12376     /// old consturctor format still supported..
12377     if(container instanceof Array){ // omit the container for later rendering
12378         buttons = container;
12379         config = buttons;
12380         container = null;
12381     }
12382     if (typeof(container) == 'object' && container.xtype) {
12383         config = container;
12384         container = config.container;
12385         buttons = config.buttons || []; // not really - use items!!
12386     }
12387     var xitems = [];
12388     if (config && config.items) {
12389         xitems = config.items;
12390         delete config.items;
12391     }
12392     Roo.apply(this, config);
12393     this.buttons = buttons;
12394     
12395     if(container){
12396         this.render(container);
12397     }
12398     this.xitems = xitems;
12399     Roo.each(xitems, function(b) {
12400         this.add(b);
12401     }, this);
12402     
12403 };
12404
12405 Roo.Toolbar.prototype = {
12406     /**
12407      * @cfg {Array} items
12408      * array of button configs or elements to add (will be converted to a MixedCollection)
12409      */
12410     
12411     /**
12412      * @cfg {String/HTMLElement/Element} container
12413      * The id or element that will contain the toolbar
12414      */
12415     // private
12416     render : function(ct){
12417         this.el = Roo.get(ct);
12418         if(this.cls){
12419             this.el.addClass(this.cls);
12420         }
12421         // using a table allows for vertical alignment
12422         // 100% width is needed by Safari...
12423         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12424         this.tr = this.el.child("tr", true);
12425         var autoId = 0;
12426         this.items = new Roo.util.MixedCollection(false, function(o){
12427             return o.id || ("item" + (++autoId));
12428         });
12429         if(this.buttons){
12430             this.add.apply(this, this.buttons);
12431             delete this.buttons;
12432         }
12433     },
12434
12435     /**
12436      * Adds element(s) to the toolbar -- this function takes a variable number of 
12437      * arguments of mixed type and adds them to the toolbar.
12438      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12439      * <ul>
12440      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12441      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12442      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12443      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12444      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12445      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12446      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12447      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12448      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12449      * </ul>
12450      * @param {Mixed} arg2
12451      * @param {Mixed} etc.
12452      */
12453     add : function(){
12454         var a = arguments, l = a.length;
12455         for(var i = 0; i < l; i++){
12456             this._add(a[i]);
12457         }
12458     },
12459     // private..
12460     _add : function(el) {
12461         
12462         if (el.xtype) {
12463             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12464         }
12465         
12466         if (el.applyTo){ // some kind of form field
12467             return this.addField(el);
12468         } 
12469         if (el.render){ // some kind of Toolbar.Item
12470             return this.addItem(el);
12471         }
12472         if (typeof el == "string"){ // string
12473             if(el == "separator" || el == "-"){
12474                 return this.addSeparator();
12475             }
12476             if (el == " "){
12477                 return this.addSpacer();
12478             }
12479             if(el == "->"){
12480                 return this.addFill();
12481             }
12482             return this.addText(el);
12483             
12484         }
12485         if(el.tagName){ // element
12486             return this.addElement(el);
12487         }
12488         if(typeof el == "object"){ // must be button config?
12489             return this.addButton(el);
12490         }
12491         // and now what?!?!
12492         return false;
12493         
12494     },
12495     
12496     /**
12497      * Add an Xtype element
12498      * @param {Object} xtype Xtype Object
12499      * @return {Object} created Object
12500      */
12501     addxtype : function(e){
12502         return this.add(e);  
12503     },
12504     
12505     /**
12506      * Returns the Element for this toolbar.
12507      * @return {Roo.Element}
12508      */
12509     getEl : function(){
12510         return this.el;  
12511     },
12512     
12513     /**
12514      * Adds a separator
12515      * @return {Roo.Toolbar.Item} The separator item
12516      */
12517     addSeparator : function(){
12518         return this.addItem(new Roo.Toolbar.Separator());
12519     },
12520
12521     /**
12522      * Adds a spacer element
12523      * @return {Roo.Toolbar.Spacer} The spacer item
12524      */
12525     addSpacer : function(){
12526         return this.addItem(new Roo.Toolbar.Spacer());
12527     },
12528
12529     /**
12530      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12531      * @return {Roo.Toolbar.Fill} The fill item
12532      */
12533     addFill : function(){
12534         return this.addItem(new Roo.Toolbar.Fill());
12535     },
12536
12537     /**
12538      * Adds any standard HTML element to the toolbar
12539      * @param {String/HTMLElement/Element} el The element or id of the element to add
12540      * @return {Roo.Toolbar.Item} The element's item
12541      */
12542     addElement : function(el){
12543         return this.addItem(new Roo.Toolbar.Item(el));
12544     },
12545     /**
12546      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12547      * @type Roo.util.MixedCollection  
12548      */
12549     items : false,
12550      
12551     /**
12552      * Adds any Toolbar.Item or subclass
12553      * @param {Roo.Toolbar.Item} item
12554      * @return {Roo.Toolbar.Item} The item
12555      */
12556     addItem : function(item){
12557         var td = this.nextBlock();
12558         item.render(td);
12559         this.items.add(item);
12560         return item;
12561     },
12562     
12563     /**
12564      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12565      * @param {Object/Array} config A button config or array of configs
12566      * @return {Roo.Toolbar.Button/Array}
12567      */
12568     addButton : function(config){
12569         if(config instanceof Array){
12570             var buttons = [];
12571             for(var i = 0, len = config.length; i < len; i++) {
12572                 buttons.push(this.addButton(config[i]));
12573             }
12574             return buttons;
12575         }
12576         var b = config;
12577         if(!(config instanceof Roo.Toolbar.Button)){
12578             b = config.split ?
12579                 new Roo.Toolbar.SplitButton(config) :
12580                 new Roo.Toolbar.Button(config);
12581         }
12582         var td = this.nextBlock();
12583         b.render(td);
12584         this.items.add(b);
12585         return b;
12586     },
12587     
12588     /**
12589      * Adds text to the toolbar
12590      * @param {String} text The text to add
12591      * @return {Roo.Toolbar.Item} The element's item
12592      */
12593     addText : function(text){
12594         return this.addItem(new Roo.Toolbar.TextItem(text));
12595     },
12596     
12597     /**
12598      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12599      * @param {Number} index The index where the item is to be inserted
12600      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12601      * @return {Roo.Toolbar.Button/Item}
12602      */
12603     insertButton : function(index, item){
12604         if(item instanceof Array){
12605             var buttons = [];
12606             for(var i = 0, len = item.length; i < len; i++) {
12607                buttons.push(this.insertButton(index + i, item[i]));
12608             }
12609             return buttons;
12610         }
12611         if (!(item instanceof Roo.Toolbar.Button)){
12612            item = new Roo.Toolbar.Button(item);
12613         }
12614         var td = document.createElement("td");
12615         this.tr.insertBefore(td, this.tr.childNodes[index]);
12616         item.render(td);
12617         this.items.insert(index, item);
12618         return item;
12619     },
12620     
12621     /**
12622      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12623      * @param {Object} config
12624      * @return {Roo.Toolbar.Item} The element's item
12625      */
12626     addDom : function(config, returnEl){
12627         var td = this.nextBlock();
12628         Roo.DomHelper.overwrite(td, config);
12629         var ti = new Roo.Toolbar.Item(td.firstChild);
12630         ti.render(td);
12631         this.items.add(ti);
12632         return ti;
12633     },
12634
12635     /**
12636      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12637      * @type Roo.util.MixedCollection  
12638      */
12639     fields : false,
12640     
12641     /**
12642      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12643      * Note: the field should not have been rendered yet. For a field that has already been
12644      * rendered, use {@link #addElement}.
12645      * @param {Roo.form.Field} field
12646      * @return {Roo.ToolbarItem}
12647      */
12648      
12649       
12650     addField : function(field) {
12651         if (!this.fields) {
12652             var autoId = 0;
12653             this.fields = new Roo.util.MixedCollection(false, function(o){
12654                 return o.id || ("item" + (++autoId));
12655             });
12656
12657         }
12658         
12659         var td = this.nextBlock();
12660         field.render(td);
12661         var ti = new Roo.Toolbar.Item(td.firstChild);
12662         ti.render(td);
12663         this.items.add(ti);
12664         this.fields.add(field);
12665         return ti;
12666     },
12667     /**
12668      * Hide the toolbar
12669      * @method hide
12670      */
12671      
12672       
12673     hide : function()
12674     {
12675         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12676         this.el.child('div').hide();
12677     },
12678     /**
12679      * Show the toolbar
12680      * @method show
12681      */
12682     show : function()
12683     {
12684         this.el.child('div').show();
12685     },
12686       
12687     // private
12688     nextBlock : function(){
12689         var td = document.createElement("td");
12690         this.tr.appendChild(td);
12691         return td;
12692     },
12693
12694     // private
12695     destroy : function(){
12696         if(this.items){ // rendered?
12697             Roo.destroy.apply(Roo, this.items.items);
12698         }
12699         if(this.fields){ // rendered?
12700             Roo.destroy.apply(Roo, this.fields.items);
12701         }
12702         Roo.Element.uncache(this.el, this.tr);
12703     }
12704 };
12705
12706 /**
12707  * @class Roo.Toolbar.Item
12708  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12709  * @constructor
12710  * Creates a new Item
12711  * @param {HTMLElement} el 
12712  */
12713 Roo.Toolbar.Item = function(el){
12714     this.el = Roo.getDom(el);
12715     this.id = Roo.id(this.el);
12716     this.hidden = false;
12717 };
12718
12719 Roo.Toolbar.Item.prototype = {
12720     
12721     /**
12722      * Get this item's HTML Element
12723      * @return {HTMLElement}
12724      */
12725     getEl : function(){
12726        return this.el;  
12727     },
12728
12729     // private
12730     render : function(td){
12731         this.td = td;
12732         td.appendChild(this.el);
12733     },
12734     
12735     /**
12736      * Removes and destroys this item.
12737      */
12738     destroy : function(){
12739         this.td.parentNode.removeChild(this.td);
12740     },
12741     
12742     /**
12743      * Shows this item.
12744      */
12745     show: function(){
12746         this.hidden = false;
12747         this.td.style.display = "";
12748     },
12749     
12750     /**
12751      * Hides this item.
12752      */
12753     hide: function(){
12754         this.hidden = true;
12755         this.td.style.display = "none";
12756     },
12757     
12758     /**
12759      * Convenience function for boolean show/hide.
12760      * @param {Boolean} visible true to show/false to hide
12761      */
12762     setVisible: function(visible){
12763         if(visible) {
12764             this.show();
12765         }else{
12766             this.hide();
12767         }
12768     },
12769     
12770     /**
12771      * Try to focus this item.
12772      */
12773     focus : function(){
12774         Roo.fly(this.el).focus();
12775     },
12776     
12777     /**
12778      * Disables this item.
12779      */
12780     disable : function(){
12781         Roo.fly(this.td).addClass("x-item-disabled");
12782         this.disabled = true;
12783         this.el.disabled = true;
12784     },
12785     
12786     /**
12787      * Enables this item.
12788      */
12789     enable : function(){
12790         Roo.fly(this.td).removeClass("x-item-disabled");
12791         this.disabled = false;
12792         this.el.disabled = false;
12793     }
12794 };
12795
12796
12797 /**
12798  * @class Roo.Toolbar.Separator
12799  * @extends Roo.Toolbar.Item
12800  * A simple toolbar separator class
12801  * @constructor
12802  * Creates a new Separator
12803  */
12804 Roo.Toolbar.Separator = function(){
12805     var s = document.createElement("span");
12806     s.className = "ytb-sep";
12807     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12808 };
12809 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12810     enable:Roo.emptyFn,
12811     disable:Roo.emptyFn,
12812     focus:Roo.emptyFn
12813 });
12814
12815 /**
12816  * @class Roo.Toolbar.Spacer
12817  * @extends Roo.Toolbar.Item
12818  * A simple element that adds extra horizontal space to a toolbar.
12819  * @constructor
12820  * Creates a new Spacer
12821  */
12822 Roo.Toolbar.Spacer = function(){
12823     var s = document.createElement("div");
12824     s.className = "ytb-spacer";
12825     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12826 };
12827 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12828     enable:Roo.emptyFn,
12829     disable:Roo.emptyFn,
12830     focus:Roo.emptyFn
12831 });
12832
12833 /**
12834  * @class Roo.Toolbar.Fill
12835  * @extends Roo.Toolbar.Spacer
12836  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12837  * @constructor
12838  * Creates a new Spacer
12839  */
12840 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12841     // private
12842     render : function(td){
12843         td.style.width = '100%';
12844         Roo.Toolbar.Fill.superclass.render.call(this, td);
12845     }
12846 });
12847
12848 /**
12849  * @class Roo.Toolbar.TextItem
12850  * @extends Roo.Toolbar.Item
12851  * A simple class that renders text directly into a toolbar.
12852  * @constructor
12853  * Creates a new TextItem
12854  * @param {String} text
12855  */
12856 Roo.Toolbar.TextItem = function(text){
12857     if (typeof(text) == 'object') {
12858         text = text.text;
12859     }
12860     var s = document.createElement("span");
12861     s.className = "ytb-text";
12862     s.innerHTML = text;
12863     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12864 };
12865 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12866     enable:Roo.emptyFn,
12867     disable:Roo.emptyFn,
12868     focus:Roo.emptyFn
12869 });
12870
12871 /**
12872  * @class Roo.Toolbar.Button
12873  * @extends Roo.Button
12874  * A button that renders into a toolbar.
12875  * @constructor
12876  * Creates a new Button
12877  * @param {Object} config A standard {@link Roo.Button} config object
12878  */
12879 Roo.Toolbar.Button = function(config){
12880     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12881 };
12882 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12883     render : function(td){
12884         this.td = td;
12885         Roo.Toolbar.Button.superclass.render.call(this, td);
12886     },
12887     
12888     /**
12889      * Removes and destroys this button
12890      */
12891     destroy : function(){
12892         Roo.Toolbar.Button.superclass.destroy.call(this);
12893         this.td.parentNode.removeChild(this.td);
12894     },
12895     
12896     /**
12897      * Shows this button
12898      */
12899     show: function(){
12900         this.hidden = false;
12901         this.td.style.display = "";
12902     },
12903     
12904     /**
12905      * Hides this button
12906      */
12907     hide: function(){
12908         this.hidden = true;
12909         this.td.style.display = "none";
12910     },
12911
12912     /**
12913      * Disables this item
12914      */
12915     disable : function(){
12916         Roo.fly(this.td).addClass("x-item-disabled");
12917         this.disabled = true;
12918     },
12919
12920     /**
12921      * Enables this item
12922      */
12923     enable : function(){
12924         Roo.fly(this.td).removeClass("x-item-disabled");
12925         this.disabled = false;
12926     }
12927 });
12928 // backwards compat
12929 Roo.ToolbarButton = Roo.Toolbar.Button;
12930
12931 /**
12932  * @class Roo.Toolbar.SplitButton
12933  * @extends Roo.SplitButton
12934  * A menu button that renders into a toolbar.
12935  * @constructor
12936  * Creates a new SplitButton
12937  * @param {Object} config A standard {@link Roo.SplitButton} config object
12938  */
12939 Roo.Toolbar.SplitButton = function(config){
12940     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12941 };
12942 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12943     render : function(td){
12944         this.td = td;
12945         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12946     },
12947     
12948     /**
12949      * Removes and destroys this button
12950      */
12951     destroy : function(){
12952         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12953         this.td.parentNode.removeChild(this.td);
12954     },
12955     
12956     /**
12957      * Shows this button
12958      */
12959     show: function(){
12960         this.hidden = false;
12961         this.td.style.display = "";
12962     },
12963     
12964     /**
12965      * Hides this button
12966      */
12967     hide: function(){
12968         this.hidden = true;
12969         this.td.style.display = "none";
12970     }
12971 });
12972
12973 // backwards compat
12974 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12975  * Based on:
12976  * Ext JS Library 1.1.1
12977  * Copyright(c) 2006-2007, Ext JS, LLC.
12978  *
12979  * Originally Released Under LGPL - original licence link has changed is not relivant.
12980  *
12981  * Fork - LGPL
12982  * <script type="text/javascript">
12983  */
12984  
12985 /**
12986  * @class Roo.PagingToolbar
12987  * @extends Roo.Toolbar
12988  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12989  * @constructor
12990  * Create a new PagingToolbar
12991  * @param {Object} config The config object
12992  */
12993 Roo.PagingToolbar = function(el, ds, config)
12994 {
12995     // old args format still supported... - xtype is prefered..
12996     if (typeof(el) == 'object' && el.xtype) {
12997         // created from xtype...
12998         config = el;
12999         ds = el.dataSource;
13000         el = config.container;
13001     }
13002     var items = [];
13003     if (config.items) {
13004         items = config.items;
13005         config.items = [];
13006     }
13007     
13008     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13009     this.ds = ds;
13010     this.cursor = 0;
13011     this.renderButtons(this.el);
13012     this.bind(ds);
13013     
13014     // supprot items array.
13015    
13016     Roo.each(items, function(e) {
13017         this.add(Roo.factory(e));
13018     },this);
13019     
13020 };
13021
13022 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13023     /**
13024      * @cfg {Roo.data.Store} dataSource
13025      * The underlying data store providing the paged data
13026      */
13027     /**
13028      * @cfg {String/HTMLElement/Element} container
13029      * container The id or element that will contain the toolbar
13030      */
13031     /**
13032      * @cfg {Boolean} displayInfo
13033      * True to display the displayMsg (defaults to false)
13034      */
13035     /**
13036      * @cfg {Number} pageSize
13037      * The number of records to display per page (defaults to 20)
13038      */
13039     pageSize: 20,
13040     /**
13041      * @cfg {String} displayMsg
13042      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13043      */
13044     displayMsg : 'Displaying {0} - {1} of {2}',
13045     /**
13046      * @cfg {String} emptyMsg
13047      * The message to display when no records are found (defaults to "No data to display")
13048      */
13049     emptyMsg : 'No data to display',
13050     /**
13051      * Customizable piece of the default paging text (defaults to "Page")
13052      * @type String
13053      */
13054     beforePageText : "Page",
13055     /**
13056      * Customizable piece of the default paging text (defaults to "of %0")
13057      * @type String
13058      */
13059     afterPageText : "of {0}",
13060     /**
13061      * Customizable piece of the default paging text (defaults to "First Page")
13062      * @type String
13063      */
13064     firstText : "First Page",
13065     /**
13066      * Customizable piece of the default paging text (defaults to "Previous Page")
13067      * @type String
13068      */
13069     prevText : "Previous Page",
13070     /**
13071      * Customizable piece of the default paging text (defaults to "Next Page")
13072      * @type String
13073      */
13074     nextText : "Next Page",
13075     /**
13076      * Customizable piece of the default paging text (defaults to "Last Page")
13077      * @type String
13078      */
13079     lastText : "Last Page",
13080     /**
13081      * Customizable piece of the default paging text (defaults to "Refresh")
13082      * @type String
13083      */
13084     refreshText : "Refresh",
13085
13086     // private
13087     renderButtons : function(el){
13088         Roo.PagingToolbar.superclass.render.call(this, el);
13089         this.first = this.addButton({
13090             tooltip: this.firstText,
13091             cls: "x-btn-icon x-grid-page-first",
13092             disabled: true,
13093             handler: this.onClick.createDelegate(this, ["first"])
13094         });
13095         this.prev = this.addButton({
13096             tooltip: this.prevText,
13097             cls: "x-btn-icon x-grid-page-prev",
13098             disabled: true,
13099             handler: this.onClick.createDelegate(this, ["prev"])
13100         });
13101         //this.addSeparator();
13102         this.add(this.beforePageText);
13103         this.field = Roo.get(this.addDom({
13104            tag: "input",
13105            type: "text",
13106            size: "3",
13107            value: "1",
13108            cls: "x-grid-page-number"
13109         }).el);
13110         this.field.on("keydown", this.onPagingKeydown, this);
13111         this.field.on("focus", function(){this.dom.select();});
13112         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13113         this.field.setHeight(18);
13114         //this.addSeparator();
13115         this.next = this.addButton({
13116             tooltip: this.nextText,
13117             cls: "x-btn-icon x-grid-page-next",
13118             disabled: true,
13119             handler: this.onClick.createDelegate(this, ["next"])
13120         });
13121         this.last = this.addButton({
13122             tooltip: this.lastText,
13123             cls: "x-btn-icon x-grid-page-last",
13124             disabled: true,
13125             handler: this.onClick.createDelegate(this, ["last"])
13126         });
13127         //this.addSeparator();
13128         this.loading = this.addButton({
13129             tooltip: this.refreshText,
13130             cls: "x-btn-icon x-grid-loading",
13131             handler: this.onClick.createDelegate(this, ["refresh"])
13132         });
13133
13134         if(this.displayInfo){
13135             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13136         }
13137     },
13138
13139     // private
13140     updateInfo : function(){
13141         if(this.displayEl){
13142             var count = this.ds.getCount();
13143             var msg = count == 0 ?
13144                 this.emptyMsg :
13145                 String.format(
13146                     this.displayMsg,
13147                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13148                 );
13149             this.displayEl.update(msg);
13150         }
13151     },
13152
13153     // private
13154     onLoad : function(ds, r, o){
13155        this.cursor = o.params ? o.params.start : 0;
13156        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13157
13158        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13159        this.field.dom.value = ap;
13160        this.first.setDisabled(ap == 1);
13161        this.prev.setDisabled(ap == 1);
13162        this.next.setDisabled(ap == ps);
13163        this.last.setDisabled(ap == ps);
13164        this.loading.enable();
13165        this.updateInfo();
13166     },
13167
13168     // private
13169     getPageData : function(){
13170         var total = this.ds.getTotalCount();
13171         return {
13172             total : total,
13173             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13174             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13175         };
13176     },
13177
13178     // private
13179     onLoadError : function(){
13180         this.loading.enable();
13181     },
13182
13183     // private
13184     onPagingKeydown : function(e){
13185         var k = e.getKey();
13186         var d = this.getPageData();
13187         if(k == e.RETURN){
13188             var v = this.field.dom.value, pageNum;
13189             if(!v || isNaN(pageNum = parseInt(v, 10))){
13190                 this.field.dom.value = d.activePage;
13191                 return;
13192             }
13193             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13194             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13195             e.stopEvent();
13196         }
13197         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))
13198         {
13199           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13200           this.field.dom.value = pageNum;
13201           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13202           e.stopEvent();
13203         }
13204         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13205         {
13206           var v = this.field.dom.value, pageNum; 
13207           var increment = (e.shiftKey) ? 10 : 1;
13208           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13209             increment *= -1;
13210           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13211             this.field.dom.value = d.activePage;
13212             return;
13213           }
13214           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13215           {
13216             this.field.dom.value = parseInt(v, 10) + increment;
13217             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13218             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13219           }
13220           e.stopEvent();
13221         }
13222     },
13223
13224     // private
13225     beforeLoad : function(){
13226         if(this.loading){
13227             this.loading.disable();
13228         }
13229     },
13230
13231     // private
13232     onClick : function(which){
13233         var ds = this.ds;
13234         switch(which){
13235             case "first":
13236                 ds.load({params:{start: 0, limit: this.pageSize}});
13237             break;
13238             case "prev":
13239                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13240             break;
13241             case "next":
13242                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13243             break;
13244             case "last":
13245                 var total = ds.getTotalCount();
13246                 var extra = total % this.pageSize;
13247                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13248                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13249             break;
13250             case "refresh":
13251                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13252             break;
13253         }
13254     },
13255
13256     /**
13257      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13258      * @param {Roo.data.Store} store The data store to unbind
13259      */
13260     unbind : function(ds){
13261         ds.un("beforeload", this.beforeLoad, this);
13262         ds.un("load", this.onLoad, this);
13263         ds.un("loadexception", this.onLoadError, this);
13264         ds.un("remove", this.updateInfo, this);
13265         ds.un("add", this.updateInfo, this);
13266         this.ds = undefined;
13267     },
13268
13269     /**
13270      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13271      * @param {Roo.data.Store} store The data store to bind
13272      */
13273     bind : function(ds){
13274         ds.on("beforeload", this.beforeLoad, this);
13275         ds.on("load", this.onLoad, this);
13276         ds.on("loadexception", this.onLoadError, this);
13277         ds.on("remove", this.updateInfo, this);
13278         ds.on("add", this.updateInfo, this);
13279         this.ds = ds;
13280     }
13281 });/*
13282  * Based on:
13283  * Ext JS Library 1.1.1
13284  * Copyright(c) 2006-2007, Ext JS, LLC.
13285  *
13286  * Originally Released Under LGPL - original licence link has changed is not relivant.
13287  *
13288  * Fork - LGPL
13289  * <script type="text/javascript">
13290  */
13291
13292 /**
13293  * @class Roo.Resizable
13294  * @extends Roo.util.Observable
13295  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13296  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13297  * 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
13298  * the element will be wrapped for you automatically.</p>
13299  * <p>Here is the list of valid resize handles:</p>
13300  * <pre>
13301 Value   Description
13302 ------  -------------------
13303  'n'     north
13304  's'     south
13305  'e'     east
13306  'w'     west
13307  'nw'    northwest
13308  'sw'    southwest
13309  'se'    southeast
13310  'ne'    northeast
13311  'hd'    horizontal drag
13312  'all'   all
13313 </pre>
13314  * <p>Here's an example showing the creation of a typical Resizable:</p>
13315  * <pre><code>
13316 var resizer = new Roo.Resizable("element-id", {
13317     handles: 'all',
13318     minWidth: 200,
13319     minHeight: 100,
13320     maxWidth: 500,
13321     maxHeight: 400,
13322     pinned: true
13323 });
13324 resizer.on("resize", myHandler);
13325 </code></pre>
13326  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13327  * resizer.east.setDisplayed(false);</p>
13328  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13329  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13330  * resize operation's new size (defaults to [0, 0])
13331  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13332  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13333  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13334  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13335  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13336  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13337  * @cfg {Number} width The width of the element in pixels (defaults to null)
13338  * @cfg {Number} height The height of the element in pixels (defaults to null)
13339  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13340  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13341  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13342  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13343  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13344  * in favor of the handles config option (defaults to false)
13345  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13346  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13347  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13348  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13349  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13350  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13351  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13352  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13353  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13354  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13355  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13356  * @constructor
13357  * Create a new resizable component
13358  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13359  * @param {Object} config configuration options
13360   */
13361 Roo.Resizable = function(el, config)
13362 {
13363     this.el = Roo.get(el);
13364
13365     if(config && config.wrap){
13366         config.resizeChild = this.el;
13367         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13368         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13369         this.el.setStyle("overflow", "hidden");
13370         this.el.setPositioning(config.resizeChild.getPositioning());
13371         config.resizeChild.clearPositioning();
13372         if(!config.width || !config.height){
13373             var csize = config.resizeChild.getSize();
13374             this.el.setSize(csize.width, csize.height);
13375         }
13376         if(config.pinned && !config.adjustments){
13377             config.adjustments = "auto";
13378         }
13379     }
13380
13381     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13382     this.proxy.unselectable();
13383     this.proxy.enableDisplayMode('block');
13384
13385     Roo.apply(this, config);
13386
13387     if(this.pinned){
13388         this.disableTrackOver = true;
13389         this.el.addClass("x-resizable-pinned");
13390     }
13391     // if the element isn't positioned, make it relative
13392     var position = this.el.getStyle("position");
13393     if(position != "absolute" && position != "fixed"){
13394         this.el.setStyle("position", "relative");
13395     }
13396     if(!this.handles){ // no handles passed, must be legacy style
13397         this.handles = 's,e,se';
13398         if(this.multiDirectional){
13399             this.handles += ',n,w';
13400         }
13401     }
13402     if(this.handles == "all"){
13403         this.handles = "n s e w ne nw se sw";
13404     }
13405     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13406     var ps = Roo.Resizable.positions;
13407     for(var i = 0, len = hs.length; i < len; i++){
13408         if(hs[i] && ps[hs[i]]){
13409             var pos = ps[hs[i]];
13410             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13411         }
13412     }
13413     // legacy
13414     this.corner = this.southeast;
13415     
13416     // updateBox = the box can move..
13417     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13418         this.updateBox = true;
13419     }
13420
13421     this.activeHandle = null;
13422
13423     if(this.resizeChild){
13424         if(typeof this.resizeChild == "boolean"){
13425             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13426         }else{
13427             this.resizeChild = Roo.get(this.resizeChild, true);
13428         }
13429     }
13430     
13431     if(this.adjustments == "auto"){
13432         var rc = this.resizeChild;
13433         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13434         if(rc && (hw || hn)){
13435             rc.position("relative");
13436             rc.setLeft(hw ? hw.el.getWidth() : 0);
13437             rc.setTop(hn ? hn.el.getHeight() : 0);
13438         }
13439         this.adjustments = [
13440             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13441             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13442         ];
13443     }
13444
13445     if(this.draggable){
13446         this.dd = this.dynamic ?
13447             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13448         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13449     }
13450
13451     // public events
13452     this.addEvents({
13453         /**
13454          * @event beforeresize
13455          * Fired before resize is allowed. Set enabled to false to cancel resize.
13456          * @param {Roo.Resizable} this
13457          * @param {Roo.EventObject} e The mousedown event
13458          */
13459         "beforeresize" : true,
13460         /**
13461          * @event resize
13462          * Fired after a resize.
13463          * @param {Roo.Resizable} this
13464          * @param {Number} width The new width
13465          * @param {Number} height The new height
13466          * @param {Roo.EventObject} e The mouseup event
13467          */
13468         "resize" : true
13469     });
13470
13471     if(this.width !== null && this.height !== null){
13472         this.resizeTo(this.width, this.height);
13473     }else{
13474         this.updateChildSize();
13475     }
13476     if(Roo.isIE){
13477         this.el.dom.style.zoom = 1;
13478     }
13479     Roo.Resizable.superclass.constructor.call(this);
13480 };
13481
13482 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13483         resizeChild : false,
13484         adjustments : [0, 0],
13485         minWidth : 5,
13486         minHeight : 5,
13487         maxWidth : 10000,
13488         maxHeight : 10000,
13489         enabled : true,
13490         animate : false,
13491         duration : .35,
13492         dynamic : false,
13493         handles : false,
13494         multiDirectional : false,
13495         disableTrackOver : false,
13496         easing : 'easeOutStrong',
13497         widthIncrement : 0,
13498         heightIncrement : 0,
13499         pinned : false,
13500         width : null,
13501         height : null,
13502         preserveRatio : false,
13503         transparent: false,
13504         minX: 0,
13505         minY: 0,
13506         draggable: false,
13507
13508         /**
13509          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13510          */
13511         constrainTo: undefined,
13512         /**
13513          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13514          */
13515         resizeRegion: undefined,
13516
13517
13518     /**
13519      * Perform a manual resize
13520      * @param {Number} width
13521      * @param {Number} height
13522      */
13523     resizeTo : function(width, height){
13524         this.el.setSize(width, height);
13525         this.updateChildSize();
13526         this.fireEvent("resize", this, width, height, null);
13527     },
13528
13529     // private
13530     startSizing : function(e, handle){
13531         this.fireEvent("beforeresize", this, e);
13532         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13533
13534             if(!this.overlay){
13535                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13536                 this.overlay.unselectable();
13537                 this.overlay.enableDisplayMode("block");
13538                 this.overlay.on("mousemove", this.onMouseMove, this);
13539                 this.overlay.on("mouseup", this.onMouseUp, this);
13540             }
13541             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13542
13543             this.resizing = true;
13544             this.startBox = this.el.getBox();
13545             this.startPoint = e.getXY();
13546             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13547                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13548
13549             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13550             this.overlay.show();
13551
13552             if(this.constrainTo) {
13553                 var ct = Roo.get(this.constrainTo);
13554                 this.resizeRegion = ct.getRegion().adjust(
13555                     ct.getFrameWidth('t'),
13556                     ct.getFrameWidth('l'),
13557                     -ct.getFrameWidth('b'),
13558                     -ct.getFrameWidth('r')
13559                 );
13560             }
13561
13562             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13563             this.proxy.show();
13564             this.proxy.setBox(this.startBox);
13565             if(!this.dynamic){
13566                 this.proxy.setStyle('visibility', 'visible');
13567             }
13568         }
13569     },
13570
13571     // private
13572     onMouseDown : function(handle, e){
13573         if(this.enabled){
13574             e.stopEvent();
13575             this.activeHandle = handle;
13576             this.startSizing(e, handle);
13577         }
13578     },
13579
13580     // private
13581     onMouseUp : function(e){
13582         var size = this.resizeElement();
13583         this.resizing = false;
13584         this.handleOut();
13585         this.overlay.hide();
13586         this.proxy.hide();
13587         this.fireEvent("resize", this, size.width, size.height, e);
13588     },
13589
13590     // private
13591     updateChildSize : function(){
13592         if(this.resizeChild){
13593             var el = this.el;
13594             var child = this.resizeChild;
13595             var adj = this.adjustments;
13596             if(el.dom.offsetWidth){
13597                 var b = el.getSize(true);
13598                 child.setSize(b.width+adj[0], b.height+adj[1]);
13599             }
13600             // Second call here for IE
13601             // The first call enables instant resizing and
13602             // the second call corrects scroll bars if they
13603             // exist
13604             if(Roo.isIE){
13605                 setTimeout(function(){
13606                     if(el.dom.offsetWidth){
13607                         var b = el.getSize(true);
13608                         child.setSize(b.width+adj[0], b.height+adj[1]);
13609                     }
13610                 }, 10);
13611             }
13612         }
13613     },
13614
13615     // private
13616     snap : function(value, inc, min){
13617         if(!inc || !value) return value;
13618         var newValue = value;
13619         var m = value % inc;
13620         if(m > 0){
13621             if(m > (inc/2)){
13622                 newValue = value + (inc-m);
13623             }else{
13624                 newValue = value - m;
13625             }
13626         }
13627         return Math.max(min, newValue);
13628     },
13629
13630     // private
13631     resizeElement : function(){
13632         var box = this.proxy.getBox();
13633         if(this.updateBox){
13634             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13635         }else{
13636             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13637         }
13638         this.updateChildSize();
13639         if(!this.dynamic){
13640             this.proxy.hide();
13641         }
13642         return box;
13643     },
13644
13645     // private
13646     constrain : function(v, diff, m, mx){
13647         if(v - diff < m){
13648             diff = v - m;
13649         }else if(v - diff > mx){
13650             diff = mx - v;
13651         }
13652         return diff;
13653     },
13654
13655     // private
13656     onMouseMove : function(e){
13657         if(this.enabled){
13658             try{// try catch so if something goes wrong the user doesn't get hung
13659
13660             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13661                 return;
13662             }
13663
13664             //var curXY = this.startPoint;
13665             var curSize = this.curSize || this.startBox;
13666             var x = this.startBox.x, y = this.startBox.y;
13667             var ox = x, oy = y;
13668             var w = curSize.width, h = curSize.height;
13669             var ow = w, oh = h;
13670             var mw = this.minWidth, mh = this.minHeight;
13671             var mxw = this.maxWidth, mxh = this.maxHeight;
13672             var wi = this.widthIncrement;
13673             var hi = this.heightIncrement;
13674
13675             var eventXY = e.getXY();
13676             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13677             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13678
13679             var pos = this.activeHandle.position;
13680
13681             switch(pos){
13682                 case "east":
13683                     w += diffX;
13684                     w = Math.min(Math.max(mw, w), mxw);
13685                     break;
13686              
13687                 case "south":
13688                     h += diffY;
13689                     h = Math.min(Math.max(mh, h), mxh);
13690                     break;
13691                 case "southeast":
13692                     w += diffX;
13693                     h += diffY;
13694                     w = Math.min(Math.max(mw, w), mxw);
13695                     h = Math.min(Math.max(mh, h), mxh);
13696                     break;
13697                 case "north":
13698                     diffY = this.constrain(h, diffY, mh, mxh);
13699                     y += diffY;
13700                     h -= diffY;
13701                     break;
13702                 case "hdrag":
13703                     
13704                     if (wi) {
13705                         var adiffX = Math.abs(diffX);
13706                         var sub = (adiffX % wi); // how much 
13707                         if (sub > (wi/2)) { // far enough to snap
13708                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13709                         } else {
13710                             // remove difference.. 
13711                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13712                         }
13713                     }
13714                     x += diffX;
13715                     x = Math.max(this.minX, x);
13716                     break;
13717                 case "west":
13718                     diffX = this.constrain(w, diffX, mw, mxw);
13719                     x += diffX;
13720                     w -= diffX;
13721                     break;
13722                 case "northeast":
13723                     w += diffX;
13724                     w = Math.min(Math.max(mw, w), mxw);
13725                     diffY = this.constrain(h, diffY, mh, mxh);
13726                     y += diffY;
13727                     h -= diffY;
13728                     break;
13729                 case "northwest":
13730                     diffX = this.constrain(w, diffX, mw, mxw);
13731                     diffY = this.constrain(h, diffY, mh, mxh);
13732                     y += diffY;
13733                     h -= diffY;
13734                     x += diffX;
13735                     w -= diffX;
13736                     break;
13737                case "southwest":
13738                     diffX = this.constrain(w, diffX, mw, mxw);
13739                     h += diffY;
13740                     h = Math.min(Math.max(mh, h), mxh);
13741                     x += diffX;
13742                     w -= diffX;
13743                     break;
13744             }
13745
13746             var sw = this.snap(w, wi, mw);
13747             var sh = this.snap(h, hi, mh);
13748             if(sw != w || sh != h){
13749                 switch(pos){
13750                     case "northeast":
13751                         y -= sh - h;
13752                     break;
13753                     case "north":
13754                         y -= sh - h;
13755                         break;
13756                     case "southwest":
13757                         x -= sw - w;
13758                     break;
13759                     case "west":
13760                         x -= sw - w;
13761                         break;
13762                     case "northwest":
13763                         x -= sw - w;
13764                         y -= sh - h;
13765                     break;
13766                 }
13767                 w = sw;
13768                 h = sh;
13769             }
13770
13771             if(this.preserveRatio){
13772                 switch(pos){
13773                     case "southeast":
13774                     case "east":
13775                         h = oh * (w/ow);
13776                         h = Math.min(Math.max(mh, h), mxh);
13777                         w = ow * (h/oh);
13778                        break;
13779                     case "south":
13780                         w = ow * (h/oh);
13781                         w = Math.min(Math.max(mw, w), mxw);
13782                         h = oh * (w/ow);
13783                         break;
13784                     case "northeast":
13785                         w = ow * (h/oh);
13786                         w = Math.min(Math.max(mw, w), mxw);
13787                         h = oh * (w/ow);
13788                     break;
13789                     case "north":
13790                         var tw = w;
13791                         w = ow * (h/oh);
13792                         w = Math.min(Math.max(mw, w), mxw);
13793                         h = oh * (w/ow);
13794                         x += (tw - w) / 2;
13795                         break;
13796                     case "southwest":
13797                         h = oh * (w/ow);
13798                         h = Math.min(Math.max(mh, h), mxh);
13799                         var tw = w;
13800                         w = ow * (h/oh);
13801                         x += tw - w;
13802                         break;
13803                     case "west":
13804                         var th = h;
13805                         h = oh * (w/ow);
13806                         h = Math.min(Math.max(mh, h), mxh);
13807                         y += (th - h) / 2;
13808                         var tw = w;
13809                         w = ow * (h/oh);
13810                         x += tw - w;
13811                        break;
13812                     case "northwest":
13813                         var tw = w;
13814                         var th = h;
13815                         h = oh * (w/ow);
13816                         h = Math.min(Math.max(mh, h), mxh);
13817                         w = ow * (h/oh);
13818                         y += th - h;
13819                         x += tw - w;
13820                        break;
13821
13822                 }
13823             }
13824             if (pos == 'hdrag') {
13825                 w = ow;
13826             }
13827             this.proxy.setBounds(x, y, w, h);
13828             if(this.dynamic){
13829                 this.resizeElement();
13830             }
13831             }catch(e){}
13832         }
13833     },
13834
13835     // private
13836     handleOver : function(){
13837         if(this.enabled){
13838             this.el.addClass("x-resizable-over");
13839         }
13840     },
13841
13842     // private
13843     handleOut : function(){
13844         if(!this.resizing){
13845             this.el.removeClass("x-resizable-over");
13846         }
13847     },
13848
13849     /**
13850      * Returns the element this component is bound to.
13851      * @return {Roo.Element}
13852      */
13853     getEl : function(){
13854         return this.el;
13855     },
13856
13857     /**
13858      * Returns the resizeChild element (or null).
13859      * @return {Roo.Element}
13860      */
13861     getResizeChild : function(){
13862         return this.resizeChild;
13863     },
13864
13865     /**
13866      * Destroys this resizable. If the element was wrapped and
13867      * removeEl is not true then the element remains.
13868      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13869      */
13870     destroy : function(removeEl){
13871         this.proxy.remove();
13872         if(this.overlay){
13873             this.overlay.removeAllListeners();
13874             this.overlay.remove();
13875         }
13876         var ps = Roo.Resizable.positions;
13877         for(var k in ps){
13878             if(typeof ps[k] != "function" && this[ps[k]]){
13879                 var h = this[ps[k]];
13880                 h.el.removeAllListeners();
13881                 h.el.remove();
13882             }
13883         }
13884         if(removeEl){
13885             this.el.update("");
13886             this.el.remove();
13887         }
13888     }
13889 });
13890
13891 // private
13892 // hash to map config positions to true positions
13893 Roo.Resizable.positions = {
13894     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13895     hd: "hdrag"
13896 };
13897
13898 // private
13899 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13900     if(!this.tpl){
13901         // only initialize the template if resizable is used
13902         var tpl = Roo.DomHelper.createTemplate(
13903             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13904         );
13905         tpl.compile();
13906         Roo.Resizable.Handle.prototype.tpl = tpl;
13907     }
13908     this.position = pos;
13909     this.rz = rz;
13910     // show north drag fro topdra
13911     var handlepos = pos == 'hdrag' ? 'north' : pos;
13912     
13913     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13914     if (pos == 'hdrag') {
13915         this.el.setStyle('cursor', 'pointer');
13916     }
13917     this.el.unselectable();
13918     if(transparent){
13919         this.el.setOpacity(0);
13920     }
13921     this.el.on("mousedown", this.onMouseDown, this);
13922     if(!disableTrackOver){
13923         this.el.on("mouseover", this.onMouseOver, this);
13924         this.el.on("mouseout", this.onMouseOut, this);
13925     }
13926 };
13927
13928 // private
13929 Roo.Resizable.Handle.prototype = {
13930     afterResize : function(rz){
13931         // do nothing
13932     },
13933     // private
13934     onMouseDown : function(e){
13935         this.rz.onMouseDown(this, e);
13936     },
13937     // private
13938     onMouseOver : function(e){
13939         this.rz.handleOver(this, e);
13940     },
13941     // private
13942     onMouseOut : function(e){
13943         this.rz.handleOut(this, e);
13944     }
13945 };/*
13946  * Based on:
13947  * Ext JS Library 1.1.1
13948  * Copyright(c) 2006-2007, Ext JS, LLC.
13949  *
13950  * Originally Released Under LGPL - original licence link has changed is not relivant.
13951  *
13952  * Fork - LGPL
13953  * <script type="text/javascript">
13954  */
13955
13956 /**
13957  * @class Roo.Editor
13958  * @extends Roo.Component
13959  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13960  * @constructor
13961  * Create a new Editor
13962  * @param {Roo.form.Field} field The Field object (or descendant)
13963  * @param {Object} config The config object
13964  */
13965 Roo.Editor = function(field, config){
13966     Roo.Editor.superclass.constructor.call(this, config);
13967     this.field = field;
13968     this.addEvents({
13969         /**
13970              * @event beforestartedit
13971              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13972              * false from the handler of this event.
13973              * @param {Editor} this
13974              * @param {Roo.Element} boundEl The underlying element bound to this editor
13975              * @param {Mixed} value The field value being set
13976              */
13977         "beforestartedit" : true,
13978         /**
13979              * @event startedit
13980              * Fires when this editor is displayed
13981              * @param {Roo.Element} boundEl The underlying element bound to this editor
13982              * @param {Mixed} value The starting field value
13983              */
13984         "startedit" : true,
13985         /**
13986              * @event beforecomplete
13987              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13988              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13989              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13990              * event will not fire since no edit actually occurred.
13991              * @param {Editor} this
13992              * @param {Mixed} value The current field value
13993              * @param {Mixed} startValue The original field value
13994              */
13995         "beforecomplete" : true,
13996         /**
13997              * @event complete
13998              * Fires after editing is complete and any changed value has been written to the underlying field.
13999              * @param {Editor} this
14000              * @param {Mixed} value The current field value
14001              * @param {Mixed} startValue The original field value
14002              */
14003         "complete" : true,
14004         /**
14005          * @event specialkey
14006          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14007          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14008          * @param {Roo.form.Field} this
14009          * @param {Roo.EventObject} e The event object
14010          */
14011         "specialkey" : true
14012     });
14013 };
14014
14015 Roo.extend(Roo.Editor, Roo.Component, {
14016     /**
14017      * @cfg {Boolean/String} autosize
14018      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14019      * or "height" to adopt the height only (defaults to false)
14020      */
14021     /**
14022      * @cfg {Boolean} revertInvalid
14023      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14024      * validation fails (defaults to true)
14025      */
14026     /**
14027      * @cfg {Boolean} ignoreNoChange
14028      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14029      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14030      * will never be ignored.
14031      */
14032     /**
14033      * @cfg {Boolean} hideEl
14034      * False to keep the bound element visible while the editor is displayed (defaults to true)
14035      */
14036     /**
14037      * @cfg {Mixed} value
14038      * The data value of the underlying field (defaults to "")
14039      */
14040     value : "",
14041     /**
14042      * @cfg {String} alignment
14043      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14044      */
14045     alignment: "c-c?",
14046     /**
14047      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14048      * for bottom-right shadow (defaults to "frame")
14049      */
14050     shadow : "frame",
14051     /**
14052      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14053      */
14054     constrain : false,
14055     /**
14056      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14057      */
14058     completeOnEnter : false,
14059     /**
14060      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14061      */
14062     cancelOnEsc : false,
14063     /**
14064      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14065      */
14066     updateEl : false,
14067
14068     // private
14069     onRender : function(ct, position){
14070         this.el = new Roo.Layer({
14071             shadow: this.shadow,
14072             cls: "x-editor",
14073             parentEl : ct,
14074             shim : this.shim,
14075             shadowOffset:4,
14076             id: this.id,
14077             constrain: this.constrain
14078         });
14079         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14080         if(this.field.msgTarget != 'title'){
14081             this.field.msgTarget = 'qtip';
14082         }
14083         this.field.render(this.el);
14084         if(Roo.isGecko){
14085             this.field.el.dom.setAttribute('autocomplete', 'off');
14086         }
14087         this.field.on("specialkey", this.onSpecialKey, this);
14088         if(this.swallowKeys){
14089             this.field.el.swallowEvent(['keydown','keypress']);
14090         }
14091         this.field.show();
14092         this.field.on("blur", this.onBlur, this);
14093         if(this.field.grow){
14094             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14095         }
14096     },
14097
14098     onSpecialKey : function(field, e)
14099     {
14100         //Roo.log('editor onSpecialKey');
14101         if(this.completeOnEnter && e.getKey() == e.ENTER){
14102             e.stopEvent();
14103             this.completeEdit();
14104             return;
14105         }
14106         // do not fire special key otherwise it might hide close the editor...
14107         if(e.getKey() == e.ENTER){    
14108             return;
14109         }
14110         if(this.cancelOnEsc && e.getKey() == e.ESC){
14111             this.cancelEdit();
14112             return;
14113         } 
14114         this.fireEvent('specialkey', field, e);
14115     
14116     },
14117
14118     /**
14119      * Starts the editing process and shows the editor.
14120      * @param {String/HTMLElement/Element} el The element to edit
14121      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14122       * to the innerHTML of el.
14123      */
14124     startEdit : function(el, value){
14125         if(this.editing){
14126             this.completeEdit();
14127         }
14128         this.boundEl = Roo.get(el);
14129         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14130         if(!this.rendered){
14131             this.render(this.parentEl || document.body);
14132         }
14133         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14134             return;
14135         }
14136         this.startValue = v;
14137         this.field.setValue(v);
14138         if(this.autoSize){
14139             var sz = this.boundEl.getSize();
14140             switch(this.autoSize){
14141                 case "width":
14142                 this.setSize(sz.width,  "");
14143                 break;
14144                 case "height":
14145                 this.setSize("",  sz.height);
14146                 break;
14147                 default:
14148                 this.setSize(sz.width,  sz.height);
14149             }
14150         }
14151         this.el.alignTo(this.boundEl, this.alignment);
14152         this.editing = true;
14153         if(Roo.QuickTips){
14154             Roo.QuickTips.disable();
14155         }
14156         this.show();
14157     },
14158
14159     /**
14160      * Sets the height and width of this editor.
14161      * @param {Number} width The new width
14162      * @param {Number} height The new height
14163      */
14164     setSize : function(w, h){
14165         this.field.setSize(w, h);
14166         if(this.el){
14167             this.el.sync();
14168         }
14169     },
14170
14171     /**
14172      * Realigns the editor to the bound field based on the current alignment config value.
14173      */
14174     realign : function(){
14175         this.el.alignTo(this.boundEl, this.alignment);
14176     },
14177
14178     /**
14179      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14180      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14181      */
14182     completeEdit : function(remainVisible){
14183         if(!this.editing){
14184             return;
14185         }
14186         var v = this.getValue();
14187         if(this.revertInvalid !== false && !this.field.isValid()){
14188             v = this.startValue;
14189             this.cancelEdit(true);
14190         }
14191         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14192             this.editing = false;
14193             this.hide();
14194             return;
14195         }
14196         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14197             this.editing = false;
14198             if(this.updateEl && this.boundEl){
14199                 this.boundEl.update(v);
14200             }
14201             if(remainVisible !== true){
14202                 this.hide();
14203             }
14204             this.fireEvent("complete", this, v, this.startValue);
14205         }
14206     },
14207
14208     // private
14209     onShow : function(){
14210         this.el.show();
14211         if(this.hideEl !== false){
14212             this.boundEl.hide();
14213         }
14214         this.field.show();
14215         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14216             this.fixIEFocus = true;
14217             this.deferredFocus.defer(50, this);
14218         }else{
14219             this.field.focus();
14220         }
14221         this.fireEvent("startedit", this.boundEl, this.startValue);
14222     },
14223
14224     deferredFocus : function(){
14225         if(this.editing){
14226             this.field.focus();
14227         }
14228     },
14229
14230     /**
14231      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14232      * reverted to the original starting value.
14233      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14234      * cancel (defaults to false)
14235      */
14236     cancelEdit : function(remainVisible){
14237         if(this.editing){
14238             this.setValue(this.startValue);
14239             if(remainVisible !== true){
14240                 this.hide();
14241             }
14242         }
14243     },
14244
14245     // private
14246     onBlur : function(){
14247         if(this.allowBlur !== true && this.editing){
14248             this.completeEdit();
14249         }
14250     },
14251
14252     // private
14253     onHide : function(){
14254         if(this.editing){
14255             this.completeEdit();
14256             return;
14257         }
14258         this.field.blur();
14259         if(this.field.collapse){
14260             this.field.collapse();
14261         }
14262         this.el.hide();
14263         if(this.hideEl !== false){
14264             this.boundEl.show();
14265         }
14266         if(Roo.QuickTips){
14267             Roo.QuickTips.enable();
14268         }
14269     },
14270
14271     /**
14272      * Sets the data value of the editor
14273      * @param {Mixed} value Any valid value supported by the underlying field
14274      */
14275     setValue : function(v){
14276         this.field.setValue(v);
14277     },
14278
14279     /**
14280      * Gets the data value of the editor
14281      * @return {Mixed} The data value
14282      */
14283     getValue : function(){
14284         return this.field.getValue();
14285     }
14286 });/*
14287  * Based on:
14288  * Ext JS Library 1.1.1
14289  * Copyright(c) 2006-2007, Ext JS, LLC.
14290  *
14291  * Originally Released Under LGPL - original licence link has changed is not relivant.
14292  *
14293  * Fork - LGPL
14294  * <script type="text/javascript">
14295  */
14296  
14297 /**
14298  * @class Roo.BasicDialog
14299  * @extends Roo.util.Observable
14300  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14301  * <pre><code>
14302 var dlg = new Roo.BasicDialog("my-dlg", {
14303     height: 200,
14304     width: 300,
14305     minHeight: 100,
14306     minWidth: 150,
14307     modal: true,
14308     proxyDrag: true,
14309     shadow: true
14310 });
14311 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14312 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14313 dlg.addButton('Cancel', dlg.hide, dlg);
14314 dlg.show();
14315 </code></pre>
14316   <b>A Dialog should always be a direct child of the body element.</b>
14317  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14318  * @cfg {String} title Default text to display in the title bar (defaults to null)
14319  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14320  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14321  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14322  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14323  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14324  * (defaults to null with no animation)
14325  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14326  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14327  * property for valid values (defaults to 'all')
14328  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14329  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14330  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14331  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14332  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14333  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14334  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14335  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14336  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14337  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14338  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14339  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14340  * draggable = true (defaults to false)
14341  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14342  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14343  * shadow (defaults to false)
14344  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14345  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14346  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14347  * @cfg {Array} buttons Array of buttons
14348  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14349  * @constructor
14350  * Create a new BasicDialog.
14351  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14352  * @param {Object} config Configuration options
14353  */
14354 Roo.BasicDialog = function(el, config){
14355     this.el = Roo.get(el);
14356     var dh = Roo.DomHelper;
14357     if(!this.el && config && config.autoCreate){
14358         if(typeof config.autoCreate == "object"){
14359             if(!config.autoCreate.id){
14360                 config.autoCreate.id = el;
14361             }
14362             this.el = dh.append(document.body,
14363                         config.autoCreate, true);
14364         }else{
14365             this.el = dh.append(document.body,
14366                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14367         }
14368     }
14369     el = this.el;
14370     el.setDisplayed(true);
14371     el.hide = this.hideAction;
14372     this.id = el.id;
14373     el.addClass("x-dlg");
14374
14375     Roo.apply(this, config);
14376
14377     this.proxy = el.createProxy("x-dlg-proxy");
14378     this.proxy.hide = this.hideAction;
14379     this.proxy.setOpacity(.5);
14380     this.proxy.hide();
14381
14382     if(config.width){
14383         el.setWidth(config.width);
14384     }
14385     if(config.height){
14386         el.setHeight(config.height);
14387     }
14388     this.size = el.getSize();
14389     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14390         this.xy = [config.x,config.y];
14391     }else{
14392         this.xy = el.getCenterXY(true);
14393     }
14394     /** The header element @type Roo.Element */
14395     this.header = el.child("> .x-dlg-hd");
14396     /** The body element @type Roo.Element */
14397     this.body = el.child("> .x-dlg-bd");
14398     /** The footer element @type Roo.Element */
14399     this.footer = el.child("> .x-dlg-ft");
14400
14401     if(!this.header){
14402         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14403     }
14404     if(!this.body){
14405         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14406     }
14407
14408     this.header.unselectable();
14409     if(this.title){
14410         this.header.update(this.title);
14411     }
14412     // this element allows the dialog to be focused for keyboard event
14413     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14414     this.focusEl.swallowEvent("click", true);
14415
14416     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14417
14418     // wrap the body and footer for special rendering
14419     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14420     if(this.footer){
14421         this.bwrap.dom.appendChild(this.footer.dom);
14422     }
14423
14424     this.bg = this.el.createChild({
14425         tag: "div", cls:"x-dlg-bg",
14426         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14427     });
14428     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14429
14430
14431     if(this.autoScroll !== false && !this.autoTabs){
14432         this.body.setStyle("overflow", "auto");
14433     }
14434
14435     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14436
14437     if(this.closable !== false){
14438         this.el.addClass("x-dlg-closable");
14439         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14440         this.close.on("click", this.closeClick, this);
14441         this.close.addClassOnOver("x-dlg-close-over");
14442     }
14443     if(this.collapsible !== false){
14444         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14445         this.collapseBtn.on("click", this.collapseClick, this);
14446         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14447         this.header.on("dblclick", this.collapseClick, this);
14448     }
14449     if(this.resizable !== false){
14450         this.el.addClass("x-dlg-resizable");
14451         this.resizer = new Roo.Resizable(el, {
14452             minWidth: this.minWidth || 80,
14453             minHeight:this.minHeight || 80,
14454             handles: this.resizeHandles || "all",
14455             pinned: true
14456         });
14457         this.resizer.on("beforeresize", this.beforeResize, this);
14458         this.resizer.on("resize", this.onResize, this);
14459     }
14460     if(this.draggable !== false){
14461         el.addClass("x-dlg-draggable");
14462         if (!this.proxyDrag) {
14463             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14464         }
14465         else {
14466             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14467         }
14468         dd.setHandleElId(this.header.id);
14469         dd.endDrag = this.endMove.createDelegate(this);
14470         dd.startDrag = this.startMove.createDelegate(this);
14471         dd.onDrag = this.onDrag.createDelegate(this);
14472         dd.scroll = false;
14473         this.dd = dd;
14474     }
14475     if(this.modal){
14476         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14477         this.mask.enableDisplayMode("block");
14478         this.mask.hide();
14479         this.el.addClass("x-dlg-modal");
14480     }
14481     if(this.shadow){
14482         this.shadow = new Roo.Shadow({
14483             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14484             offset : this.shadowOffset
14485         });
14486     }else{
14487         this.shadowOffset = 0;
14488     }
14489     if(Roo.useShims && this.shim !== false){
14490         this.shim = this.el.createShim();
14491         this.shim.hide = this.hideAction;
14492         this.shim.hide();
14493     }else{
14494         this.shim = false;
14495     }
14496     if(this.autoTabs){
14497         this.initTabs();
14498     }
14499     if (this.buttons) { 
14500         var bts= this.buttons;
14501         this.buttons = [];
14502         Roo.each(bts, function(b) {
14503             this.addButton(b);
14504         }, this);
14505     }
14506     
14507     
14508     this.addEvents({
14509         /**
14510          * @event keydown
14511          * Fires when a key is pressed
14512          * @param {Roo.BasicDialog} this
14513          * @param {Roo.EventObject} e
14514          */
14515         "keydown" : true,
14516         /**
14517          * @event move
14518          * Fires when this dialog is moved by the user.
14519          * @param {Roo.BasicDialog} this
14520          * @param {Number} x The new page X
14521          * @param {Number} y The new page Y
14522          */
14523         "move" : true,
14524         /**
14525          * @event resize
14526          * Fires when this dialog is resized by the user.
14527          * @param {Roo.BasicDialog} this
14528          * @param {Number} width The new width
14529          * @param {Number} height The new height
14530          */
14531         "resize" : true,
14532         /**
14533          * @event beforehide
14534          * Fires before this dialog is hidden.
14535          * @param {Roo.BasicDialog} this
14536          */
14537         "beforehide" : true,
14538         /**
14539          * @event hide
14540          * Fires when this dialog is hidden.
14541          * @param {Roo.BasicDialog} this
14542          */
14543         "hide" : true,
14544         /**
14545          * @event beforeshow
14546          * Fires before this dialog is shown.
14547          * @param {Roo.BasicDialog} this
14548          */
14549         "beforeshow" : true,
14550         /**
14551          * @event show
14552          * Fires when this dialog is shown.
14553          * @param {Roo.BasicDialog} this
14554          */
14555         "show" : true
14556     });
14557     el.on("keydown", this.onKeyDown, this);
14558     el.on("mousedown", this.toFront, this);
14559     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14560     this.el.hide();
14561     Roo.DialogManager.register(this);
14562     Roo.BasicDialog.superclass.constructor.call(this);
14563 };
14564
14565 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14566     shadowOffset: Roo.isIE ? 6 : 5,
14567     minHeight: 80,
14568     minWidth: 200,
14569     minButtonWidth: 75,
14570     defaultButton: null,
14571     buttonAlign: "right",
14572     tabTag: 'div',
14573     firstShow: true,
14574
14575     /**
14576      * Sets the dialog title text
14577      * @param {String} text The title text to display
14578      * @return {Roo.BasicDialog} this
14579      */
14580     setTitle : function(text){
14581         this.header.update(text);
14582         return this;
14583     },
14584
14585     // private
14586     closeClick : function(){
14587         this.hide();
14588     },
14589
14590     // private
14591     collapseClick : function(){
14592         this[this.collapsed ? "expand" : "collapse"]();
14593     },
14594
14595     /**
14596      * Collapses the dialog to its minimized state (only the title bar is visible).
14597      * Equivalent to the user clicking the collapse dialog button.
14598      */
14599     collapse : function(){
14600         if(!this.collapsed){
14601             this.collapsed = true;
14602             this.el.addClass("x-dlg-collapsed");
14603             this.restoreHeight = this.el.getHeight();
14604             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14605         }
14606     },
14607
14608     /**
14609      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14610      * clicking the expand dialog button.
14611      */
14612     expand : function(){
14613         if(this.collapsed){
14614             this.collapsed = false;
14615             this.el.removeClass("x-dlg-collapsed");
14616             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14617         }
14618     },
14619
14620     /**
14621      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14622      * @return {Roo.TabPanel} The tabs component
14623      */
14624     initTabs : function(){
14625         var tabs = this.getTabs();
14626         while(tabs.getTab(0)){
14627             tabs.removeTab(0);
14628         }
14629         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14630             var dom = el.dom;
14631             tabs.addTab(Roo.id(dom), dom.title);
14632             dom.title = "";
14633         });
14634         tabs.activate(0);
14635         return tabs;
14636     },
14637
14638     // private
14639     beforeResize : function(){
14640         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14641     },
14642
14643     // private
14644     onResize : function(){
14645         this.refreshSize();
14646         this.syncBodyHeight();
14647         this.adjustAssets();
14648         this.focus();
14649         this.fireEvent("resize", this, this.size.width, this.size.height);
14650     },
14651
14652     // private
14653     onKeyDown : function(e){
14654         if(this.isVisible()){
14655             this.fireEvent("keydown", this, e);
14656         }
14657     },
14658
14659     /**
14660      * Resizes the dialog.
14661      * @param {Number} width
14662      * @param {Number} height
14663      * @return {Roo.BasicDialog} this
14664      */
14665     resizeTo : function(width, height){
14666         this.el.setSize(width, height);
14667         this.size = {width: width, height: height};
14668         this.syncBodyHeight();
14669         if(this.fixedcenter){
14670             this.center();
14671         }
14672         if(this.isVisible()){
14673             this.constrainXY();
14674             this.adjustAssets();
14675         }
14676         this.fireEvent("resize", this, width, height);
14677         return this;
14678     },
14679
14680
14681     /**
14682      * Resizes the dialog to fit the specified content size.
14683      * @param {Number} width
14684      * @param {Number} height
14685      * @return {Roo.BasicDialog} this
14686      */
14687     setContentSize : function(w, h){
14688         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14689         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14690         //if(!this.el.isBorderBox()){
14691             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14692             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14693         //}
14694         if(this.tabs){
14695             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14696             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14697         }
14698         this.resizeTo(w, h);
14699         return this;
14700     },
14701
14702     /**
14703      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14704      * executed in response to a particular key being pressed while the dialog is active.
14705      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14706      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14707      * @param {Function} fn The function to call
14708      * @param {Object} scope (optional) The scope of the function
14709      * @return {Roo.BasicDialog} this
14710      */
14711     addKeyListener : function(key, fn, scope){
14712         var keyCode, shift, ctrl, alt;
14713         if(typeof key == "object" && !(key instanceof Array)){
14714             keyCode = key["key"];
14715             shift = key["shift"];
14716             ctrl = key["ctrl"];
14717             alt = key["alt"];
14718         }else{
14719             keyCode = key;
14720         }
14721         var handler = function(dlg, e){
14722             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14723                 var k = e.getKey();
14724                 if(keyCode instanceof Array){
14725                     for(var i = 0, len = keyCode.length; i < len; i++){
14726                         if(keyCode[i] == k){
14727                           fn.call(scope || window, dlg, k, e);
14728                           return;
14729                         }
14730                     }
14731                 }else{
14732                     if(k == keyCode){
14733                         fn.call(scope || window, dlg, k, e);
14734                     }
14735                 }
14736             }
14737         };
14738         this.on("keydown", handler);
14739         return this;
14740     },
14741
14742     /**
14743      * Returns the TabPanel component (creates it if it doesn't exist).
14744      * Note: If you wish to simply check for the existence of tabs without creating them,
14745      * check for a null 'tabs' property.
14746      * @return {Roo.TabPanel} The tabs component
14747      */
14748     getTabs : function(){
14749         if(!this.tabs){
14750             this.el.addClass("x-dlg-auto-tabs");
14751             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14752             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14753         }
14754         return this.tabs;
14755     },
14756
14757     /**
14758      * Adds a button to the footer section of the dialog.
14759      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14760      * object or a valid Roo.DomHelper element config
14761      * @param {Function} handler The function called when the button is clicked
14762      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14763      * @return {Roo.Button} The new button
14764      */
14765     addButton : function(config, handler, scope){
14766         var dh = Roo.DomHelper;
14767         if(!this.footer){
14768             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14769         }
14770         if(!this.btnContainer){
14771             var tb = this.footer.createChild({
14772
14773                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14774                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14775             }, null, true);
14776             this.btnContainer = tb.firstChild.firstChild.firstChild;
14777         }
14778         var bconfig = {
14779             handler: handler,
14780             scope: scope,
14781             minWidth: this.minButtonWidth,
14782             hideParent:true
14783         };
14784         if(typeof config == "string"){
14785             bconfig.text = config;
14786         }else{
14787             if(config.tag){
14788                 bconfig.dhconfig = config;
14789             }else{
14790                 Roo.apply(bconfig, config);
14791             }
14792         }
14793         var fc = false;
14794         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14795             bconfig.position = Math.max(0, bconfig.position);
14796             fc = this.btnContainer.childNodes[bconfig.position];
14797         }
14798          
14799         var btn = new Roo.Button(
14800             fc ? 
14801                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14802                 : this.btnContainer.appendChild(document.createElement("td")),
14803             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14804             bconfig
14805         );
14806         this.syncBodyHeight();
14807         if(!this.buttons){
14808             /**
14809              * Array of all the buttons that have been added to this dialog via addButton
14810              * @type Array
14811              */
14812             this.buttons = [];
14813         }
14814         this.buttons.push(btn);
14815         return btn;
14816     },
14817
14818     /**
14819      * Sets the default button to be focused when the dialog is displayed.
14820      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14821      * @return {Roo.BasicDialog} this
14822      */
14823     setDefaultButton : function(btn){
14824         this.defaultButton = btn;
14825         return this;
14826     },
14827
14828     // private
14829     getHeaderFooterHeight : function(safe){
14830         var height = 0;
14831         if(this.header){
14832            height += this.header.getHeight();
14833         }
14834         if(this.footer){
14835            var fm = this.footer.getMargins();
14836             height += (this.footer.getHeight()+fm.top+fm.bottom);
14837         }
14838         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14839         height += this.centerBg.getPadding("tb");
14840         return height;
14841     },
14842
14843     // private
14844     syncBodyHeight : function(){
14845         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14846         var height = this.size.height - this.getHeaderFooterHeight(false);
14847         bd.setHeight(height-bd.getMargins("tb"));
14848         var hh = this.header.getHeight();
14849         var h = this.size.height-hh;
14850         cb.setHeight(h);
14851         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14852         bw.setHeight(h-cb.getPadding("tb"));
14853         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14854         bd.setWidth(bw.getWidth(true));
14855         if(this.tabs){
14856             this.tabs.syncHeight();
14857             if(Roo.isIE){
14858                 this.tabs.el.repaint();
14859             }
14860         }
14861     },
14862
14863     /**
14864      * Restores the previous state of the dialog if Roo.state is configured.
14865      * @return {Roo.BasicDialog} this
14866      */
14867     restoreState : function(){
14868         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14869         if(box && box.width){
14870             this.xy = [box.x, box.y];
14871             this.resizeTo(box.width, box.height);
14872         }
14873         return this;
14874     },
14875
14876     // private
14877     beforeShow : function(){
14878         this.expand();
14879         if(this.fixedcenter){
14880             this.xy = this.el.getCenterXY(true);
14881         }
14882         if(this.modal){
14883             Roo.get(document.body).addClass("x-body-masked");
14884             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14885             this.mask.show();
14886         }
14887         this.constrainXY();
14888     },
14889
14890     // private
14891     animShow : function(){
14892         var b = Roo.get(this.animateTarget).getBox();
14893         this.proxy.setSize(b.width, b.height);
14894         this.proxy.setLocation(b.x, b.y);
14895         this.proxy.show();
14896         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14897                     true, .35, this.showEl.createDelegate(this));
14898     },
14899
14900     /**
14901      * Shows the dialog.
14902      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14903      * @return {Roo.BasicDialog} this
14904      */
14905     show : function(animateTarget){
14906         if (this.fireEvent("beforeshow", this) === false){
14907             return;
14908         }
14909         if(this.syncHeightBeforeShow){
14910             this.syncBodyHeight();
14911         }else if(this.firstShow){
14912             this.firstShow = false;
14913             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14914         }
14915         this.animateTarget = animateTarget || this.animateTarget;
14916         if(!this.el.isVisible()){
14917             this.beforeShow();
14918             if(this.animateTarget && Roo.get(this.animateTarget)){
14919                 this.animShow();
14920             }else{
14921                 this.showEl();
14922             }
14923         }
14924         return this;
14925     },
14926
14927     // private
14928     showEl : function(){
14929         this.proxy.hide();
14930         this.el.setXY(this.xy);
14931         this.el.show();
14932         this.adjustAssets(true);
14933         this.toFront();
14934         this.focus();
14935         // IE peekaboo bug - fix found by Dave Fenwick
14936         if(Roo.isIE){
14937             this.el.repaint();
14938         }
14939         this.fireEvent("show", this);
14940     },
14941
14942     /**
14943      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14944      * dialog itself will receive focus.
14945      */
14946     focus : function(){
14947         if(this.defaultButton){
14948             this.defaultButton.focus();
14949         }else{
14950             this.focusEl.focus();
14951         }
14952     },
14953
14954     // private
14955     constrainXY : function(){
14956         if(this.constraintoviewport !== false){
14957             if(!this.viewSize){
14958                 if(this.container){
14959                     var s = this.container.getSize();
14960                     this.viewSize = [s.width, s.height];
14961                 }else{
14962                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14963                 }
14964             }
14965             var s = Roo.get(this.container||document).getScroll();
14966
14967             var x = this.xy[0], y = this.xy[1];
14968             var w = this.size.width, h = this.size.height;
14969             var vw = this.viewSize[0], vh = this.viewSize[1];
14970             // only move it if it needs it
14971             var moved = false;
14972             // first validate right/bottom
14973             if(x + w > vw+s.left){
14974                 x = vw - w;
14975                 moved = true;
14976             }
14977             if(y + h > vh+s.top){
14978                 y = vh - h;
14979                 moved = true;
14980             }
14981             // then make sure top/left isn't negative
14982             if(x < s.left){
14983                 x = s.left;
14984                 moved = true;
14985             }
14986             if(y < s.top){
14987                 y = s.top;
14988                 moved = true;
14989             }
14990             if(moved){
14991                 // cache xy
14992                 this.xy = [x, y];
14993                 if(this.isVisible()){
14994                     this.el.setLocation(x, y);
14995                     this.adjustAssets();
14996                 }
14997             }
14998         }
14999     },
15000
15001     // private
15002     onDrag : function(){
15003         if(!this.proxyDrag){
15004             this.xy = this.el.getXY();
15005             this.adjustAssets();
15006         }
15007     },
15008
15009     // private
15010     adjustAssets : function(doShow){
15011         var x = this.xy[0], y = this.xy[1];
15012         var w = this.size.width, h = this.size.height;
15013         if(doShow === true){
15014             if(this.shadow){
15015                 this.shadow.show(this.el);
15016             }
15017             if(this.shim){
15018                 this.shim.show();
15019             }
15020         }
15021         if(this.shadow && this.shadow.isVisible()){
15022             this.shadow.show(this.el);
15023         }
15024         if(this.shim && this.shim.isVisible()){
15025             this.shim.setBounds(x, y, w, h);
15026         }
15027     },
15028
15029     // private
15030     adjustViewport : function(w, h){
15031         if(!w || !h){
15032             w = Roo.lib.Dom.getViewWidth();
15033             h = Roo.lib.Dom.getViewHeight();
15034         }
15035         // cache the size
15036         this.viewSize = [w, h];
15037         if(this.modal && this.mask.isVisible()){
15038             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15039             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15040         }
15041         if(this.isVisible()){
15042             this.constrainXY();
15043         }
15044     },
15045
15046     /**
15047      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15048      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15049      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15050      */
15051     destroy : function(removeEl){
15052         if(this.isVisible()){
15053             this.animateTarget = null;
15054             this.hide();
15055         }
15056         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15057         if(this.tabs){
15058             this.tabs.destroy(removeEl);
15059         }
15060         Roo.destroy(
15061              this.shim,
15062              this.proxy,
15063              this.resizer,
15064              this.close,
15065              this.mask
15066         );
15067         if(this.dd){
15068             this.dd.unreg();
15069         }
15070         if(this.buttons){
15071            for(var i = 0, len = this.buttons.length; i < len; i++){
15072                this.buttons[i].destroy();
15073            }
15074         }
15075         this.el.removeAllListeners();
15076         if(removeEl === true){
15077             this.el.update("");
15078             this.el.remove();
15079         }
15080         Roo.DialogManager.unregister(this);
15081     },
15082
15083     // private
15084     startMove : function(){
15085         if(this.proxyDrag){
15086             this.proxy.show();
15087         }
15088         if(this.constraintoviewport !== false){
15089             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15090         }
15091     },
15092
15093     // private
15094     endMove : function(){
15095         if(!this.proxyDrag){
15096             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15097         }else{
15098             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15099             this.proxy.hide();
15100         }
15101         this.refreshSize();
15102         this.adjustAssets();
15103         this.focus();
15104         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15105     },
15106
15107     /**
15108      * Brings this dialog to the front of any other visible dialogs
15109      * @return {Roo.BasicDialog} this
15110      */
15111     toFront : function(){
15112         Roo.DialogManager.bringToFront(this);
15113         return this;
15114     },
15115
15116     /**
15117      * Sends this dialog to the back (under) of any other visible dialogs
15118      * @return {Roo.BasicDialog} this
15119      */
15120     toBack : function(){
15121         Roo.DialogManager.sendToBack(this);
15122         return this;
15123     },
15124
15125     /**
15126      * Centers this dialog in the viewport
15127      * @return {Roo.BasicDialog} this
15128      */
15129     center : function(){
15130         var xy = this.el.getCenterXY(true);
15131         this.moveTo(xy[0], xy[1]);
15132         return this;
15133     },
15134
15135     /**
15136      * Moves the dialog's top-left corner to the specified point
15137      * @param {Number} x
15138      * @param {Number} y
15139      * @return {Roo.BasicDialog} this
15140      */
15141     moveTo : function(x, y){
15142         this.xy = [x,y];
15143         if(this.isVisible()){
15144             this.el.setXY(this.xy);
15145             this.adjustAssets();
15146         }
15147         return this;
15148     },
15149
15150     /**
15151      * Aligns the dialog to the specified element
15152      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15153      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15154      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15155      * @return {Roo.BasicDialog} this
15156      */
15157     alignTo : function(element, position, offsets){
15158         this.xy = this.el.getAlignToXY(element, position, offsets);
15159         if(this.isVisible()){
15160             this.el.setXY(this.xy);
15161             this.adjustAssets();
15162         }
15163         return this;
15164     },
15165
15166     /**
15167      * Anchors an element to another element and realigns it when the window is resized.
15168      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15169      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15170      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15171      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15172      * is a number, it is used as the buffer delay (defaults to 50ms).
15173      * @return {Roo.BasicDialog} this
15174      */
15175     anchorTo : function(el, alignment, offsets, monitorScroll){
15176         var action = function(){
15177             this.alignTo(el, alignment, offsets);
15178         };
15179         Roo.EventManager.onWindowResize(action, this);
15180         var tm = typeof monitorScroll;
15181         if(tm != 'undefined'){
15182             Roo.EventManager.on(window, 'scroll', action, this,
15183                 {buffer: tm == 'number' ? monitorScroll : 50});
15184         }
15185         action.call(this);
15186         return this;
15187     },
15188
15189     /**
15190      * Returns true if the dialog is visible
15191      * @return {Boolean}
15192      */
15193     isVisible : function(){
15194         return this.el.isVisible();
15195     },
15196
15197     // private
15198     animHide : function(callback){
15199         var b = Roo.get(this.animateTarget).getBox();
15200         this.proxy.show();
15201         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15202         this.el.hide();
15203         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15204                     this.hideEl.createDelegate(this, [callback]));
15205     },
15206
15207     /**
15208      * Hides the dialog.
15209      * @param {Function} callback (optional) Function to call when the dialog is hidden
15210      * @return {Roo.BasicDialog} this
15211      */
15212     hide : function(callback){
15213         if (this.fireEvent("beforehide", this) === false){
15214             return;
15215         }
15216         if(this.shadow){
15217             this.shadow.hide();
15218         }
15219         if(this.shim) {
15220           this.shim.hide();
15221         }
15222         // sometimes animateTarget seems to get set.. causing problems...
15223         // this just double checks..
15224         if(this.animateTarget && Roo.get(this.animateTarget)) {
15225            this.animHide(callback);
15226         }else{
15227             this.el.hide();
15228             this.hideEl(callback);
15229         }
15230         return this;
15231     },
15232
15233     // private
15234     hideEl : function(callback){
15235         this.proxy.hide();
15236         if(this.modal){
15237             this.mask.hide();
15238             Roo.get(document.body).removeClass("x-body-masked");
15239         }
15240         this.fireEvent("hide", this);
15241         if(typeof callback == "function"){
15242             callback();
15243         }
15244     },
15245
15246     // private
15247     hideAction : function(){
15248         this.setLeft("-10000px");
15249         this.setTop("-10000px");
15250         this.setStyle("visibility", "hidden");
15251     },
15252
15253     // private
15254     refreshSize : function(){
15255         this.size = this.el.getSize();
15256         this.xy = this.el.getXY();
15257         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15258     },
15259
15260     // private
15261     // z-index is managed by the DialogManager and may be overwritten at any time
15262     setZIndex : function(index){
15263         if(this.modal){
15264             this.mask.setStyle("z-index", index);
15265         }
15266         if(this.shim){
15267             this.shim.setStyle("z-index", ++index);
15268         }
15269         if(this.shadow){
15270             this.shadow.setZIndex(++index);
15271         }
15272         this.el.setStyle("z-index", ++index);
15273         if(this.proxy){
15274             this.proxy.setStyle("z-index", ++index);
15275         }
15276         if(this.resizer){
15277             this.resizer.proxy.setStyle("z-index", ++index);
15278         }
15279
15280         this.lastZIndex = index;
15281     },
15282
15283     /**
15284      * Returns the element for this dialog
15285      * @return {Roo.Element} The underlying dialog Element
15286      */
15287     getEl : function(){
15288         return this.el;
15289     }
15290 });
15291
15292 /**
15293  * @class Roo.DialogManager
15294  * Provides global access to BasicDialogs that have been created and
15295  * support for z-indexing (layering) multiple open dialogs.
15296  */
15297 Roo.DialogManager = function(){
15298     var list = {};
15299     var accessList = [];
15300     var front = null;
15301
15302     // private
15303     var sortDialogs = function(d1, d2){
15304         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15305     };
15306
15307     // private
15308     var orderDialogs = function(){
15309         accessList.sort(sortDialogs);
15310         var seed = Roo.DialogManager.zseed;
15311         for(var i = 0, len = accessList.length; i < len; i++){
15312             var dlg = accessList[i];
15313             if(dlg){
15314                 dlg.setZIndex(seed + (i*10));
15315             }
15316         }
15317     };
15318
15319     return {
15320         /**
15321          * The starting z-index for BasicDialogs (defaults to 9000)
15322          * @type Number The z-index value
15323          */
15324         zseed : 9000,
15325
15326         // private
15327         register : function(dlg){
15328             list[dlg.id] = dlg;
15329             accessList.push(dlg);
15330         },
15331
15332         // private
15333         unregister : function(dlg){
15334             delete list[dlg.id];
15335             var i=0;
15336             var len=0;
15337             if(!accessList.indexOf){
15338                 for(  i = 0, len = accessList.length; i < len; i++){
15339                     if(accessList[i] == dlg){
15340                         accessList.splice(i, 1);
15341                         return;
15342                     }
15343                 }
15344             }else{
15345                  i = accessList.indexOf(dlg);
15346                 if(i != -1){
15347                     accessList.splice(i, 1);
15348                 }
15349             }
15350         },
15351
15352         /**
15353          * Gets a registered dialog by id
15354          * @param {String/Object} id The id of the dialog or a dialog
15355          * @return {Roo.BasicDialog} this
15356          */
15357         get : function(id){
15358             return typeof id == "object" ? id : list[id];
15359         },
15360
15361         /**
15362          * Brings the specified dialog to the front
15363          * @param {String/Object} dlg The id of the dialog or a dialog
15364          * @return {Roo.BasicDialog} this
15365          */
15366         bringToFront : function(dlg){
15367             dlg = this.get(dlg);
15368             if(dlg != front){
15369                 front = dlg;
15370                 dlg._lastAccess = new Date().getTime();
15371                 orderDialogs();
15372             }
15373             return dlg;
15374         },
15375
15376         /**
15377          * Sends the specified dialog to the back
15378          * @param {String/Object} dlg The id of the dialog or a dialog
15379          * @return {Roo.BasicDialog} this
15380          */
15381         sendToBack : function(dlg){
15382             dlg = this.get(dlg);
15383             dlg._lastAccess = -(new Date().getTime());
15384             orderDialogs();
15385             return dlg;
15386         },
15387
15388         /**
15389          * Hides all dialogs
15390          */
15391         hideAll : function(){
15392             for(var id in list){
15393                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15394                     list[id].hide();
15395                 }
15396             }
15397         }
15398     };
15399 }();
15400
15401 /**
15402  * @class Roo.LayoutDialog
15403  * @extends Roo.BasicDialog
15404  * Dialog which provides adjustments for working with a layout in a Dialog.
15405  * Add your necessary layout config options to the dialog's config.<br>
15406  * Example usage (including a nested layout):
15407  * <pre><code>
15408 if(!dialog){
15409     dialog = new Roo.LayoutDialog("download-dlg", {
15410         modal: true,
15411         width:600,
15412         height:450,
15413         shadow:true,
15414         minWidth:500,
15415         minHeight:350,
15416         autoTabs:true,
15417         proxyDrag:true,
15418         // layout config merges with the dialog config
15419         center:{
15420             tabPosition: "top",
15421             alwaysShowTabs: true
15422         }
15423     });
15424     dialog.addKeyListener(27, dialog.hide, dialog);
15425     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15426     dialog.addButton("Build It!", this.getDownload, this);
15427
15428     // we can even add nested layouts
15429     var innerLayout = new Roo.BorderLayout("dl-inner", {
15430         east: {
15431             initialSize: 200,
15432             autoScroll:true,
15433             split:true
15434         },
15435         center: {
15436             autoScroll:true
15437         }
15438     });
15439     innerLayout.beginUpdate();
15440     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15441     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15442     innerLayout.endUpdate(true);
15443
15444     var layout = dialog.getLayout();
15445     layout.beginUpdate();
15446     layout.add("center", new Roo.ContentPanel("standard-panel",
15447                         {title: "Download the Source", fitToFrame:true}));
15448     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15449                {title: "Build your own roo.js"}));
15450     layout.getRegion("center").showPanel(sp);
15451     layout.endUpdate();
15452 }
15453 </code></pre>
15454     * @constructor
15455     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15456     * @param {Object} config configuration options
15457   */
15458 Roo.LayoutDialog = function(el, cfg){
15459     
15460     var config=  cfg;
15461     if (typeof(cfg) == 'undefined') {
15462         config = Roo.apply({}, el);
15463         // not sure why we use documentElement here.. - it should always be body.
15464         // IE7 borks horribly if we use documentElement.
15465         // webkit also does not like documentElement - it creates a body element...
15466         el = Roo.get( document.body || document.documentElement ).createChild();
15467         //config.autoCreate = true;
15468     }
15469     
15470     
15471     config.autoTabs = false;
15472     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15473     this.body.setStyle({overflow:"hidden", position:"relative"});
15474     this.layout = new Roo.BorderLayout(this.body.dom, config);
15475     this.layout.monitorWindowResize = false;
15476     this.el.addClass("x-dlg-auto-layout");
15477     // fix case when center region overwrites center function
15478     this.center = Roo.BasicDialog.prototype.center;
15479     this.on("show", this.layout.layout, this.layout, true);
15480     if (config.items) {
15481         var xitems = config.items;
15482         delete config.items;
15483         Roo.each(xitems, this.addxtype, this);
15484     }
15485     
15486     
15487 };
15488 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15489     /**
15490      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15491      * @deprecated
15492      */
15493     endUpdate : function(){
15494         this.layout.endUpdate();
15495     },
15496
15497     /**
15498      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15499      *  @deprecated
15500      */
15501     beginUpdate : function(){
15502         this.layout.beginUpdate();
15503     },
15504
15505     /**
15506      * Get the BorderLayout for this dialog
15507      * @return {Roo.BorderLayout}
15508      */
15509     getLayout : function(){
15510         return this.layout;
15511     },
15512
15513     showEl : function(){
15514         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15515         if(Roo.isIE7){
15516             this.layout.layout();
15517         }
15518     },
15519
15520     // private
15521     // Use the syncHeightBeforeShow config option to control this automatically
15522     syncBodyHeight : function(){
15523         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15524         if(this.layout){this.layout.layout();}
15525     },
15526     
15527       /**
15528      * Add an xtype element (actually adds to the layout.)
15529      * @return {Object} xdata xtype object data.
15530      */
15531     
15532     addxtype : function(c) {
15533         return this.layout.addxtype(c);
15534     }
15535 });/*
15536  * Based on:
15537  * Ext JS Library 1.1.1
15538  * Copyright(c) 2006-2007, Ext JS, LLC.
15539  *
15540  * Originally Released Under LGPL - original licence link has changed is not relivant.
15541  *
15542  * Fork - LGPL
15543  * <script type="text/javascript">
15544  */
15545  
15546 /**
15547  * @class Roo.MessageBox
15548  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15549  * Example usage:
15550  *<pre><code>
15551 // Basic alert:
15552 Roo.Msg.alert('Status', 'Changes saved successfully.');
15553
15554 // Prompt for user data:
15555 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15556     if (btn == 'ok'){
15557         // process text value...
15558     }
15559 });
15560
15561 // Show a dialog using config options:
15562 Roo.Msg.show({
15563    title:'Save Changes?',
15564    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15565    buttons: Roo.Msg.YESNOCANCEL,
15566    fn: processResult,
15567    animEl: 'elId'
15568 });
15569 </code></pre>
15570  * @singleton
15571  */
15572 Roo.MessageBox = function(){
15573     var dlg, opt, mask, waitTimer;
15574     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15575     var buttons, activeTextEl, bwidth;
15576
15577     // private
15578     var handleButton = function(button){
15579         dlg.hide();
15580         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15581     };
15582
15583     // private
15584     var handleHide = function(){
15585         if(opt && opt.cls){
15586             dlg.el.removeClass(opt.cls);
15587         }
15588         if(waitTimer){
15589             Roo.TaskMgr.stop(waitTimer);
15590             waitTimer = null;
15591         }
15592     };
15593
15594     // private
15595     var updateButtons = function(b){
15596         var width = 0;
15597         if(!b){
15598             buttons["ok"].hide();
15599             buttons["cancel"].hide();
15600             buttons["yes"].hide();
15601             buttons["no"].hide();
15602             dlg.footer.dom.style.display = 'none';
15603             return width;
15604         }
15605         dlg.footer.dom.style.display = '';
15606         for(var k in buttons){
15607             if(typeof buttons[k] != "function"){
15608                 if(b[k]){
15609                     buttons[k].show();
15610                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15611                     width += buttons[k].el.getWidth()+15;
15612                 }else{
15613                     buttons[k].hide();
15614                 }
15615             }
15616         }
15617         return width;
15618     };
15619
15620     // private
15621     var handleEsc = function(d, k, e){
15622         if(opt && opt.closable !== false){
15623             dlg.hide();
15624         }
15625         if(e){
15626             e.stopEvent();
15627         }
15628     };
15629
15630     return {
15631         /**
15632          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15633          * @return {Roo.BasicDialog} The BasicDialog element
15634          */
15635         getDialog : function(){
15636            if(!dlg){
15637                 dlg = new Roo.BasicDialog("x-msg-box", {
15638                     autoCreate : true,
15639                     shadow: true,
15640                     draggable: true,
15641                     resizable:false,
15642                     constraintoviewport:false,
15643                     fixedcenter:true,
15644                     collapsible : false,
15645                     shim:true,
15646                     modal: true,
15647                     width:400, height:100,
15648                     buttonAlign:"center",
15649                     closeClick : function(){
15650                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15651                             handleButton("no");
15652                         }else{
15653                             handleButton("cancel");
15654                         }
15655                     }
15656                 });
15657                 dlg.on("hide", handleHide);
15658                 mask = dlg.mask;
15659                 dlg.addKeyListener(27, handleEsc);
15660                 buttons = {};
15661                 var bt = this.buttonText;
15662                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15663                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15664                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15665                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15666                 bodyEl = dlg.body.createChild({
15667
15668                     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>'
15669                 });
15670                 msgEl = bodyEl.dom.firstChild;
15671                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15672                 textboxEl.enableDisplayMode();
15673                 textboxEl.addKeyListener([10,13], function(){
15674                     if(dlg.isVisible() && opt && opt.buttons){
15675                         if(opt.buttons.ok){
15676                             handleButton("ok");
15677                         }else if(opt.buttons.yes){
15678                             handleButton("yes");
15679                         }
15680                     }
15681                 });
15682                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15683                 textareaEl.enableDisplayMode();
15684                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15685                 progressEl.enableDisplayMode();
15686                 var pf = progressEl.dom.firstChild;
15687                 if (pf) {
15688                     pp = Roo.get(pf.firstChild);
15689                     pp.setHeight(pf.offsetHeight);
15690                 }
15691                 
15692             }
15693             return dlg;
15694         },
15695
15696         /**
15697          * Updates the message box body text
15698          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15699          * the XHTML-compliant non-breaking space character '&amp;#160;')
15700          * @return {Roo.MessageBox} This message box
15701          */
15702         updateText : function(text){
15703             if(!dlg.isVisible() && !opt.width){
15704                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15705             }
15706             msgEl.innerHTML = text || '&#160;';
15707       
15708             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15709             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15710             var w = Math.max(
15711                     Math.min(opt.width || cw , this.maxWidth), 
15712                     Math.max(opt.minWidth || this.minWidth, bwidth)
15713             );
15714             if(opt.prompt){
15715                 activeTextEl.setWidth(w);
15716             }
15717             if(dlg.isVisible()){
15718                 dlg.fixedcenter = false;
15719             }
15720             // to big, make it scroll. = But as usual stupid IE does not support
15721             // !important..
15722             
15723             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15724                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15725                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15726             } else {
15727                 bodyEl.dom.style.height = '';
15728                 bodyEl.dom.style.overflowY = '';
15729             }
15730             if (cw > w) {
15731                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15732             } else {
15733                 bodyEl.dom.style.overflowX = '';
15734             }
15735             
15736             dlg.setContentSize(w, bodyEl.getHeight());
15737             if(dlg.isVisible()){
15738                 dlg.fixedcenter = true;
15739             }
15740             return this;
15741         },
15742
15743         /**
15744          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15745          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15746          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15747          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15748          * @return {Roo.MessageBox} This message box
15749          */
15750         updateProgress : function(value, text){
15751             if(text){
15752                 this.updateText(text);
15753             }
15754             if (pp) { // weird bug on my firefox - for some reason this is not defined
15755                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15756             }
15757             return this;
15758         },        
15759
15760         /**
15761          * Returns true if the message box is currently displayed
15762          * @return {Boolean} True if the message box is visible, else false
15763          */
15764         isVisible : function(){
15765             return dlg && dlg.isVisible();  
15766         },
15767
15768         /**
15769          * Hides the message box if it is displayed
15770          */
15771         hide : function(){
15772             if(this.isVisible()){
15773                 dlg.hide();
15774             }  
15775         },
15776
15777         /**
15778          * Displays a new message box, or reinitializes an existing message box, based on the config options
15779          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15780          * The following config object properties are supported:
15781          * <pre>
15782 Property    Type             Description
15783 ----------  ---------------  ------------------------------------------------------------------------------------
15784 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15785                                    closes (defaults to undefined)
15786 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15787                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15788 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15789                                    progress and wait dialogs will ignore this property and always hide the
15790                                    close button as they can only be closed programmatically.
15791 cls               String           A custom CSS class to apply to the message box element
15792 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15793                                    displayed (defaults to 75)
15794 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15795                                    function will be btn (the name of the button that was clicked, if applicable,
15796                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15797                                    Progress and wait dialogs will ignore this option since they do not respond to
15798                                    user actions and can only be closed programmatically, so any required function
15799                                    should be called by the same code after it closes the dialog.
15800 icon              String           A CSS class that provides a background image to be used as an icon for
15801                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15802 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15803 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15804 modal             Boolean          False to allow user interaction with the page while the message box is
15805                                    displayed (defaults to true)
15806 msg               String           A string that will replace the existing message box body text (defaults
15807                                    to the XHTML-compliant non-breaking space character '&#160;')
15808 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15809 progress          Boolean          True to display a progress bar (defaults to false)
15810 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15811 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15812 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15813 title             String           The title text
15814 value             String           The string value to set into the active textbox element if displayed
15815 wait              Boolean          True to display a progress bar (defaults to false)
15816 width             Number           The width of the dialog in pixels
15817 </pre>
15818          *
15819          * Example usage:
15820          * <pre><code>
15821 Roo.Msg.show({
15822    title: 'Address',
15823    msg: 'Please enter your address:',
15824    width: 300,
15825    buttons: Roo.MessageBox.OKCANCEL,
15826    multiline: true,
15827    fn: saveAddress,
15828    animEl: 'addAddressBtn'
15829 });
15830 </code></pre>
15831          * @param {Object} config Configuration options
15832          * @return {Roo.MessageBox} This message box
15833          */
15834         show : function(options)
15835         {
15836             
15837             // this causes nightmares if you show one dialog after another
15838             // especially on callbacks..
15839              
15840             if(this.isVisible()){
15841                 
15842                 this.hide();
15843                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15844                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15845                 Roo.log("New Dialog Message:" +  options.msg )
15846                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15847                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15848                 
15849             }
15850             var d = this.getDialog();
15851             opt = options;
15852             d.setTitle(opt.title || "&#160;");
15853             d.close.setDisplayed(opt.closable !== false);
15854             activeTextEl = textboxEl;
15855             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15856             if(opt.prompt){
15857                 if(opt.multiline){
15858                     textboxEl.hide();
15859                     textareaEl.show();
15860                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15861                         opt.multiline : this.defaultTextHeight);
15862                     activeTextEl = textareaEl;
15863                 }else{
15864                     textboxEl.show();
15865                     textareaEl.hide();
15866                 }
15867             }else{
15868                 textboxEl.hide();
15869                 textareaEl.hide();
15870             }
15871             progressEl.setDisplayed(opt.progress === true);
15872             this.updateProgress(0);
15873             activeTextEl.dom.value = opt.value || "";
15874             if(opt.prompt){
15875                 dlg.setDefaultButton(activeTextEl);
15876             }else{
15877                 var bs = opt.buttons;
15878                 var db = null;
15879                 if(bs && bs.ok){
15880                     db = buttons["ok"];
15881                 }else if(bs && bs.yes){
15882                     db = buttons["yes"];
15883                 }
15884                 dlg.setDefaultButton(db);
15885             }
15886             bwidth = updateButtons(opt.buttons);
15887             this.updateText(opt.msg);
15888             if(opt.cls){
15889                 d.el.addClass(opt.cls);
15890             }
15891             d.proxyDrag = opt.proxyDrag === true;
15892             d.modal = opt.modal !== false;
15893             d.mask = opt.modal !== false ? mask : false;
15894             if(!d.isVisible()){
15895                 // force it to the end of the z-index stack so it gets a cursor in FF
15896                 document.body.appendChild(dlg.el.dom);
15897                 d.animateTarget = null;
15898                 d.show(options.animEl);
15899             }
15900             return this;
15901         },
15902
15903         /**
15904          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15905          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15906          * and closing the message box when the process is complete.
15907          * @param {String} title The title bar text
15908          * @param {String} msg The message box body text
15909          * @return {Roo.MessageBox} This message box
15910          */
15911         progress : function(title, msg){
15912             this.show({
15913                 title : title,
15914                 msg : msg,
15915                 buttons: false,
15916                 progress:true,
15917                 closable:false,
15918                 minWidth: this.minProgressWidth,
15919                 modal : true
15920             });
15921             return this;
15922         },
15923
15924         /**
15925          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15926          * If a callback function is passed it will be called after the user clicks the button, and the
15927          * id of the button that was clicked will be passed as the only parameter to the callback
15928          * (could also be the top-right close button).
15929          * @param {String} title The title bar text
15930          * @param {String} msg The message box body text
15931          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15932          * @param {Object} scope (optional) The scope of the callback function
15933          * @return {Roo.MessageBox} This message box
15934          */
15935         alert : function(title, msg, fn, scope){
15936             this.show({
15937                 title : title,
15938                 msg : msg,
15939                 buttons: this.OK,
15940                 fn: fn,
15941                 scope : scope,
15942                 modal : true
15943             });
15944             return this;
15945         },
15946
15947         /**
15948          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15949          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15950          * You are responsible for closing the message box when the process is complete.
15951          * @param {String} msg The message box body text
15952          * @param {String} title (optional) The title bar text
15953          * @return {Roo.MessageBox} This message box
15954          */
15955         wait : function(msg, title){
15956             this.show({
15957                 title : title,
15958                 msg : msg,
15959                 buttons: false,
15960                 closable:false,
15961                 progress:true,
15962                 modal:true,
15963                 width:300,
15964                 wait:true
15965             });
15966             waitTimer = Roo.TaskMgr.start({
15967                 run: function(i){
15968                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15969                 },
15970                 interval: 1000
15971             });
15972             return this;
15973         },
15974
15975         /**
15976          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15977          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15978          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15979          * @param {String} title The title bar text
15980          * @param {String} msg The message box body text
15981          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15982          * @param {Object} scope (optional) The scope of the callback function
15983          * @return {Roo.MessageBox} This message box
15984          */
15985         confirm : function(title, msg, fn, scope){
15986             this.show({
15987                 title : title,
15988                 msg : msg,
15989                 buttons: this.YESNO,
15990                 fn: fn,
15991                 scope : scope,
15992                 modal : true
15993             });
15994             return this;
15995         },
15996
15997         /**
15998          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15999          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16000          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16001          * (could also be the top-right close button) and the text that was entered will be passed as the two
16002          * parameters to the callback.
16003          * @param {String} title The title bar text
16004          * @param {String} msg The message box body text
16005          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16006          * @param {Object} scope (optional) The scope of the callback function
16007          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16008          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16009          * @return {Roo.MessageBox} This message box
16010          */
16011         prompt : function(title, msg, fn, scope, multiline){
16012             this.show({
16013                 title : title,
16014                 msg : msg,
16015                 buttons: this.OKCANCEL,
16016                 fn: fn,
16017                 minWidth:250,
16018                 scope : scope,
16019                 prompt:true,
16020                 multiline: multiline,
16021                 modal : true
16022             });
16023             return this;
16024         },
16025
16026         /**
16027          * Button config that displays a single OK button
16028          * @type Object
16029          */
16030         OK : {ok:true},
16031         /**
16032          * Button config that displays Yes and No buttons
16033          * @type Object
16034          */
16035         YESNO : {yes:true, no:true},
16036         /**
16037          * Button config that displays OK and Cancel buttons
16038          * @type Object
16039          */
16040         OKCANCEL : {ok:true, cancel:true},
16041         /**
16042          * Button config that displays Yes, No and Cancel buttons
16043          * @type Object
16044          */
16045         YESNOCANCEL : {yes:true, no:true, cancel:true},
16046
16047         /**
16048          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16049          * @type Number
16050          */
16051         defaultTextHeight : 75,
16052         /**
16053          * The maximum width in pixels of the message box (defaults to 600)
16054          * @type Number
16055          */
16056         maxWidth : 600,
16057         /**
16058          * The minimum width in pixels of the message box (defaults to 100)
16059          * @type Number
16060          */
16061         minWidth : 100,
16062         /**
16063          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16064          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16065          * @type Number
16066          */
16067         minProgressWidth : 250,
16068         /**
16069          * An object containing the default button text strings that can be overriden for localized language support.
16070          * Supported properties are: ok, cancel, yes and no.
16071          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16072          * @type Object
16073          */
16074         buttonText : {
16075             ok : "OK",
16076             cancel : "Cancel",
16077             yes : "Yes",
16078             no : "No"
16079         }
16080     };
16081 }();
16082
16083 /**
16084  * Shorthand for {@link Roo.MessageBox}
16085  */
16086 Roo.Msg = Roo.MessageBox;/*
16087  * Based on:
16088  * Ext JS Library 1.1.1
16089  * Copyright(c) 2006-2007, Ext JS, LLC.
16090  *
16091  * Originally Released Under LGPL - original licence link has changed is not relivant.
16092  *
16093  * Fork - LGPL
16094  * <script type="text/javascript">
16095  */
16096 /**
16097  * @class Roo.QuickTips
16098  * Provides attractive and customizable tooltips for any element.
16099  * @singleton
16100  */
16101 Roo.QuickTips = function(){
16102     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16103     var ce, bd, xy, dd;
16104     var visible = false, disabled = true, inited = false;
16105     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16106     
16107     var onOver = function(e){
16108         if(disabled){
16109             return;
16110         }
16111         var t = e.getTarget();
16112         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16113             return;
16114         }
16115         if(ce && t == ce.el){
16116             clearTimeout(hideProc);
16117             return;
16118         }
16119         if(t && tagEls[t.id]){
16120             tagEls[t.id].el = t;
16121             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16122             return;
16123         }
16124         var ttp, et = Roo.fly(t);
16125         var ns = cfg.namespace;
16126         if(tm.interceptTitles && t.title){
16127             ttp = t.title;
16128             t.qtip = ttp;
16129             t.removeAttribute("title");
16130             e.preventDefault();
16131         }else{
16132             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16133         }
16134         if(ttp){
16135             showProc = show.defer(tm.showDelay, tm, [{
16136                 el: t, 
16137                 text: ttp, 
16138                 width: et.getAttributeNS(ns, cfg.width),
16139                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16140                 title: et.getAttributeNS(ns, cfg.title),
16141                     cls: et.getAttributeNS(ns, cfg.cls)
16142             }]);
16143         }
16144     };
16145     
16146     var onOut = function(e){
16147         clearTimeout(showProc);
16148         var t = e.getTarget();
16149         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16150             hideProc = setTimeout(hide, tm.hideDelay);
16151         }
16152     };
16153     
16154     var onMove = function(e){
16155         if(disabled){
16156             return;
16157         }
16158         xy = e.getXY();
16159         xy[1] += 18;
16160         if(tm.trackMouse && ce){
16161             el.setXY(xy);
16162         }
16163     };
16164     
16165     var onDown = function(e){
16166         clearTimeout(showProc);
16167         clearTimeout(hideProc);
16168         if(!e.within(el)){
16169             if(tm.hideOnClick){
16170                 hide();
16171                 tm.disable();
16172                 tm.enable.defer(100, tm);
16173             }
16174         }
16175     };
16176     
16177     var getPad = function(){
16178         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16179     };
16180
16181     var show = function(o){
16182         if(disabled){
16183             return;
16184         }
16185         clearTimeout(dismissProc);
16186         ce = o;
16187         if(removeCls){ // in case manually hidden
16188             el.removeClass(removeCls);
16189             removeCls = null;
16190         }
16191         if(ce.cls){
16192             el.addClass(ce.cls);
16193             removeCls = ce.cls;
16194         }
16195         if(ce.title){
16196             tipTitle.update(ce.title);
16197             tipTitle.show();
16198         }else{
16199             tipTitle.update('');
16200             tipTitle.hide();
16201         }
16202         el.dom.style.width  = tm.maxWidth+'px';
16203         //tipBody.dom.style.width = '';
16204         tipBodyText.update(o.text);
16205         var p = getPad(), w = ce.width;
16206         if(!w){
16207             var td = tipBodyText.dom;
16208             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16209             if(aw > tm.maxWidth){
16210                 w = tm.maxWidth;
16211             }else if(aw < tm.minWidth){
16212                 w = tm.minWidth;
16213             }else{
16214                 w = aw;
16215             }
16216         }
16217         //tipBody.setWidth(w);
16218         el.setWidth(parseInt(w, 10) + p);
16219         if(ce.autoHide === false){
16220             close.setDisplayed(true);
16221             if(dd){
16222                 dd.unlock();
16223             }
16224         }else{
16225             close.setDisplayed(false);
16226             if(dd){
16227                 dd.lock();
16228             }
16229         }
16230         if(xy){
16231             el.avoidY = xy[1]-18;
16232             el.setXY(xy);
16233         }
16234         if(tm.animate){
16235             el.setOpacity(.1);
16236             el.setStyle("visibility", "visible");
16237             el.fadeIn({callback: afterShow});
16238         }else{
16239             afterShow();
16240         }
16241     };
16242     
16243     var afterShow = function(){
16244         if(ce){
16245             el.show();
16246             esc.enable();
16247             if(tm.autoDismiss && ce.autoHide !== false){
16248                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16249             }
16250         }
16251     };
16252     
16253     var hide = function(noanim){
16254         clearTimeout(dismissProc);
16255         clearTimeout(hideProc);
16256         ce = null;
16257         if(el.isVisible()){
16258             esc.disable();
16259             if(noanim !== true && tm.animate){
16260                 el.fadeOut({callback: afterHide});
16261             }else{
16262                 afterHide();
16263             } 
16264         }
16265     };
16266     
16267     var afterHide = function(){
16268         el.hide();
16269         if(removeCls){
16270             el.removeClass(removeCls);
16271             removeCls = null;
16272         }
16273     };
16274     
16275     return {
16276         /**
16277         * @cfg {Number} minWidth
16278         * The minimum width of the quick tip (defaults to 40)
16279         */
16280        minWidth : 40,
16281         /**
16282         * @cfg {Number} maxWidth
16283         * The maximum width of the quick tip (defaults to 300)
16284         */
16285        maxWidth : 300,
16286         /**
16287         * @cfg {Boolean} interceptTitles
16288         * True to automatically use the element's DOM title value if available (defaults to false)
16289         */
16290        interceptTitles : false,
16291         /**
16292         * @cfg {Boolean} trackMouse
16293         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16294         */
16295        trackMouse : false,
16296         /**
16297         * @cfg {Boolean} hideOnClick
16298         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16299         */
16300        hideOnClick : true,
16301         /**
16302         * @cfg {Number} showDelay
16303         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16304         */
16305        showDelay : 500,
16306         /**
16307         * @cfg {Number} hideDelay
16308         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16309         */
16310        hideDelay : 200,
16311         /**
16312         * @cfg {Boolean} autoHide
16313         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16314         * Used in conjunction with hideDelay.
16315         */
16316        autoHide : true,
16317         /**
16318         * @cfg {Boolean}
16319         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16320         * (defaults to true).  Used in conjunction with autoDismissDelay.
16321         */
16322        autoDismiss : true,
16323         /**
16324         * @cfg {Number}
16325         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16326         */
16327        autoDismissDelay : 5000,
16328        /**
16329         * @cfg {Boolean} animate
16330         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16331         */
16332        animate : false,
16333
16334        /**
16335         * @cfg {String} title
16336         * Title text to display (defaults to '').  This can be any valid HTML markup.
16337         */
16338         title: '',
16339        /**
16340         * @cfg {String} text
16341         * Body text to display (defaults to '').  This can be any valid HTML markup.
16342         */
16343         text : '',
16344        /**
16345         * @cfg {String} cls
16346         * A CSS class to apply to the base quick tip element (defaults to '').
16347         */
16348         cls : '',
16349        /**
16350         * @cfg {Number} width
16351         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16352         * minWidth or maxWidth.
16353         */
16354         width : null,
16355
16356     /**
16357      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16358      * or display QuickTips in a page.
16359      */
16360        init : function(){
16361           tm = Roo.QuickTips;
16362           cfg = tm.tagConfig;
16363           if(!inited){
16364               if(!Roo.isReady){ // allow calling of init() before onReady
16365                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16366                   return;
16367               }
16368               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16369               el.fxDefaults = {stopFx: true};
16370               // maximum custom styling
16371               //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>');
16372               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>');              
16373               tipTitle = el.child('h3');
16374               tipTitle.enableDisplayMode("block");
16375               tipBody = el.child('div.x-tip-bd');
16376               tipBodyText = el.child('div.x-tip-bd-inner');
16377               //bdLeft = el.child('div.x-tip-bd-left');
16378               //bdRight = el.child('div.x-tip-bd-right');
16379               close = el.child('div.x-tip-close');
16380               close.enableDisplayMode("block");
16381               close.on("click", hide);
16382               var d = Roo.get(document);
16383               d.on("mousedown", onDown);
16384               d.on("mouseover", onOver);
16385               d.on("mouseout", onOut);
16386               d.on("mousemove", onMove);
16387               esc = d.addKeyListener(27, hide);
16388               esc.disable();
16389               if(Roo.dd.DD){
16390                   dd = el.initDD("default", null, {
16391                       onDrag : function(){
16392                           el.sync();  
16393                       }
16394                   });
16395                   dd.setHandleElId(tipTitle.id);
16396                   dd.lock();
16397               }
16398               inited = true;
16399           }
16400           this.enable(); 
16401        },
16402
16403     /**
16404      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16405      * are supported:
16406      * <pre>
16407 Property    Type                   Description
16408 ----------  ---------------------  ------------------------------------------------------------------------
16409 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16410      * </ul>
16411      * @param {Object} config The config object
16412      */
16413        register : function(config){
16414            var cs = config instanceof Array ? config : arguments;
16415            for(var i = 0, len = cs.length; i < len; i++) {
16416                var c = cs[i];
16417                var target = c.target;
16418                if(target){
16419                    if(target instanceof Array){
16420                        for(var j = 0, jlen = target.length; j < jlen; j++){
16421                            tagEls[target[j]] = c;
16422                        }
16423                    }else{
16424                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16425                    }
16426                }
16427            }
16428        },
16429
16430     /**
16431      * Removes this quick tip from its element and destroys it.
16432      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16433      */
16434        unregister : function(el){
16435            delete tagEls[Roo.id(el)];
16436        },
16437
16438     /**
16439      * Enable this quick tip.
16440      */
16441        enable : function(){
16442            if(inited && disabled){
16443                locks.pop();
16444                if(locks.length < 1){
16445                    disabled = false;
16446                }
16447            }
16448        },
16449
16450     /**
16451      * Disable this quick tip.
16452      */
16453        disable : function(){
16454           disabled = true;
16455           clearTimeout(showProc);
16456           clearTimeout(hideProc);
16457           clearTimeout(dismissProc);
16458           if(ce){
16459               hide(true);
16460           }
16461           locks.push(1);
16462        },
16463
16464     /**
16465      * Returns true if the quick tip is enabled, else false.
16466      */
16467        isEnabled : function(){
16468             return !disabled;
16469        },
16470
16471         // private
16472        tagConfig : {
16473            namespace : "ext",
16474            attribute : "qtip",
16475            width : "width",
16476            target : "target",
16477            title : "qtitle",
16478            hide : "hide",
16479            cls : "qclass"
16480        }
16481    };
16482 }();
16483
16484 // backwards compat
16485 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16486  * Based on:
16487  * Ext JS Library 1.1.1
16488  * Copyright(c) 2006-2007, Ext JS, LLC.
16489  *
16490  * Originally Released Under LGPL - original licence link has changed is not relivant.
16491  *
16492  * Fork - LGPL
16493  * <script type="text/javascript">
16494  */
16495  
16496
16497 /**
16498  * @class Roo.tree.TreePanel
16499  * @extends Roo.data.Tree
16500
16501  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16502  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16503  * @cfg {Boolean} enableDD true to enable drag and drop
16504  * @cfg {Boolean} enableDrag true to enable just drag
16505  * @cfg {Boolean} enableDrop true to enable just drop
16506  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16507  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16508  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16509  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16510  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16511  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16512  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16513  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16514  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16515  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16516  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16517  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16518  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16519  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16520  * @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>
16521  * @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>
16522  * 
16523  * @constructor
16524  * @param {String/HTMLElement/Element} el The container element
16525  * @param {Object} config
16526  */
16527 Roo.tree.TreePanel = function(el, config){
16528     var root = false;
16529     var loader = false;
16530     if (config.root) {
16531         root = config.root;
16532         delete config.root;
16533     }
16534     if (config.loader) {
16535         loader = config.loader;
16536         delete config.loader;
16537     }
16538     
16539     Roo.apply(this, config);
16540     Roo.tree.TreePanel.superclass.constructor.call(this);
16541     this.el = Roo.get(el);
16542     this.el.addClass('x-tree');
16543     //console.log(root);
16544     if (root) {
16545         this.setRootNode( Roo.factory(root, Roo.tree));
16546     }
16547     if (loader) {
16548         this.loader = Roo.factory(loader, Roo.tree);
16549     }
16550    /**
16551     * Read-only. The id of the container element becomes this TreePanel's id.
16552     */
16553     this.id = this.el.id;
16554     this.addEvents({
16555         /**
16556         * @event beforeload
16557         * Fires before a node is loaded, return false to cancel
16558         * @param {Node} node The node being loaded
16559         */
16560         "beforeload" : true,
16561         /**
16562         * @event load
16563         * Fires when a node is loaded
16564         * @param {Node} node The node that was loaded
16565         */
16566         "load" : true,
16567         /**
16568         * @event textchange
16569         * Fires when the text for a node is changed
16570         * @param {Node} node The node
16571         * @param {String} text The new text
16572         * @param {String} oldText The old text
16573         */
16574         "textchange" : true,
16575         /**
16576         * @event beforeexpand
16577         * Fires before a node is expanded, return false to cancel.
16578         * @param {Node} node The node
16579         * @param {Boolean} deep
16580         * @param {Boolean} anim
16581         */
16582         "beforeexpand" : true,
16583         /**
16584         * @event beforecollapse
16585         * Fires before a node is collapsed, return false to cancel.
16586         * @param {Node} node The node
16587         * @param {Boolean} deep
16588         * @param {Boolean} anim
16589         */
16590         "beforecollapse" : true,
16591         /**
16592         * @event expand
16593         * Fires when a node is expanded
16594         * @param {Node} node The node
16595         */
16596         "expand" : true,
16597         /**
16598         * @event disabledchange
16599         * Fires when the disabled status of a node changes
16600         * @param {Node} node The node
16601         * @param {Boolean} disabled
16602         */
16603         "disabledchange" : true,
16604         /**
16605         * @event collapse
16606         * Fires when a node is collapsed
16607         * @param {Node} node The node
16608         */
16609         "collapse" : true,
16610         /**
16611         * @event beforeclick
16612         * Fires before click processing on a node. Return false to cancel the default action.
16613         * @param {Node} node The node
16614         * @param {Roo.EventObject} e The event object
16615         */
16616         "beforeclick":true,
16617         /**
16618         * @event checkchange
16619         * Fires when a node with a checkbox's checked property changes
16620         * @param {Node} this This node
16621         * @param {Boolean} checked
16622         */
16623         "checkchange":true,
16624         /**
16625         * @event click
16626         * Fires when a node is clicked
16627         * @param {Node} node The node
16628         * @param {Roo.EventObject} e The event object
16629         */
16630         "click":true,
16631         /**
16632         * @event dblclick
16633         * Fires when a node is double clicked
16634         * @param {Node} node The node
16635         * @param {Roo.EventObject} e The event object
16636         */
16637         "dblclick":true,
16638         /**
16639         * @event contextmenu
16640         * Fires when a node is right clicked
16641         * @param {Node} node The node
16642         * @param {Roo.EventObject} e The event object
16643         */
16644         "contextmenu":true,
16645         /**
16646         * @event beforechildrenrendered
16647         * Fires right before the child nodes for a node are rendered
16648         * @param {Node} node The node
16649         */
16650         "beforechildrenrendered":true,
16651         /**
16652         * @event startdrag
16653         * Fires when a node starts being dragged
16654         * @param {Roo.tree.TreePanel} this
16655         * @param {Roo.tree.TreeNode} node
16656         * @param {event} e The raw browser event
16657         */ 
16658        "startdrag" : true,
16659        /**
16660         * @event enddrag
16661         * Fires when a drag operation is complete
16662         * @param {Roo.tree.TreePanel} this
16663         * @param {Roo.tree.TreeNode} node
16664         * @param {event} e The raw browser event
16665         */
16666        "enddrag" : true,
16667        /**
16668         * @event dragdrop
16669         * Fires when a dragged node is dropped on a valid DD target
16670         * @param {Roo.tree.TreePanel} this
16671         * @param {Roo.tree.TreeNode} node
16672         * @param {DD} dd The dd it was dropped on
16673         * @param {event} e The raw browser event
16674         */
16675        "dragdrop" : true,
16676        /**
16677         * @event beforenodedrop
16678         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16679         * passed to handlers has the following properties:<br />
16680         * <ul style="padding:5px;padding-left:16px;">
16681         * <li>tree - The TreePanel</li>
16682         * <li>target - The node being targeted for the drop</li>
16683         * <li>data - The drag data from the drag source</li>
16684         * <li>point - The point of the drop - append, above or below</li>
16685         * <li>source - The drag source</li>
16686         * <li>rawEvent - Raw mouse event</li>
16687         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16688         * to be inserted by setting them on this object.</li>
16689         * <li>cancel - Set this to true to cancel the drop.</li>
16690         * </ul>
16691         * @param {Object} dropEvent
16692         */
16693        "beforenodedrop" : true,
16694        /**
16695         * @event nodedrop
16696         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16697         * passed to handlers has the following properties:<br />
16698         * <ul style="padding:5px;padding-left:16px;">
16699         * <li>tree - The TreePanel</li>
16700         * <li>target - The node being targeted for the drop</li>
16701         * <li>data - The drag data from the drag source</li>
16702         * <li>point - The point of the drop - append, above or below</li>
16703         * <li>source - The drag source</li>
16704         * <li>rawEvent - Raw mouse event</li>
16705         * <li>dropNode - Dropped node(s).</li>
16706         * </ul>
16707         * @param {Object} dropEvent
16708         */
16709        "nodedrop" : true,
16710         /**
16711         * @event nodedragover
16712         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16713         * passed to handlers has the following properties:<br />
16714         * <ul style="padding:5px;padding-left:16px;">
16715         * <li>tree - The TreePanel</li>
16716         * <li>target - The node being targeted for the drop</li>
16717         * <li>data - The drag data from the drag source</li>
16718         * <li>point - The point of the drop - append, above or below</li>
16719         * <li>source - The drag source</li>
16720         * <li>rawEvent - Raw mouse event</li>
16721         * <li>dropNode - Drop node(s) provided by the source.</li>
16722         * <li>cancel - Set this to true to signal drop not allowed.</li>
16723         * </ul>
16724         * @param {Object} dragOverEvent
16725         */
16726        "nodedragover" : true
16727         
16728     });
16729     if(this.singleExpand){
16730        this.on("beforeexpand", this.restrictExpand, this);
16731     }
16732     if (this.editor) {
16733         this.editor.tree = this;
16734         this.editor = Roo.factory(this.editor, Roo.tree);
16735     }
16736     
16737     if (this.selModel) {
16738         this.selModel = Roo.factory(this.selModel, Roo.tree);
16739     }
16740    
16741 };
16742 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16743     rootVisible : true,
16744     animate: Roo.enableFx,
16745     lines : true,
16746     enableDD : false,
16747     hlDrop : Roo.enableFx,
16748   
16749     renderer: false,
16750     
16751     rendererTip: false,
16752     // private
16753     restrictExpand : function(node){
16754         var p = node.parentNode;
16755         if(p){
16756             if(p.expandedChild && p.expandedChild.parentNode == p){
16757                 p.expandedChild.collapse();
16758             }
16759             p.expandedChild = node;
16760         }
16761     },
16762
16763     // private override
16764     setRootNode : function(node){
16765         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16766         if(!this.rootVisible){
16767             node.ui = new Roo.tree.RootTreeNodeUI(node);
16768         }
16769         return node;
16770     },
16771
16772     /**
16773      * Returns the container element for this TreePanel
16774      */
16775     getEl : function(){
16776         return this.el;
16777     },
16778
16779     /**
16780      * Returns the default TreeLoader for this TreePanel
16781      */
16782     getLoader : function(){
16783         return this.loader;
16784     },
16785
16786     /**
16787      * Expand all nodes
16788      */
16789     expandAll : function(){
16790         this.root.expand(true);
16791     },
16792
16793     /**
16794      * Collapse all nodes
16795      */
16796     collapseAll : function(){
16797         this.root.collapse(true);
16798     },
16799
16800     /**
16801      * Returns the selection model used by this TreePanel
16802      */
16803     getSelectionModel : function(){
16804         if(!this.selModel){
16805             this.selModel = new Roo.tree.DefaultSelectionModel();
16806         }
16807         return this.selModel;
16808     },
16809
16810     /**
16811      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16812      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16813      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16814      * @return {Array}
16815      */
16816     getChecked : function(a, startNode){
16817         startNode = startNode || this.root;
16818         var r = [];
16819         var f = function(){
16820             if(this.attributes.checked){
16821                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16822             }
16823         }
16824         startNode.cascade(f);
16825         return r;
16826     },
16827
16828     /**
16829      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16830      * @param {String} path
16831      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16832      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16833      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16834      */
16835     expandPath : function(path, attr, callback){
16836         attr = attr || "id";
16837         var keys = path.split(this.pathSeparator);
16838         var curNode = this.root;
16839         if(curNode.attributes[attr] != keys[1]){ // invalid root
16840             if(callback){
16841                 callback(false, null);
16842             }
16843             return;
16844         }
16845         var index = 1;
16846         var f = function(){
16847             if(++index == keys.length){
16848                 if(callback){
16849                     callback(true, curNode);
16850                 }
16851                 return;
16852             }
16853             var c = curNode.findChild(attr, keys[index]);
16854             if(!c){
16855                 if(callback){
16856                     callback(false, curNode);
16857                 }
16858                 return;
16859             }
16860             curNode = c;
16861             c.expand(false, false, f);
16862         };
16863         curNode.expand(false, false, f);
16864     },
16865
16866     /**
16867      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16868      * @param {String} path
16869      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16870      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16871      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16872      */
16873     selectPath : function(path, attr, callback){
16874         attr = attr || "id";
16875         var keys = path.split(this.pathSeparator);
16876         var v = keys.pop();
16877         if(keys.length > 0){
16878             var f = function(success, node){
16879                 if(success && node){
16880                     var n = node.findChild(attr, v);
16881                     if(n){
16882                         n.select();
16883                         if(callback){
16884                             callback(true, n);
16885                         }
16886                     }else if(callback){
16887                         callback(false, n);
16888                     }
16889                 }else{
16890                     if(callback){
16891                         callback(false, n);
16892                     }
16893                 }
16894             };
16895             this.expandPath(keys.join(this.pathSeparator), attr, f);
16896         }else{
16897             this.root.select();
16898             if(callback){
16899                 callback(true, this.root);
16900             }
16901         }
16902     },
16903
16904     getTreeEl : function(){
16905         return this.el;
16906     },
16907
16908     /**
16909      * Trigger rendering of this TreePanel
16910      */
16911     render : function(){
16912         if (this.innerCt) {
16913             return this; // stop it rendering more than once!!
16914         }
16915         
16916         this.innerCt = this.el.createChild({tag:"ul",
16917                cls:"x-tree-root-ct " +
16918                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16919
16920         if(this.containerScroll){
16921             Roo.dd.ScrollManager.register(this.el);
16922         }
16923         if((this.enableDD || this.enableDrop) && !this.dropZone){
16924            /**
16925             * The dropZone used by this tree if drop is enabled
16926             * @type Roo.tree.TreeDropZone
16927             */
16928              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16929                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16930            });
16931         }
16932         if((this.enableDD || this.enableDrag) && !this.dragZone){
16933            /**
16934             * The dragZone used by this tree if drag is enabled
16935             * @type Roo.tree.TreeDragZone
16936             */
16937             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16938                ddGroup: this.ddGroup || "TreeDD",
16939                scroll: this.ddScroll
16940            });
16941         }
16942         this.getSelectionModel().init(this);
16943         if (!this.root) {
16944             Roo.log("ROOT not set in tree");
16945             return this;
16946         }
16947         this.root.render();
16948         if(!this.rootVisible){
16949             this.root.renderChildren();
16950         }
16951         return this;
16952     }
16953 });/*
16954  * Based on:
16955  * Ext JS Library 1.1.1
16956  * Copyright(c) 2006-2007, Ext JS, LLC.
16957  *
16958  * Originally Released Under LGPL - original licence link has changed is not relivant.
16959  *
16960  * Fork - LGPL
16961  * <script type="text/javascript">
16962  */
16963  
16964
16965 /**
16966  * @class Roo.tree.DefaultSelectionModel
16967  * @extends Roo.util.Observable
16968  * The default single selection for a TreePanel.
16969  * @param {Object} cfg Configuration
16970  */
16971 Roo.tree.DefaultSelectionModel = function(cfg){
16972    this.selNode = null;
16973    
16974    
16975    
16976    this.addEvents({
16977        /**
16978         * @event selectionchange
16979         * Fires when the selected node changes
16980         * @param {DefaultSelectionModel} this
16981         * @param {TreeNode} node the new selection
16982         */
16983        "selectionchange" : true,
16984
16985        /**
16986         * @event beforeselect
16987         * Fires before the selected node changes, return false to cancel the change
16988         * @param {DefaultSelectionModel} this
16989         * @param {TreeNode} node the new selection
16990         * @param {TreeNode} node the old selection
16991         */
16992        "beforeselect" : true
16993    });
16994    
16995     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16996 };
16997
16998 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16999     init : function(tree){
17000         this.tree = tree;
17001         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17002         tree.on("click", this.onNodeClick, this);
17003     },
17004     
17005     onNodeClick : function(node, e){
17006         if (e.ctrlKey && this.selNode == node)  {
17007             this.unselect(node);
17008             return;
17009         }
17010         this.select(node);
17011     },
17012     
17013     /**
17014      * Select a node.
17015      * @param {TreeNode} node The node to select
17016      * @return {TreeNode} The selected node
17017      */
17018     select : function(node){
17019         var last = this.selNode;
17020         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17021             if(last){
17022                 last.ui.onSelectedChange(false);
17023             }
17024             this.selNode = node;
17025             node.ui.onSelectedChange(true);
17026             this.fireEvent("selectionchange", this, node, last);
17027         }
17028         return node;
17029     },
17030     
17031     /**
17032      * Deselect a node.
17033      * @param {TreeNode} node The node to unselect
17034      */
17035     unselect : function(node){
17036         if(this.selNode == node){
17037             this.clearSelections();
17038         }    
17039     },
17040     
17041     /**
17042      * Clear all selections
17043      */
17044     clearSelections : function(){
17045         var n = this.selNode;
17046         if(n){
17047             n.ui.onSelectedChange(false);
17048             this.selNode = null;
17049             this.fireEvent("selectionchange", this, null);
17050         }
17051         return n;
17052     },
17053     
17054     /**
17055      * Get the selected node
17056      * @return {TreeNode} The selected node
17057      */
17058     getSelectedNode : function(){
17059         return this.selNode;    
17060     },
17061     
17062     /**
17063      * Returns true if the node is selected
17064      * @param {TreeNode} node The node to check
17065      * @return {Boolean}
17066      */
17067     isSelected : function(node){
17068         return this.selNode == node;  
17069     },
17070
17071     /**
17072      * Selects the node above the selected node in the tree, intelligently walking the nodes
17073      * @return TreeNode The new selection
17074      */
17075     selectPrevious : function(){
17076         var s = this.selNode || this.lastSelNode;
17077         if(!s){
17078             return null;
17079         }
17080         var ps = s.previousSibling;
17081         if(ps){
17082             if(!ps.isExpanded() || ps.childNodes.length < 1){
17083                 return this.select(ps);
17084             } else{
17085                 var lc = ps.lastChild;
17086                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17087                     lc = lc.lastChild;
17088                 }
17089                 return this.select(lc);
17090             }
17091         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17092             return this.select(s.parentNode);
17093         }
17094         return null;
17095     },
17096
17097     /**
17098      * Selects the node above the selected node in the tree, intelligently walking the nodes
17099      * @return TreeNode The new selection
17100      */
17101     selectNext : function(){
17102         var s = this.selNode || this.lastSelNode;
17103         if(!s){
17104             return null;
17105         }
17106         if(s.firstChild && s.isExpanded()){
17107              return this.select(s.firstChild);
17108          }else if(s.nextSibling){
17109              return this.select(s.nextSibling);
17110          }else if(s.parentNode){
17111             var newS = null;
17112             s.parentNode.bubble(function(){
17113                 if(this.nextSibling){
17114                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17115                     return false;
17116                 }
17117             });
17118             return newS;
17119          }
17120         return null;
17121     },
17122
17123     onKeyDown : function(e){
17124         var s = this.selNode || this.lastSelNode;
17125         // undesirable, but required
17126         var sm = this;
17127         if(!s){
17128             return;
17129         }
17130         var k = e.getKey();
17131         switch(k){
17132              case e.DOWN:
17133                  e.stopEvent();
17134                  this.selectNext();
17135              break;
17136              case e.UP:
17137                  e.stopEvent();
17138                  this.selectPrevious();
17139              break;
17140              case e.RIGHT:
17141                  e.preventDefault();
17142                  if(s.hasChildNodes()){
17143                      if(!s.isExpanded()){
17144                          s.expand();
17145                      }else if(s.firstChild){
17146                          this.select(s.firstChild, e);
17147                      }
17148                  }
17149              break;
17150              case e.LEFT:
17151                  e.preventDefault();
17152                  if(s.hasChildNodes() && s.isExpanded()){
17153                      s.collapse();
17154                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17155                      this.select(s.parentNode, e);
17156                  }
17157              break;
17158         };
17159     }
17160 });
17161
17162 /**
17163  * @class Roo.tree.MultiSelectionModel
17164  * @extends Roo.util.Observable
17165  * Multi selection for a TreePanel.
17166  * @param {Object} cfg Configuration
17167  */
17168 Roo.tree.MultiSelectionModel = function(){
17169    this.selNodes = [];
17170    this.selMap = {};
17171    this.addEvents({
17172        /**
17173         * @event selectionchange
17174         * Fires when the selected nodes change
17175         * @param {MultiSelectionModel} this
17176         * @param {Array} nodes Array of the selected nodes
17177         */
17178        "selectionchange" : true
17179    });
17180    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17181    
17182 };
17183
17184 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17185     init : function(tree){
17186         this.tree = tree;
17187         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17188         tree.on("click", this.onNodeClick, this);
17189     },
17190     
17191     onNodeClick : function(node, e){
17192         this.select(node, e, e.ctrlKey);
17193     },
17194     
17195     /**
17196      * Select a node.
17197      * @param {TreeNode} node The node to select
17198      * @param {EventObject} e (optional) An event associated with the selection
17199      * @param {Boolean} keepExisting True to retain existing selections
17200      * @return {TreeNode} The selected node
17201      */
17202     select : function(node, e, keepExisting){
17203         if(keepExisting !== true){
17204             this.clearSelections(true);
17205         }
17206         if(this.isSelected(node)){
17207             this.lastSelNode = node;
17208             return node;
17209         }
17210         this.selNodes.push(node);
17211         this.selMap[node.id] = node;
17212         this.lastSelNode = node;
17213         node.ui.onSelectedChange(true);
17214         this.fireEvent("selectionchange", this, this.selNodes);
17215         return node;
17216     },
17217     
17218     /**
17219      * Deselect a node.
17220      * @param {TreeNode} node The node to unselect
17221      */
17222     unselect : function(node){
17223         if(this.selMap[node.id]){
17224             node.ui.onSelectedChange(false);
17225             var sn = this.selNodes;
17226             var index = -1;
17227             if(sn.indexOf){
17228                 index = sn.indexOf(node);
17229             }else{
17230                 for(var i = 0, len = sn.length; i < len; i++){
17231                     if(sn[i] == node){
17232                         index = i;
17233                         break;
17234                     }
17235                 }
17236             }
17237             if(index != -1){
17238                 this.selNodes.splice(index, 1);
17239             }
17240             delete this.selMap[node.id];
17241             this.fireEvent("selectionchange", this, this.selNodes);
17242         }
17243     },
17244     
17245     /**
17246      * Clear all selections
17247      */
17248     clearSelections : function(suppressEvent){
17249         var sn = this.selNodes;
17250         if(sn.length > 0){
17251             for(var i = 0, len = sn.length; i < len; i++){
17252                 sn[i].ui.onSelectedChange(false);
17253             }
17254             this.selNodes = [];
17255             this.selMap = {};
17256             if(suppressEvent !== true){
17257                 this.fireEvent("selectionchange", this, this.selNodes);
17258             }
17259         }
17260     },
17261     
17262     /**
17263      * Returns true if the node is selected
17264      * @param {TreeNode} node The node to check
17265      * @return {Boolean}
17266      */
17267     isSelected : function(node){
17268         return this.selMap[node.id] ? true : false;  
17269     },
17270     
17271     /**
17272      * Returns an array of the selected nodes
17273      * @return {Array}
17274      */
17275     getSelectedNodes : function(){
17276         return this.selNodes;    
17277     },
17278
17279     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17280
17281     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17282
17283     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17284 });/*
17285  * Based on:
17286  * Ext JS Library 1.1.1
17287  * Copyright(c) 2006-2007, Ext JS, LLC.
17288  *
17289  * Originally Released Under LGPL - original licence link has changed is not relivant.
17290  *
17291  * Fork - LGPL
17292  * <script type="text/javascript">
17293  */
17294  
17295 /**
17296  * @class Roo.tree.TreeNode
17297  * @extends Roo.data.Node
17298  * @cfg {String} text The text for this node
17299  * @cfg {Boolean} expanded true to start the node expanded
17300  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17301  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17302  * @cfg {Boolean} disabled true to start the node disabled
17303  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17304  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17305  * @cfg {String} cls A css class to be added to the node
17306  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17307  * @cfg {String} href URL of the link used for the node (defaults to #)
17308  * @cfg {String} hrefTarget target frame for the link
17309  * @cfg {String} qtip An Ext QuickTip for the node
17310  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17311  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17312  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17313  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17314  * (defaults to undefined with no checkbox rendered)
17315  * @constructor
17316  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17317  */
17318 Roo.tree.TreeNode = function(attributes){
17319     attributes = attributes || {};
17320     if(typeof attributes == "string"){
17321         attributes = {text: attributes};
17322     }
17323     this.childrenRendered = false;
17324     this.rendered = false;
17325     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17326     this.expanded = attributes.expanded === true;
17327     this.isTarget = attributes.isTarget !== false;
17328     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17329     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17330
17331     /**
17332      * Read-only. The text for this node. To change it use setText().
17333      * @type String
17334      */
17335     this.text = attributes.text;
17336     /**
17337      * True if this node is disabled.
17338      * @type Boolean
17339      */
17340     this.disabled = attributes.disabled === true;
17341
17342     this.addEvents({
17343         /**
17344         * @event textchange
17345         * Fires when the text for this node is changed
17346         * @param {Node} this This node
17347         * @param {String} text The new text
17348         * @param {String} oldText The old text
17349         */
17350         "textchange" : true,
17351         /**
17352         * @event beforeexpand
17353         * Fires before this node is expanded, return false to cancel.
17354         * @param {Node} this This node
17355         * @param {Boolean} deep
17356         * @param {Boolean} anim
17357         */
17358         "beforeexpand" : true,
17359         /**
17360         * @event beforecollapse
17361         * Fires before this node is collapsed, return false to cancel.
17362         * @param {Node} this This node
17363         * @param {Boolean} deep
17364         * @param {Boolean} anim
17365         */
17366         "beforecollapse" : true,
17367         /**
17368         * @event expand
17369         * Fires when this node is expanded
17370         * @param {Node} this This node
17371         */
17372         "expand" : true,
17373         /**
17374         * @event disabledchange
17375         * Fires when the disabled status of this node changes
17376         * @param {Node} this This node
17377         * @param {Boolean} disabled
17378         */
17379         "disabledchange" : true,
17380         /**
17381         * @event collapse
17382         * Fires when this node is collapsed
17383         * @param {Node} this This node
17384         */
17385         "collapse" : true,
17386         /**
17387         * @event beforeclick
17388         * Fires before click processing. Return false to cancel the default action.
17389         * @param {Node} this This node
17390         * @param {Roo.EventObject} e The event object
17391         */
17392         "beforeclick":true,
17393         /**
17394         * @event checkchange
17395         * Fires when a node with a checkbox's checked property changes
17396         * @param {Node} this This node
17397         * @param {Boolean} checked
17398         */
17399         "checkchange":true,
17400         /**
17401         * @event click
17402         * Fires when this node is clicked
17403         * @param {Node} this This node
17404         * @param {Roo.EventObject} e The event object
17405         */
17406         "click":true,
17407         /**
17408         * @event dblclick
17409         * Fires when this node is double clicked
17410         * @param {Node} this This node
17411         * @param {Roo.EventObject} e The event object
17412         */
17413         "dblclick":true,
17414         /**
17415         * @event contextmenu
17416         * Fires when this node is right clicked
17417         * @param {Node} this This node
17418         * @param {Roo.EventObject} e The event object
17419         */
17420         "contextmenu":true,
17421         /**
17422         * @event beforechildrenrendered
17423         * Fires right before the child nodes for this node are rendered
17424         * @param {Node} this This node
17425         */
17426         "beforechildrenrendered":true
17427     });
17428
17429     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17430
17431     /**
17432      * Read-only. The UI for this node
17433      * @type TreeNodeUI
17434      */
17435     this.ui = new uiClass(this);
17436     
17437     // finally support items[]
17438     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17439         return;
17440     }
17441     
17442     
17443     Roo.each(this.attributes.items, function(c) {
17444         this.appendChild(Roo.factory(c,Roo.Tree));
17445     }, this);
17446     delete this.attributes.items;
17447     
17448     
17449     
17450 };
17451 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17452     preventHScroll: true,
17453     /**
17454      * Returns true if this node is expanded
17455      * @return {Boolean}
17456      */
17457     isExpanded : function(){
17458         return this.expanded;
17459     },
17460
17461     /**
17462      * Returns the UI object for this node
17463      * @return {TreeNodeUI}
17464      */
17465     getUI : function(){
17466         return this.ui;
17467     },
17468
17469     // private override
17470     setFirstChild : function(node){
17471         var of = this.firstChild;
17472         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17473         if(this.childrenRendered && of && node != of){
17474             of.renderIndent(true, true);
17475         }
17476         if(this.rendered){
17477             this.renderIndent(true, true);
17478         }
17479     },
17480
17481     // private override
17482     setLastChild : function(node){
17483         var ol = this.lastChild;
17484         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17485         if(this.childrenRendered && ol && node != ol){
17486             ol.renderIndent(true, true);
17487         }
17488         if(this.rendered){
17489             this.renderIndent(true, true);
17490         }
17491     },
17492
17493     // these methods are overridden to provide lazy rendering support
17494     // private override
17495     appendChild : function()
17496     {
17497         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17498         if(node && this.childrenRendered){
17499             node.render();
17500         }
17501         this.ui.updateExpandIcon();
17502         return node;
17503     },
17504
17505     // private override
17506     removeChild : function(node){
17507         this.ownerTree.getSelectionModel().unselect(node);
17508         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17509         // if it's been rendered remove dom node
17510         if(this.childrenRendered){
17511             node.ui.remove();
17512         }
17513         if(this.childNodes.length < 1){
17514             this.collapse(false, false);
17515         }else{
17516             this.ui.updateExpandIcon();
17517         }
17518         if(!this.firstChild) {
17519             this.childrenRendered = false;
17520         }
17521         return node;
17522     },
17523
17524     // private override
17525     insertBefore : function(node, refNode){
17526         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17527         if(newNode && refNode && this.childrenRendered){
17528             node.render();
17529         }
17530         this.ui.updateExpandIcon();
17531         return newNode;
17532     },
17533
17534     /**
17535      * Sets the text for this node
17536      * @param {String} text
17537      */
17538     setText : function(text){
17539         var oldText = this.text;
17540         this.text = text;
17541         this.attributes.text = text;
17542         if(this.rendered){ // event without subscribing
17543             this.ui.onTextChange(this, text, oldText);
17544         }
17545         this.fireEvent("textchange", this, text, oldText);
17546     },
17547
17548     /**
17549      * Triggers selection of this node
17550      */
17551     select : function(){
17552         this.getOwnerTree().getSelectionModel().select(this);
17553     },
17554
17555     /**
17556      * Triggers deselection of this node
17557      */
17558     unselect : function(){
17559         this.getOwnerTree().getSelectionModel().unselect(this);
17560     },
17561
17562     /**
17563      * Returns true if this node is selected
17564      * @return {Boolean}
17565      */
17566     isSelected : function(){
17567         return this.getOwnerTree().getSelectionModel().isSelected(this);
17568     },
17569
17570     /**
17571      * Expand this node.
17572      * @param {Boolean} deep (optional) True to expand all children as well
17573      * @param {Boolean} anim (optional) false to cancel the default animation
17574      * @param {Function} callback (optional) A callback to be called when
17575      * expanding this node completes (does not wait for deep expand to complete).
17576      * Called with 1 parameter, this node.
17577      */
17578     expand : function(deep, anim, callback){
17579         if(!this.expanded){
17580             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17581                 return;
17582             }
17583             if(!this.childrenRendered){
17584                 this.renderChildren();
17585             }
17586             this.expanded = true;
17587             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17588                 this.ui.animExpand(function(){
17589                     this.fireEvent("expand", this);
17590                     if(typeof callback == "function"){
17591                         callback(this);
17592                     }
17593                     if(deep === true){
17594                         this.expandChildNodes(true);
17595                     }
17596                 }.createDelegate(this));
17597                 return;
17598             }else{
17599                 this.ui.expand();
17600                 this.fireEvent("expand", this);
17601                 if(typeof callback == "function"){
17602                     callback(this);
17603                 }
17604             }
17605         }else{
17606            if(typeof callback == "function"){
17607                callback(this);
17608            }
17609         }
17610         if(deep === true){
17611             this.expandChildNodes(true);
17612         }
17613     },
17614
17615     isHiddenRoot : function(){
17616         return this.isRoot && !this.getOwnerTree().rootVisible;
17617     },
17618
17619     /**
17620      * Collapse this node.
17621      * @param {Boolean} deep (optional) True to collapse all children as well
17622      * @param {Boolean} anim (optional) false to cancel the default animation
17623      */
17624     collapse : function(deep, anim){
17625         if(this.expanded && !this.isHiddenRoot()){
17626             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17627                 return;
17628             }
17629             this.expanded = false;
17630             if((this.getOwnerTree().animate && anim !== false) || anim){
17631                 this.ui.animCollapse(function(){
17632                     this.fireEvent("collapse", this);
17633                     if(deep === true){
17634                         this.collapseChildNodes(true);
17635                     }
17636                 }.createDelegate(this));
17637                 return;
17638             }else{
17639                 this.ui.collapse();
17640                 this.fireEvent("collapse", this);
17641             }
17642         }
17643         if(deep === true){
17644             var cs = this.childNodes;
17645             for(var i = 0, len = cs.length; i < len; i++) {
17646                 cs[i].collapse(true, false);
17647             }
17648         }
17649     },
17650
17651     // private
17652     delayedExpand : function(delay){
17653         if(!this.expandProcId){
17654             this.expandProcId = this.expand.defer(delay, this);
17655         }
17656     },
17657
17658     // private
17659     cancelExpand : function(){
17660         if(this.expandProcId){
17661             clearTimeout(this.expandProcId);
17662         }
17663         this.expandProcId = false;
17664     },
17665
17666     /**
17667      * Toggles expanded/collapsed state of the node
17668      */
17669     toggle : function(){
17670         if(this.expanded){
17671             this.collapse();
17672         }else{
17673             this.expand();
17674         }
17675     },
17676
17677     /**
17678      * Ensures all parent nodes are expanded
17679      */
17680     ensureVisible : function(callback){
17681         var tree = this.getOwnerTree();
17682         tree.expandPath(this.parentNode.getPath(), false, function(){
17683             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17684             Roo.callback(callback);
17685         }.createDelegate(this));
17686     },
17687
17688     /**
17689      * Expand all child nodes
17690      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17691      */
17692     expandChildNodes : function(deep){
17693         var cs = this.childNodes;
17694         for(var i = 0, len = cs.length; i < len; i++) {
17695                 cs[i].expand(deep);
17696         }
17697     },
17698
17699     /**
17700      * Collapse all child nodes
17701      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17702      */
17703     collapseChildNodes : function(deep){
17704         var cs = this.childNodes;
17705         for(var i = 0, len = cs.length; i < len; i++) {
17706                 cs[i].collapse(deep);
17707         }
17708     },
17709
17710     /**
17711      * Disables this node
17712      */
17713     disable : function(){
17714         this.disabled = true;
17715         this.unselect();
17716         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17717             this.ui.onDisableChange(this, true);
17718         }
17719         this.fireEvent("disabledchange", this, true);
17720     },
17721
17722     /**
17723      * Enables this node
17724      */
17725     enable : function(){
17726         this.disabled = false;
17727         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17728             this.ui.onDisableChange(this, false);
17729         }
17730         this.fireEvent("disabledchange", this, false);
17731     },
17732
17733     // private
17734     renderChildren : function(suppressEvent){
17735         if(suppressEvent !== false){
17736             this.fireEvent("beforechildrenrendered", this);
17737         }
17738         var cs = this.childNodes;
17739         for(var i = 0, len = cs.length; i < len; i++){
17740             cs[i].render(true);
17741         }
17742         this.childrenRendered = true;
17743     },
17744
17745     // private
17746     sort : function(fn, scope){
17747         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17748         if(this.childrenRendered){
17749             var cs = this.childNodes;
17750             for(var i = 0, len = cs.length; i < len; i++){
17751                 cs[i].render(true);
17752             }
17753         }
17754     },
17755
17756     // private
17757     render : function(bulkRender){
17758         this.ui.render(bulkRender);
17759         if(!this.rendered){
17760             this.rendered = true;
17761             if(this.expanded){
17762                 this.expanded = false;
17763                 this.expand(false, false);
17764             }
17765         }
17766     },
17767
17768     // private
17769     renderIndent : function(deep, refresh){
17770         if(refresh){
17771             this.ui.childIndent = null;
17772         }
17773         this.ui.renderIndent();
17774         if(deep === true && this.childrenRendered){
17775             var cs = this.childNodes;
17776             for(var i = 0, len = cs.length; i < len; i++){
17777                 cs[i].renderIndent(true, refresh);
17778             }
17779         }
17780     }
17781 });/*
17782  * Based on:
17783  * Ext JS Library 1.1.1
17784  * Copyright(c) 2006-2007, Ext JS, LLC.
17785  *
17786  * Originally Released Under LGPL - original licence link has changed is not relivant.
17787  *
17788  * Fork - LGPL
17789  * <script type="text/javascript">
17790  */
17791  
17792 /**
17793  * @class Roo.tree.AsyncTreeNode
17794  * @extends Roo.tree.TreeNode
17795  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17796  * @constructor
17797  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17798  */
17799  Roo.tree.AsyncTreeNode = function(config){
17800     this.loaded = false;
17801     this.loading = false;
17802     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17803     /**
17804     * @event beforeload
17805     * Fires before this node is loaded, return false to cancel
17806     * @param {Node} this This node
17807     */
17808     this.addEvents({'beforeload':true, 'load': true});
17809     /**
17810     * @event load
17811     * Fires when this node is loaded
17812     * @param {Node} this This node
17813     */
17814     /**
17815      * The loader used by this node (defaults to using the tree's defined loader)
17816      * @type TreeLoader
17817      * @property loader
17818      */
17819 };
17820 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17821     expand : function(deep, anim, callback){
17822         if(this.loading){ // if an async load is already running, waiting til it's done
17823             var timer;
17824             var f = function(){
17825                 if(!this.loading){ // done loading
17826                     clearInterval(timer);
17827                     this.expand(deep, anim, callback);
17828                 }
17829             }.createDelegate(this);
17830             timer = setInterval(f, 200);
17831             return;
17832         }
17833         if(!this.loaded){
17834             if(this.fireEvent("beforeload", this) === false){
17835                 return;
17836             }
17837             this.loading = true;
17838             this.ui.beforeLoad(this);
17839             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17840             if(loader){
17841                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17842                 return;
17843             }
17844         }
17845         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17846     },
17847     
17848     /**
17849      * Returns true if this node is currently loading
17850      * @return {Boolean}
17851      */
17852     isLoading : function(){
17853         return this.loading;  
17854     },
17855     
17856     loadComplete : function(deep, anim, callback){
17857         this.loading = false;
17858         this.loaded = true;
17859         this.ui.afterLoad(this);
17860         this.fireEvent("load", this);
17861         this.expand(deep, anim, callback);
17862     },
17863     
17864     /**
17865      * Returns true if this node has been loaded
17866      * @return {Boolean}
17867      */
17868     isLoaded : function(){
17869         return this.loaded;
17870     },
17871     
17872     hasChildNodes : function(){
17873         if(!this.isLeaf() && !this.loaded){
17874             return true;
17875         }else{
17876             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17877         }
17878     },
17879
17880     /**
17881      * Trigger a reload for this node
17882      * @param {Function} callback
17883      */
17884     reload : function(callback){
17885         this.collapse(false, false);
17886         while(this.firstChild){
17887             this.removeChild(this.firstChild);
17888         }
17889         this.childrenRendered = false;
17890         this.loaded = false;
17891         if(this.isHiddenRoot()){
17892             this.expanded = false;
17893         }
17894         this.expand(false, false, callback);
17895     }
17896 });/*
17897  * Based on:
17898  * Ext JS Library 1.1.1
17899  * Copyright(c) 2006-2007, Ext JS, LLC.
17900  *
17901  * Originally Released Under LGPL - original licence link has changed is not relivant.
17902  *
17903  * Fork - LGPL
17904  * <script type="text/javascript">
17905  */
17906  
17907 /**
17908  * @class Roo.tree.TreeNodeUI
17909  * @constructor
17910  * @param {Object} node The node to render
17911  * The TreeNode UI implementation is separate from the
17912  * tree implementation. Unless you are customizing the tree UI,
17913  * you should never have to use this directly.
17914  */
17915 Roo.tree.TreeNodeUI = function(node){
17916     this.node = node;
17917     this.rendered = false;
17918     this.animating = false;
17919     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17920 };
17921
17922 Roo.tree.TreeNodeUI.prototype = {
17923     removeChild : function(node){
17924         if(this.rendered){
17925             this.ctNode.removeChild(node.ui.getEl());
17926         }
17927     },
17928
17929     beforeLoad : function(){
17930          this.addClass("x-tree-node-loading");
17931     },
17932
17933     afterLoad : function(){
17934          this.removeClass("x-tree-node-loading");
17935     },
17936
17937     onTextChange : function(node, text, oldText){
17938         if(this.rendered){
17939             this.textNode.innerHTML = text;
17940         }
17941     },
17942
17943     onDisableChange : function(node, state){
17944         this.disabled = state;
17945         if(state){
17946             this.addClass("x-tree-node-disabled");
17947         }else{
17948             this.removeClass("x-tree-node-disabled");
17949         }
17950     },
17951
17952     onSelectedChange : function(state){
17953         if(state){
17954             this.focus();
17955             this.addClass("x-tree-selected");
17956         }else{
17957             //this.blur();
17958             this.removeClass("x-tree-selected");
17959         }
17960     },
17961
17962     onMove : function(tree, node, oldParent, newParent, index, refNode){
17963         this.childIndent = null;
17964         if(this.rendered){
17965             var targetNode = newParent.ui.getContainer();
17966             if(!targetNode){//target not rendered
17967                 this.holder = document.createElement("div");
17968                 this.holder.appendChild(this.wrap);
17969                 return;
17970             }
17971             var insertBefore = refNode ? refNode.ui.getEl() : null;
17972             if(insertBefore){
17973                 targetNode.insertBefore(this.wrap, insertBefore);
17974             }else{
17975                 targetNode.appendChild(this.wrap);
17976             }
17977             this.node.renderIndent(true);
17978         }
17979     },
17980
17981     addClass : function(cls){
17982         if(this.elNode){
17983             Roo.fly(this.elNode).addClass(cls);
17984         }
17985     },
17986
17987     removeClass : function(cls){
17988         if(this.elNode){
17989             Roo.fly(this.elNode).removeClass(cls);
17990         }
17991     },
17992
17993     remove : function(){
17994         if(this.rendered){
17995             this.holder = document.createElement("div");
17996             this.holder.appendChild(this.wrap);
17997         }
17998     },
17999
18000     fireEvent : function(){
18001         return this.node.fireEvent.apply(this.node, arguments);
18002     },
18003
18004     initEvents : function(){
18005         this.node.on("move", this.onMove, this);
18006         var E = Roo.EventManager;
18007         var a = this.anchor;
18008
18009         var el = Roo.fly(a, '_treeui');
18010
18011         if(Roo.isOpera){ // opera render bug ignores the CSS
18012             el.setStyle("text-decoration", "none");
18013         }
18014
18015         el.on("click", this.onClick, this);
18016         el.on("dblclick", this.onDblClick, this);
18017
18018         if(this.checkbox){
18019             Roo.EventManager.on(this.checkbox,
18020                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18021         }
18022
18023         el.on("contextmenu", this.onContextMenu, this);
18024
18025         var icon = Roo.fly(this.iconNode);
18026         icon.on("click", this.onClick, this);
18027         icon.on("dblclick", this.onDblClick, this);
18028         icon.on("contextmenu", this.onContextMenu, this);
18029         E.on(this.ecNode, "click", this.ecClick, this, true);
18030
18031         if(this.node.disabled){
18032             this.addClass("x-tree-node-disabled");
18033         }
18034         if(this.node.hidden){
18035             this.addClass("x-tree-node-disabled");
18036         }
18037         var ot = this.node.getOwnerTree();
18038         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18039         if(dd && (!this.node.isRoot || ot.rootVisible)){
18040             Roo.dd.Registry.register(this.elNode, {
18041                 node: this.node,
18042                 handles: this.getDDHandles(),
18043                 isHandle: false
18044             });
18045         }
18046     },
18047
18048     getDDHandles : function(){
18049         return [this.iconNode, this.textNode];
18050     },
18051
18052     hide : function(){
18053         if(this.rendered){
18054             this.wrap.style.display = "none";
18055         }
18056     },
18057
18058     show : function(){
18059         if(this.rendered){
18060             this.wrap.style.display = "";
18061         }
18062     },
18063
18064     onContextMenu : function(e){
18065         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18066             e.preventDefault();
18067             this.focus();
18068             this.fireEvent("contextmenu", this.node, e);
18069         }
18070     },
18071
18072     onClick : function(e){
18073         if(this.dropping){
18074             e.stopEvent();
18075             return;
18076         }
18077         if(this.fireEvent("beforeclick", this.node, e) !== false){
18078             if(!this.disabled && this.node.attributes.href){
18079                 this.fireEvent("click", this.node, e);
18080                 return;
18081             }
18082             e.preventDefault();
18083             if(this.disabled){
18084                 return;
18085             }
18086
18087             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18088                 this.node.toggle();
18089             }
18090
18091             this.fireEvent("click", this.node, e);
18092         }else{
18093             e.stopEvent();
18094         }
18095     },
18096
18097     onDblClick : function(e){
18098         e.preventDefault();
18099         if(this.disabled){
18100             return;
18101         }
18102         if(this.checkbox){
18103             this.toggleCheck();
18104         }
18105         if(!this.animating && this.node.hasChildNodes()){
18106             this.node.toggle();
18107         }
18108         this.fireEvent("dblclick", this.node, e);
18109     },
18110
18111     onCheckChange : function(){
18112         var checked = this.checkbox.checked;
18113         this.node.attributes.checked = checked;
18114         this.fireEvent('checkchange', this.node, checked);
18115     },
18116
18117     ecClick : function(e){
18118         if(!this.animating && this.node.hasChildNodes()){
18119             this.node.toggle();
18120         }
18121     },
18122
18123     startDrop : function(){
18124         this.dropping = true;
18125     },
18126
18127     // delayed drop so the click event doesn't get fired on a drop
18128     endDrop : function(){
18129        setTimeout(function(){
18130            this.dropping = false;
18131        }.createDelegate(this), 50);
18132     },
18133
18134     expand : function(){
18135         this.updateExpandIcon();
18136         this.ctNode.style.display = "";
18137     },
18138
18139     focus : function(){
18140         if(!this.node.preventHScroll){
18141             try{this.anchor.focus();
18142             }catch(e){}
18143         }else if(!Roo.isIE){
18144             try{
18145                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18146                 var l = noscroll.scrollLeft;
18147                 this.anchor.focus();
18148                 noscroll.scrollLeft = l;
18149             }catch(e){}
18150         }
18151     },
18152
18153     toggleCheck : function(value){
18154         var cb = this.checkbox;
18155         if(cb){
18156             cb.checked = (value === undefined ? !cb.checked : value);
18157         }
18158     },
18159
18160     blur : function(){
18161         try{
18162             this.anchor.blur();
18163         }catch(e){}
18164     },
18165
18166     animExpand : function(callback){
18167         var ct = Roo.get(this.ctNode);
18168         ct.stopFx();
18169         if(!this.node.hasChildNodes()){
18170             this.updateExpandIcon();
18171             this.ctNode.style.display = "";
18172             Roo.callback(callback);
18173             return;
18174         }
18175         this.animating = true;
18176         this.updateExpandIcon();
18177
18178         ct.slideIn('t', {
18179            callback : function(){
18180                this.animating = false;
18181                Roo.callback(callback);
18182             },
18183             scope: this,
18184             duration: this.node.ownerTree.duration || .25
18185         });
18186     },
18187
18188     highlight : function(){
18189         var tree = this.node.getOwnerTree();
18190         Roo.fly(this.wrap).highlight(
18191             tree.hlColor || "C3DAF9",
18192             {endColor: tree.hlBaseColor}
18193         );
18194     },
18195
18196     collapse : function(){
18197         this.updateExpandIcon();
18198         this.ctNode.style.display = "none";
18199     },
18200
18201     animCollapse : function(callback){
18202         var ct = Roo.get(this.ctNode);
18203         ct.enableDisplayMode('block');
18204         ct.stopFx();
18205
18206         this.animating = true;
18207         this.updateExpandIcon();
18208
18209         ct.slideOut('t', {
18210             callback : function(){
18211                this.animating = false;
18212                Roo.callback(callback);
18213             },
18214             scope: this,
18215             duration: this.node.ownerTree.duration || .25
18216         });
18217     },
18218
18219     getContainer : function(){
18220         return this.ctNode;
18221     },
18222
18223     getEl : function(){
18224         return this.wrap;
18225     },
18226
18227     appendDDGhost : function(ghostNode){
18228         ghostNode.appendChild(this.elNode.cloneNode(true));
18229     },
18230
18231     getDDRepairXY : function(){
18232         return Roo.lib.Dom.getXY(this.iconNode);
18233     },
18234
18235     onRender : function(){
18236         this.render();
18237     },
18238
18239     render : function(bulkRender){
18240         var n = this.node, a = n.attributes;
18241         var targetNode = n.parentNode ?
18242               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18243
18244         if(!this.rendered){
18245             this.rendered = true;
18246
18247             this.renderElements(n, a, targetNode, bulkRender);
18248
18249             if(a.qtip){
18250                if(this.textNode.setAttributeNS){
18251                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18252                    if(a.qtipTitle){
18253                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18254                    }
18255                }else{
18256                    this.textNode.setAttribute("ext:qtip", a.qtip);
18257                    if(a.qtipTitle){
18258                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18259                    }
18260                }
18261             }else if(a.qtipCfg){
18262                 a.qtipCfg.target = Roo.id(this.textNode);
18263                 Roo.QuickTips.register(a.qtipCfg);
18264             }
18265             this.initEvents();
18266             if(!this.node.expanded){
18267                 this.updateExpandIcon();
18268             }
18269         }else{
18270             if(bulkRender === true) {
18271                 targetNode.appendChild(this.wrap);
18272             }
18273         }
18274     },
18275
18276     renderElements : function(n, a, targetNode, bulkRender)
18277     {
18278         // add some indent caching, this helps performance when rendering a large tree
18279         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18280         var t = n.getOwnerTree();
18281         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18282         if (typeof(n.attributes.html) != 'undefined') {
18283             txt = n.attributes.html;
18284         }
18285         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18286         var cb = typeof a.checked == 'boolean';
18287         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18288         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18289             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18290             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18291             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18292             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18293             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18294              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18295                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18296             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18297             "</li>"];
18298
18299         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18300             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18301                                 n.nextSibling.ui.getEl(), buf.join(""));
18302         }else{
18303             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18304         }
18305
18306         this.elNode = this.wrap.childNodes[0];
18307         this.ctNode = this.wrap.childNodes[1];
18308         var cs = this.elNode.childNodes;
18309         this.indentNode = cs[0];
18310         this.ecNode = cs[1];
18311         this.iconNode = cs[2];
18312         var index = 3;
18313         if(cb){
18314             this.checkbox = cs[3];
18315             index++;
18316         }
18317         this.anchor = cs[index];
18318         this.textNode = cs[index].firstChild;
18319     },
18320
18321     getAnchor : function(){
18322         return this.anchor;
18323     },
18324
18325     getTextEl : function(){
18326         return this.textNode;
18327     },
18328
18329     getIconEl : function(){
18330         return this.iconNode;
18331     },
18332
18333     isChecked : function(){
18334         return this.checkbox ? this.checkbox.checked : false;
18335     },
18336
18337     updateExpandIcon : function(){
18338         if(this.rendered){
18339             var n = this.node, c1, c2;
18340             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18341             var hasChild = n.hasChildNodes();
18342             if(hasChild){
18343                 if(n.expanded){
18344                     cls += "-minus";
18345                     c1 = "x-tree-node-collapsed";
18346                     c2 = "x-tree-node-expanded";
18347                 }else{
18348                     cls += "-plus";
18349                     c1 = "x-tree-node-expanded";
18350                     c2 = "x-tree-node-collapsed";
18351                 }
18352                 if(this.wasLeaf){
18353                     this.removeClass("x-tree-node-leaf");
18354                     this.wasLeaf = false;
18355                 }
18356                 if(this.c1 != c1 || this.c2 != c2){
18357                     Roo.fly(this.elNode).replaceClass(c1, c2);
18358                     this.c1 = c1; this.c2 = c2;
18359                 }
18360             }else{
18361                 // this changes non-leafs into leafs if they have no children.
18362                 // it's not very rational behaviour..
18363                 
18364                 if(!this.wasLeaf && this.node.leaf){
18365                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18366                     delete this.c1;
18367                     delete this.c2;
18368                     this.wasLeaf = true;
18369                 }
18370             }
18371             var ecc = "x-tree-ec-icon "+cls;
18372             if(this.ecc != ecc){
18373                 this.ecNode.className = ecc;
18374                 this.ecc = ecc;
18375             }
18376         }
18377     },
18378
18379     getChildIndent : function(){
18380         if(!this.childIndent){
18381             var buf = [];
18382             var p = this.node;
18383             while(p){
18384                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18385                     if(!p.isLast()) {
18386                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18387                     } else {
18388                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18389                     }
18390                 }
18391                 p = p.parentNode;
18392             }
18393             this.childIndent = buf.join("");
18394         }
18395         return this.childIndent;
18396     },
18397
18398     renderIndent : function(){
18399         if(this.rendered){
18400             var indent = "";
18401             var p = this.node.parentNode;
18402             if(p){
18403                 indent = p.ui.getChildIndent();
18404             }
18405             if(this.indentMarkup != indent){ // don't rerender if not required
18406                 this.indentNode.innerHTML = indent;
18407                 this.indentMarkup = indent;
18408             }
18409             this.updateExpandIcon();
18410         }
18411     }
18412 };
18413
18414 Roo.tree.RootTreeNodeUI = function(){
18415     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18416 };
18417 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18418     render : function(){
18419         if(!this.rendered){
18420             var targetNode = this.node.ownerTree.innerCt.dom;
18421             this.node.expanded = true;
18422             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18423             this.wrap = this.ctNode = targetNode.firstChild;
18424         }
18425     },
18426     collapse : function(){
18427     },
18428     expand : function(){
18429     }
18430 });/*
18431  * Based on:
18432  * Ext JS Library 1.1.1
18433  * Copyright(c) 2006-2007, Ext JS, LLC.
18434  *
18435  * Originally Released Under LGPL - original licence link has changed is not relivant.
18436  *
18437  * Fork - LGPL
18438  * <script type="text/javascript">
18439  */
18440 /**
18441  * @class Roo.tree.TreeLoader
18442  * @extends Roo.util.Observable
18443  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18444  * nodes from a specified URL. The response must be a javascript Array definition
18445  * who's elements are node definition objects. eg:
18446  * <pre><code>
18447 {  success : true,
18448    data :      [
18449    
18450     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18451     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18452     ]
18453 }
18454
18455
18456 </code></pre>
18457  * <br><br>
18458  * The old style respose with just an array is still supported, but not recommended.
18459  * <br><br>
18460  *
18461  * A server request is sent, and child nodes are loaded only when a node is expanded.
18462  * The loading node's id is passed to the server under the parameter name "node" to
18463  * enable the server to produce the correct child nodes.
18464  * <br><br>
18465  * To pass extra parameters, an event handler may be attached to the "beforeload"
18466  * event, and the parameters specified in the TreeLoader's baseParams property:
18467  * <pre><code>
18468     myTreeLoader.on("beforeload", function(treeLoader, node) {
18469         this.baseParams.category = node.attributes.category;
18470     }, this);
18471 </code></pre><
18472  * This would pass an HTTP parameter called "category" to the server containing
18473  * the value of the Node's "category" attribute.
18474  * @constructor
18475  * Creates a new Treeloader.
18476  * @param {Object} config A config object containing config properties.
18477  */
18478 Roo.tree.TreeLoader = function(config){
18479     this.baseParams = {};
18480     this.requestMethod = "POST";
18481     Roo.apply(this, config);
18482
18483     this.addEvents({
18484     
18485         /**
18486          * @event beforeload
18487          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18488          * @param {Object} This TreeLoader object.
18489          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18490          * @param {Object} callback The callback function specified in the {@link #load} call.
18491          */
18492         beforeload : true,
18493         /**
18494          * @event load
18495          * Fires when the node has been successfuly loaded.
18496          * @param {Object} This TreeLoader object.
18497          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18498          * @param {Object} response The response object containing the data from the server.
18499          */
18500         load : true,
18501         /**
18502          * @event loadexception
18503          * Fires if the network request failed.
18504          * @param {Object} This TreeLoader object.
18505          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18506          * @param {Object} response The response object containing the data from the server.
18507          */
18508         loadexception : true,
18509         /**
18510          * @event create
18511          * Fires before a node is created, enabling you to return custom Node types 
18512          * @param {Object} This TreeLoader object.
18513          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18514          */
18515         create : true
18516     });
18517
18518     Roo.tree.TreeLoader.superclass.constructor.call(this);
18519 };
18520
18521 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18522     /**
18523     * @cfg {String} dataUrl The URL from which to request a Json string which
18524     * specifies an array of node definition object representing the child nodes
18525     * to be loaded.
18526     */
18527     /**
18528     * @cfg {String} requestMethod either GET or POST
18529     * defaults to POST (due to BC)
18530     * to be loaded.
18531     */
18532     /**
18533     * @cfg {Object} baseParams (optional) An object containing properties which
18534     * specify HTTP parameters to be passed to each request for child nodes.
18535     */
18536     /**
18537     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18538     * created by this loader. If the attributes sent by the server have an attribute in this object,
18539     * they take priority.
18540     */
18541     /**
18542     * @cfg {Object} uiProviders (optional) An object containing properties which
18543     * 
18544     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18545     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18546     * <i>uiProvider</i> attribute of a returned child node is a string rather
18547     * than a reference to a TreeNodeUI implementation, this that string value
18548     * is used as a property name in the uiProviders object. You can define the provider named
18549     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18550     */
18551     uiProviders : {},
18552
18553     /**
18554     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18555     * child nodes before loading.
18556     */
18557     clearOnLoad : true,
18558
18559     /**
18560     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18561     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18562     * Grid query { data : [ .....] }
18563     */
18564     
18565     root : false,
18566      /**
18567     * @cfg {String} queryParam (optional) 
18568     * Name of the query as it will be passed on the querystring (defaults to 'node')
18569     * eg. the request will be ?node=[id]
18570     */
18571     
18572     
18573     queryParam: false,
18574     
18575     /**
18576      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18577      * This is called automatically when a node is expanded, but may be used to reload
18578      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18579      * @param {Roo.tree.TreeNode} node
18580      * @param {Function} callback
18581      */
18582     load : function(node, callback){
18583         if(this.clearOnLoad){
18584             while(node.firstChild){
18585                 node.removeChild(node.firstChild);
18586             }
18587         }
18588         if(node.attributes.children){ // preloaded json children
18589             var cs = node.attributes.children;
18590             for(var i = 0, len = cs.length; i < len; i++){
18591                 node.appendChild(this.createNode(cs[i]));
18592             }
18593             if(typeof callback == "function"){
18594                 callback();
18595             }
18596         }else if(this.dataUrl){
18597             this.requestData(node, callback);
18598         }
18599     },
18600
18601     getParams: function(node){
18602         var buf = [], bp = this.baseParams;
18603         for(var key in bp){
18604             if(typeof bp[key] != "function"){
18605                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18606             }
18607         }
18608         var n = this.queryParam === false ? 'node' : this.queryParam;
18609         buf.push(n + "=", encodeURIComponent(node.id));
18610         return buf.join("");
18611     },
18612
18613     requestData : function(node, callback){
18614         if(this.fireEvent("beforeload", this, node, callback) !== false){
18615             this.transId = Roo.Ajax.request({
18616                 method:this.requestMethod,
18617                 url: this.dataUrl||this.url,
18618                 success: this.handleResponse,
18619                 failure: this.handleFailure,
18620                 scope: this,
18621                 argument: {callback: callback, node: node},
18622                 params: this.getParams(node)
18623             });
18624         }else{
18625             // if the load is cancelled, make sure we notify
18626             // the node that we are done
18627             if(typeof callback == "function"){
18628                 callback();
18629             }
18630         }
18631     },
18632
18633     isLoading : function(){
18634         return this.transId ? true : false;
18635     },
18636
18637     abort : function(){
18638         if(this.isLoading()){
18639             Roo.Ajax.abort(this.transId);
18640         }
18641     },
18642
18643     // private
18644     createNode : function(attr)
18645     {
18646         // apply baseAttrs, nice idea Corey!
18647         if(this.baseAttrs){
18648             Roo.applyIf(attr, this.baseAttrs);
18649         }
18650         if(this.applyLoader !== false){
18651             attr.loader = this;
18652         }
18653         // uiProvider = depreciated..
18654         
18655         if(typeof(attr.uiProvider) == 'string'){
18656            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18657                 /**  eval:var:attr */ eval(attr.uiProvider);
18658         }
18659         if(typeof(this.uiProviders['default']) != 'undefined') {
18660             attr.uiProvider = this.uiProviders['default'];
18661         }
18662         
18663         this.fireEvent('create', this, attr);
18664         
18665         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18666         return(attr.leaf ?
18667                         new Roo.tree.TreeNode(attr) :
18668                         new Roo.tree.AsyncTreeNode(attr));
18669     },
18670
18671     processResponse : function(response, node, callback)
18672     {
18673         var json = response.responseText;
18674         try {
18675             
18676             var o = Roo.decode(json);
18677             
18678             if (this.root === false && typeof(o.success) != undefined) {
18679                 this.root = 'data'; // the default behaviour for list like data..
18680                 }
18681                 
18682             if (this.root !== false &&  !o.success) {
18683                 // it's a failure condition.
18684                 var a = response.argument;
18685                 this.fireEvent("loadexception", this, a.node, response);
18686                 Roo.log("Load failed - should have a handler really");
18687                 return;
18688             }
18689             
18690             
18691             
18692             if (this.root !== false) {
18693                  o = o[this.root];
18694             }
18695             
18696             for(var i = 0, len = o.length; i < len; i++){
18697                 var n = this.createNode(o[i]);
18698                 if(n){
18699                     node.appendChild(n);
18700                 }
18701             }
18702             if(typeof callback == "function"){
18703                 callback(this, node);
18704             }
18705         }catch(e){
18706             this.handleFailure(response);
18707         }
18708     },
18709
18710     handleResponse : function(response){
18711         this.transId = false;
18712         var a = response.argument;
18713         this.processResponse(response, a.node, a.callback);
18714         this.fireEvent("load", this, a.node, response);
18715     },
18716
18717     handleFailure : function(response)
18718     {
18719         // should handle failure better..
18720         this.transId = false;
18721         var a = response.argument;
18722         this.fireEvent("loadexception", this, a.node, response);
18723         if(typeof a.callback == "function"){
18724             a.callback(this, a.node);
18725         }
18726     }
18727 });/*
18728  * Based on:
18729  * Ext JS Library 1.1.1
18730  * Copyright(c) 2006-2007, Ext JS, LLC.
18731  *
18732  * Originally Released Under LGPL - original licence link has changed is not relivant.
18733  *
18734  * Fork - LGPL
18735  * <script type="text/javascript">
18736  */
18737
18738 /**
18739 * @class Roo.tree.TreeFilter
18740 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18741 * @param {TreePanel} tree
18742 * @param {Object} config (optional)
18743  */
18744 Roo.tree.TreeFilter = function(tree, config){
18745     this.tree = tree;
18746     this.filtered = {};
18747     Roo.apply(this, config);
18748 };
18749
18750 Roo.tree.TreeFilter.prototype = {
18751     clearBlank:false,
18752     reverse:false,
18753     autoClear:false,
18754     remove:false,
18755
18756      /**
18757      * Filter the data by a specific attribute.
18758      * @param {String/RegExp} value Either string that the attribute value
18759      * should start with or a RegExp to test against the attribute
18760      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18761      * @param {TreeNode} startNode (optional) The node to start the filter at.
18762      */
18763     filter : function(value, attr, startNode){
18764         attr = attr || "text";
18765         var f;
18766         if(typeof value == "string"){
18767             var vlen = value.length;
18768             // auto clear empty filter
18769             if(vlen == 0 && this.clearBlank){
18770                 this.clear();
18771                 return;
18772             }
18773             value = value.toLowerCase();
18774             f = function(n){
18775                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18776             };
18777         }else if(value.exec){ // regex?
18778             f = function(n){
18779                 return value.test(n.attributes[attr]);
18780             };
18781         }else{
18782             throw 'Illegal filter type, must be string or regex';
18783         }
18784         this.filterBy(f, null, startNode);
18785         },
18786
18787     /**
18788      * Filter by a function. The passed function will be called with each
18789      * node in the tree (or from the startNode). If the function returns true, the node is kept
18790      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18791      * @param {Function} fn The filter function
18792      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18793      */
18794     filterBy : function(fn, scope, startNode){
18795         startNode = startNode || this.tree.root;
18796         if(this.autoClear){
18797             this.clear();
18798         }
18799         var af = this.filtered, rv = this.reverse;
18800         var f = function(n){
18801             if(n == startNode){
18802                 return true;
18803             }
18804             if(af[n.id]){
18805                 return false;
18806             }
18807             var m = fn.call(scope || n, n);
18808             if(!m || rv){
18809                 af[n.id] = n;
18810                 n.ui.hide();
18811                 return false;
18812             }
18813             return true;
18814         };
18815         startNode.cascade(f);
18816         if(this.remove){
18817            for(var id in af){
18818                if(typeof id != "function"){
18819                    var n = af[id];
18820                    if(n && n.parentNode){
18821                        n.parentNode.removeChild(n);
18822                    }
18823                }
18824            }
18825         }
18826     },
18827
18828     /**
18829      * Clears the current filter. Note: with the "remove" option
18830      * set a filter cannot be cleared.
18831      */
18832     clear : function(){
18833         var t = this.tree;
18834         var af = this.filtered;
18835         for(var id in af){
18836             if(typeof id != "function"){
18837                 var n = af[id];
18838                 if(n){
18839                     n.ui.show();
18840                 }
18841             }
18842         }
18843         this.filtered = {};
18844     }
18845 };
18846 /*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856  
18857
18858 /**
18859  * @class Roo.tree.TreeSorter
18860  * Provides sorting of nodes in a TreePanel
18861  * 
18862  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18863  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18864  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18865  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18866  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18867  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18868  * @constructor
18869  * @param {TreePanel} tree
18870  * @param {Object} config
18871  */
18872 Roo.tree.TreeSorter = function(tree, config){
18873     Roo.apply(this, config);
18874     tree.on("beforechildrenrendered", this.doSort, this);
18875     tree.on("append", this.updateSort, this);
18876     tree.on("insert", this.updateSort, this);
18877     
18878     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18879     var p = this.property || "text";
18880     var sortType = this.sortType;
18881     var fs = this.folderSort;
18882     var cs = this.caseSensitive === true;
18883     var leafAttr = this.leafAttr || 'leaf';
18884
18885     this.sortFn = function(n1, n2){
18886         if(fs){
18887             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18888                 return 1;
18889             }
18890             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18891                 return -1;
18892             }
18893         }
18894         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18895         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18896         if(v1 < v2){
18897                         return dsc ? +1 : -1;
18898                 }else if(v1 > v2){
18899                         return dsc ? -1 : +1;
18900         }else{
18901                 return 0;
18902         }
18903     };
18904 };
18905
18906 Roo.tree.TreeSorter.prototype = {
18907     doSort : function(node){
18908         node.sort(this.sortFn);
18909     },
18910     
18911     compareNodes : function(n1, n2){
18912         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18913     },
18914     
18915     updateSort : function(tree, node){
18916         if(node.childrenRendered){
18917             this.doSort.defer(1, this, [node]);
18918         }
18919     }
18920 };/*
18921  * Based on:
18922  * Ext JS Library 1.1.1
18923  * Copyright(c) 2006-2007, Ext JS, LLC.
18924  *
18925  * Originally Released Under LGPL - original licence link has changed is not relivant.
18926  *
18927  * Fork - LGPL
18928  * <script type="text/javascript">
18929  */
18930
18931 if(Roo.dd.DropZone){
18932     
18933 Roo.tree.TreeDropZone = function(tree, config){
18934     this.allowParentInsert = false;
18935     this.allowContainerDrop = false;
18936     this.appendOnly = false;
18937     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18938     this.tree = tree;
18939     this.lastInsertClass = "x-tree-no-status";
18940     this.dragOverData = {};
18941 };
18942
18943 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18944     ddGroup : "TreeDD",
18945     scroll:  true,
18946     
18947     expandDelay : 1000,
18948     
18949     expandNode : function(node){
18950         if(node.hasChildNodes() && !node.isExpanded()){
18951             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18952         }
18953     },
18954     
18955     queueExpand : function(node){
18956         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18957     },
18958     
18959     cancelExpand : function(){
18960         if(this.expandProcId){
18961             clearTimeout(this.expandProcId);
18962             this.expandProcId = false;
18963         }
18964     },
18965     
18966     isValidDropPoint : function(n, pt, dd, e, data){
18967         if(!n || !data){ return false; }
18968         var targetNode = n.node;
18969         var dropNode = data.node;
18970         // default drop rules
18971         if(!(targetNode && targetNode.isTarget && pt)){
18972             return false;
18973         }
18974         if(pt == "append" && targetNode.allowChildren === false){
18975             return false;
18976         }
18977         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18978             return false;
18979         }
18980         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18981             return false;
18982         }
18983         // reuse the object
18984         var overEvent = this.dragOverData;
18985         overEvent.tree = this.tree;
18986         overEvent.target = targetNode;
18987         overEvent.data = data;
18988         overEvent.point = pt;
18989         overEvent.source = dd;
18990         overEvent.rawEvent = e;
18991         overEvent.dropNode = dropNode;
18992         overEvent.cancel = false;  
18993         var result = this.tree.fireEvent("nodedragover", overEvent);
18994         return overEvent.cancel === false && result !== false;
18995     },
18996     
18997     getDropPoint : function(e, n, dd)
18998     {
18999         var tn = n.node;
19000         if(tn.isRoot){
19001             return tn.allowChildren !== false ? "append" : false; // always append for root
19002         }
19003         var dragEl = n.ddel;
19004         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19005         var y = Roo.lib.Event.getPageY(e);
19006         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19007         
19008         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19009         var noAppend = tn.allowChildren === false;
19010         if(this.appendOnly || tn.parentNode.allowChildren === false){
19011             return noAppend ? false : "append";
19012         }
19013         var noBelow = false;
19014         if(!this.allowParentInsert){
19015             noBelow = tn.hasChildNodes() && tn.isExpanded();
19016         }
19017         var q = (b - t) / (noAppend ? 2 : 3);
19018         if(y >= t && y < (t + q)){
19019             return "above";
19020         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19021             return "below";
19022         }else{
19023             return "append";
19024         }
19025     },
19026     
19027     onNodeEnter : function(n, dd, e, data)
19028     {
19029         this.cancelExpand();
19030     },
19031     
19032     onNodeOver : function(n, dd, e, data)
19033     {
19034        
19035         var pt = this.getDropPoint(e, n, dd);
19036         var node = n.node;
19037         
19038         // auto node expand check
19039         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19040             this.queueExpand(node);
19041         }else if(pt != "append"){
19042             this.cancelExpand();
19043         }
19044         
19045         // set the insert point style on the target node
19046         var returnCls = this.dropNotAllowed;
19047         if(this.isValidDropPoint(n, pt, dd, e, data)){
19048            if(pt){
19049                var el = n.ddel;
19050                var cls;
19051                if(pt == "above"){
19052                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19053                    cls = "x-tree-drag-insert-above";
19054                }else if(pt == "below"){
19055                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19056                    cls = "x-tree-drag-insert-below";
19057                }else{
19058                    returnCls = "x-tree-drop-ok-append";
19059                    cls = "x-tree-drag-append";
19060                }
19061                if(this.lastInsertClass != cls){
19062                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19063                    this.lastInsertClass = cls;
19064                }
19065            }
19066        }
19067        return returnCls;
19068     },
19069     
19070     onNodeOut : function(n, dd, e, data){
19071         
19072         this.cancelExpand();
19073         this.removeDropIndicators(n);
19074     },
19075     
19076     onNodeDrop : function(n, dd, e, data){
19077         var point = this.getDropPoint(e, n, dd);
19078         var targetNode = n.node;
19079         targetNode.ui.startDrop();
19080         if(!this.isValidDropPoint(n, point, dd, e, data)){
19081             targetNode.ui.endDrop();
19082             return false;
19083         }
19084         // first try to find the drop node
19085         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19086         var dropEvent = {
19087             tree : this.tree,
19088             target: targetNode,
19089             data: data,
19090             point: point,
19091             source: dd,
19092             rawEvent: e,
19093             dropNode: dropNode,
19094             cancel: !dropNode   
19095         };
19096         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19097         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19098             targetNode.ui.endDrop();
19099             return false;
19100         }
19101         // allow target changing
19102         targetNode = dropEvent.target;
19103         if(point == "append" && !targetNode.isExpanded()){
19104             targetNode.expand(false, null, function(){
19105                 this.completeDrop(dropEvent);
19106             }.createDelegate(this));
19107         }else{
19108             this.completeDrop(dropEvent);
19109         }
19110         return true;
19111     },
19112     
19113     completeDrop : function(de){
19114         var ns = de.dropNode, p = de.point, t = de.target;
19115         if(!(ns instanceof Array)){
19116             ns = [ns];
19117         }
19118         var n;
19119         for(var i = 0, len = ns.length; i < len; i++){
19120             n = ns[i];
19121             if(p == "above"){
19122                 t.parentNode.insertBefore(n, t);
19123             }else if(p == "below"){
19124                 t.parentNode.insertBefore(n, t.nextSibling);
19125             }else{
19126                 t.appendChild(n);
19127             }
19128         }
19129         n.ui.focus();
19130         if(this.tree.hlDrop){
19131             n.ui.highlight();
19132         }
19133         t.ui.endDrop();
19134         this.tree.fireEvent("nodedrop", de);
19135     },
19136     
19137     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19138         if(this.tree.hlDrop){
19139             dropNode.ui.focus();
19140             dropNode.ui.highlight();
19141         }
19142         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19143     },
19144     
19145     getTree : function(){
19146         return this.tree;
19147     },
19148     
19149     removeDropIndicators : function(n){
19150         if(n && n.ddel){
19151             var el = n.ddel;
19152             Roo.fly(el).removeClass([
19153                     "x-tree-drag-insert-above",
19154                     "x-tree-drag-insert-below",
19155                     "x-tree-drag-append"]);
19156             this.lastInsertClass = "_noclass";
19157         }
19158     },
19159     
19160     beforeDragDrop : function(target, e, id){
19161         this.cancelExpand();
19162         return true;
19163     },
19164     
19165     afterRepair : function(data){
19166         if(data && Roo.enableFx){
19167             data.node.ui.highlight();
19168         }
19169         this.hideProxy();
19170     } 
19171     
19172 });
19173
19174 }
19175 /*
19176  * Based on:
19177  * Ext JS Library 1.1.1
19178  * Copyright(c) 2006-2007, Ext JS, LLC.
19179  *
19180  * Originally Released Under LGPL - original licence link has changed is not relivant.
19181  *
19182  * Fork - LGPL
19183  * <script type="text/javascript">
19184  */
19185  
19186
19187 if(Roo.dd.DragZone){
19188 Roo.tree.TreeDragZone = function(tree, config){
19189     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19190     this.tree = tree;
19191 };
19192
19193 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19194     ddGroup : "TreeDD",
19195    
19196     onBeforeDrag : function(data, e){
19197         var n = data.node;
19198         return n && n.draggable && !n.disabled;
19199     },
19200      
19201     
19202     onInitDrag : function(e){
19203         var data = this.dragData;
19204         this.tree.getSelectionModel().select(data.node);
19205         this.proxy.update("");
19206         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19207         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19208     },
19209     
19210     getRepairXY : function(e, data){
19211         return data.node.ui.getDDRepairXY();
19212     },
19213     
19214     onEndDrag : function(data, e){
19215         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19216         
19217         
19218     },
19219     
19220     onValidDrop : function(dd, e, id){
19221         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19222         this.hideProxy();
19223     },
19224     
19225     beforeInvalidDrop : function(e, id){
19226         // this scrolls the original position back into view
19227         var sm = this.tree.getSelectionModel();
19228         sm.clearSelections();
19229         sm.select(this.dragData.node);
19230     }
19231 });
19232 }/*
19233  * Based on:
19234  * Ext JS Library 1.1.1
19235  * Copyright(c) 2006-2007, Ext JS, LLC.
19236  *
19237  * Originally Released Under LGPL - original licence link has changed is not relivant.
19238  *
19239  * Fork - LGPL
19240  * <script type="text/javascript">
19241  */
19242 /**
19243  * @class Roo.tree.TreeEditor
19244  * @extends Roo.Editor
19245  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19246  * as the editor field.
19247  * @constructor
19248  * @param {Object} config (used to be the tree panel.)
19249  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19250  * 
19251  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19252  * @cfg {Roo.form.TextField|Object} field The field configuration
19253  *
19254  * 
19255  */
19256 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19257     var tree = config;
19258     var field;
19259     if (oldconfig) { // old style..
19260         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19261     } else {
19262         // new style..
19263         tree = config.tree;
19264         config.field = config.field  || {};
19265         config.field.xtype = 'TextField';
19266         field = Roo.factory(config.field, Roo.form);
19267     }
19268     config = config || {};
19269     
19270     
19271     this.addEvents({
19272         /**
19273          * @event beforenodeedit
19274          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19275          * false from the handler of this event.
19276          * @param {Editor} this
19277          * @param {Roo.tree.Node} node 
19278          */
19279         "beforenodeedit" : true
19280     });
19281     
19282     //Roo.log(config);
19283     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19284
19285     this.tree = tree;
19286
19287     tree.on('beforeclick', this.beforeNodeClick, this);
19288     tree.getTreeEl().on('mousedown', this.hide, this);
19289     this.on('complete', this.updateNode, this);
19290     this.on('beforestartedit', this.fitToTree, this);
19291     this.on('startedit', this.bindScroll, this, {delay:10});
19292     this.on('specialkey', this.onSpecialKey, this);
19293 };
19294
19295 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19296     /**
19297      * @cfg {String} alignment
19298      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19299      */
19300     alignment: "l-l",
19301     // inherit
19302     autoSize: false,
19303     /**
19304      * @cfg {Boolean} hideEl
19305      * True to hide the bound element while the editor is displayed (defaults to false)
19306      */
19307     hideEl : false,
19308     /**
19309      * @cfg {String} cls
19310      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19311      */
19312     cls: "x-small-editor x-tree-editor",
19313     /**
19314      * @cfg {Boolean} shim
19315      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19316      */
19317     shim:false,
19318     // inherit
19319     shadow:"frame",
19320     /**
19321      * @cfg {Number} maxWidth
19322      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19323      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19324      * scroll and client offsets into account prior to each edit.
19325      */
19326     maxWidth: 250,
19327
19328     editDelay : 350,
19329
19330     // private
19331     fitToTree : function(ed, el){
19332         var td = this.tree.getTreeEl().dom, nd = el.dom;
19333         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19334             td.scrollLeft = nd.offsetLeft;
19335         }
19336         var w = Math.min(
19337                 this.maxWidth,
19338                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19339         this.setSize(w, '');
19340         
19341         return this.fireEvent('beforenodeedit', this, this.editNode);
19342         
19343     },
19344
19345     // private
19346     triggerEdit : function(node){
19347         this.completeEdit();
19348         this.editNode = node;
19349         this.startEdit(node.ui.textNode, node.text);
19350     },
19351
19352     // private
19353     bindScroll : function(){
19354         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19355     },
19356
19357     // private
19358     beforeNodeClick : function(node, e){
19359         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19360         this.lastClick = new Date();
19361         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19362             e.stopEvent();
19363             this.triggerEdit(node);
19364             return false;
19365         }
19366         return true;
19367     },
19368
19369     // private
19370     updateNode : function(ed, value){
19371         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19372         this.editNode.setText(value);
19373     },
19374
19375     // private
19376     onHide : function(){
19377         Roo.tree.TreeEditor.superclass.onHide.call(this);
19378         if(this.editNode){
19379             this.editNode.ui.focus();
19380         }
19381     },
19382
19383     // private
19384     onSpecialKey : function(field, e){
19385         var k = e.getKey();
19386         if(k == e.ESC){
19387             e.stopEvent();
19388             this.cancelEdit();
19389         }else if(k == e.ENTER && !e.hasModifier()){
19390             e.stopEvent();
19391             this.completeEdit();
19392         }
19393     }
19394 });//<Script type="text/javascript">
19395 /*
19396  * Based on:
19397  * Ext JS Library 1.1.1
19398  * Copyright(c) 2006-2007, Ext JS, LLC.
19399  *
19400  * Originally Released Under LGPL - original licence link has changed is not relivant.
19401  *
19402  * Fork - LGPL
19403  * <script type="text/javascript">
19404  */
19405  
19406 /**
19407  * Not documented??? - probably should be...
19408  */
19409
19410 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19411     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19412     
19413     renderElements : function(n, a, targetNode, bulkRender){
19414         //consel.log("renderElements?");
19415         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19416
19417         var t = n.getOwnerTree();
19418         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19419         
19420         var cols = t.columns;
19421         var bw = t.borderWidth;
19422         var c = cols[0];
19423         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19424          var cb = typeof a.checked == "boolean";
19425         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19426         var colcls = 'x-t-' + tid + '-c0';
19427         var buf = [
19428             '<li class="x-tree-node">',
19429             
19430                 
19431                 '<div class="x-tree-node-el ', a.cls,'">',
19432                     // extran...
19433                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19434                 
19435                 
19436                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19437                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19438                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19439                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19440                            (a.iconCls ? ' '+a.iconCls : ''),
19441                            '" unselectable="on" />',
19442                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19443                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19444                              
19445                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19446                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19447                             '<span unselectable="on" qtip="' + tx + '">',
19448                              tx,
19449                              '</span></a>' ,
19450                     '</div>',
19451                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19452                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19453                  ];
19454         for(var i = 1, len = cols.length; i < len; i++){
19455             c = cols[i];
19456             colcls = 'x-t-' + tid + '-c' +i;
19457             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19458             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19459                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19460                       "</div>");
19461          }
19462          
19463          buf.push(
19464             '</a>',
19465             '<div class="x-clear"></div></div>',
19466             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19467             "</li>");
19468         
19469         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19470             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19471                                 n.nextSibling.ui.getEl(), buf.join(""));
19472         }else{
19473             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19474         }
19475         var el = this.wrap.firstChild;
19476         this.elRow = el;
19477         this.elNode = el.firstChild;
19478         this.ranchor = el.childNodes[1];
19479         this.ctNode = this.wrap.childNodes[1];
19480         var cs = el.firstChild.childNodes;
19481         this.indentNode = cs[0];
19482         this.ecNode = cs[1];
19483         this.iconNode = cs[2];
19484         var index = 3;
19485         if(cb){
19486             this.checkbox = cs[3];
19487             index++;
19488         }
19489         this.anchor = cs[index];
19490         
19491         this.textNode = cs[index].firstChild;
19492         
19493         //el.on("click", this.onClick, this);
19494         //el.on("dblclick", this.onDblClick, this);
19495         
19496         
19497        // console.log(this);
19498     },
19499     initEvents : function(){
19500         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19501         
19502             
19503         var a = this.ranchor;
19504
19505         var el = Roo.get(a);
19506
19507         if(Roo.isOpera){ // opera render bug ignores the CSS
19508             el.setStyle("text-decoration", "none");
19509         }
19510
19511         el.on("click", this.onClick, this);
19512         el.on("dblclick", this.onDblClick, this);
19513         el.on("contextmenu", this.onContextMenu, this);
19514         
19515     },
19516     
19517     /*onSelectedChange : function(state){
19518         if(state){
19519             this.focus();
19520             this.addClass("x-tree-selected");
19521         }else{
19522             //this.blur();
19523             this.removeClass("x-tree-selected");
19524         }
19525     },*/
19526     addClass : function(cls){
19527         if(this.elRow){
19528             Roo.fly(this.elRow).addClass(cls);
19529         }
19530         
19531     },
19532     
19533     
19534     removeClass : function(cls){
19535         if(this.elRow){
19536             Roo.fly(this.elRow).removeClass(cls);
19537         }
19538     }
19539
19540     
19541     
19542 });//<Script type="text/javascript">
19543
19544 /*
19545  * Based on:
19546  * Ext JS Library 1.1.1
19547  * Copyright(c) 2006-2007, Ext JS, LLC.
19548  *
19549  * Originally Released Under LGPL - original licence link has changed is not relivant.
19550  *
19551  * Fork - LGPL
19552  * <script type="text/javascript">
19553  */
19554  
19555
19556 /**
19557  * @class Roo.tree.ColumnTree
19558  * @extends Roo.data.TreePanel
19559  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19560  * @cfg {int} borderWidth  compined right/left border allowance
19561  * @constructor
19562  * @param {String/HTMLElement/Element} el The container element
19563  * @param {Object} config
19564  */
19565 Roo.tree.ColumnTree =  function(el, config)
19566 {
19567    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19568    this.addEvents({
19569         /**
19570         * @event resize
19571         * Fire this event on a container when it resizes
19572         * @param {int} w Width
19573         * @param {int} h Height
19574         */
19575        "resize" : true
19576     });
19577     this.on('resize', this.onResize, this);
19578 };
19579
19580 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19581     //lines:false,
19582     
19583     
19584     borderWidth: Roo.isBorderBox ? 0 : 2, 
19585     headEls : false,
19586     
19587     render : function(){
19588         // add the header.....
19589        
19590         Roo.tree.ColumnTree.superclass.render.apply(this);
19591         
19592         this.el.addClass('x-column-tree');
19593         
19594         this.headers = this.el.createChild(
19595             {cls:'x-tree-headers'},this.innerCt.dom);
19596    
19597         var cols = this.columns, c;
19598         var totalWidth = 0;
19599         this.headEls = [];
19600         var  len = cols.length;
19601         for(var i = 0; i < len; i++){
19602              c = cols[i];
19603              totalWidth += c.width;
19604             this.headEls.push(this.headers.createChild({
19605                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19606                  cn: {
19607                      cls:'x-tree-hd-text',
19608                      html: c.header
19609                  },
19610                  style:'width:'+(c.width-this.borderWidth)+'px;'
19611              }));
19612         }
19613         this.headers.createChild({cls:'x-clear'});
19614         // prevent floats from wrapping when clipped
19615         this.headers.setWidth(totalWidth);
19616         //this.innerCt.setWidth(totalWidth);
19617         this.innerCt.setStyle({ overflow: 'auto' });
19618         this.onResize(this.width, this.height);
19619              
19620         
19621     },
19622     onResize : function(w,h)
19623     {
19624         this.height = h;
19625         this.width = w;
19626         // resize cols..
19627         this.innerCt.setWidth(this.width);
19628         this.innerCt.setHeight(this.height-20);
19629         
19630         // headers...
19631         var cols = this.columns, c;
19632         var totalWidth = 0;
19633         var expEl = false;
19634         var len = cols.length;
19635         for(var i = 0; i < len; i++){
19636             c = cols[i];
19637             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19638                 // it's the expander..
19639                 expEl  = this.headEls[i];
19640                 continue;
19641             }
19642             totalWidth += c.width;
19643             
19644         }
19645         if (expEl) {
19646             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19647         }
19648         this.headers.setWidth(w-20);
19649
19650         
19651         
19652         
19653     }
19654 });
19655 /*
19656  * Based on:
19657  * Ext JS Library 1.1.1
19658  * Copyright(c) 2006-2007, Ext JS, LLC.
19659  *
19660  * Originally Released Under LGPL - original licence link has changed is not relivant.
19661  *
19662  * Fork - LGPL
19663  * <script type="text/javascript">
19664  */
19665  
19666 /**
19667  * @class Roo.menu.Menu
19668  * @extends Roo.util.Observable
19669  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19670  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19671  * @constructor
19672  * Creates a new Menu
19673  * @param {Object} config Configuration options
19674  */
19675 Roo.menu.Menu = function(config){
19676     Roo.apply(this, config);
19677     this.id = this.id || Roo.id();
19678     this.addEvents({
19679         /**
19680          * @event beforeshow
19681          * Fires before this menu is displayed
19682          * @param {Roo.menu.Menu} this
19683          */
19684         beforeshow : true,
19685         /**
19686          * @event beforehide
19687          * Fires before this menu is hidden
19688          * @param {Roo.menu.Menu} this
19689          */
19690         beforehide : true,
19691         /**
19692          * @event show
19693          * Fires after this menu is displayed
19694          * @param {Roo.menu.Menu} this
19695          */
19696         show : true,
19697         /**
19698          * @event hide
19699          * Fires after this menu is hidden
19700          * @param {Roo.menu.Menu} this
19701          */
19702         hide : true,
19703         /**
19704          * @event click
19705          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19706          * @param {Roo.menu.Menu} this
19707          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19708          * @param {Roo.EventObject} e
19709          */
19710         click : true,
19711         /**
19712          * @event mouseover
19713          * Fires when the mouse is hovering over this menu
19714          * @param {Roo.menu.Menu} this
19715          * @param {Roo.EventObject} e
19716          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19717          */
19718         mouseover : true,
19719         /**
19720          * @event mouseout
19721          * Fires when the mouse exits this menu
19722          * @param {Roo.menu.Menu} this
19723          * @param {Roo.EventObject} e
19724          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19725          */
19726         mouseout : true,
19727         /**
19728          * @event itemclick
19729          * Fires when a menu item contained in this menu is clicked
19730          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19731          * @param {Roo.EventObject} e
19732          */
19733         itemclick: true
19734     });
19735     if (this.registerMenu) {
19736         Roo.menu.MenuMgr.register(this);
19737     }
19738     
19739     var mis = this.items;
19740     this.items = new Roo.util.MixedCollection();
19741     if(mis){
19742         this.add.apply(this, mis);
19743     }
19744 };
19745
19746 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19747     /**
19748      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19749      */
19750     minWidth : 120,
19751     /**
19752      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19753      * for bottom-right shadow (defaults to "sides")
19754      */
19755     shadow : "sides",
19756     /**
19757      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19758      * this menu (defaults to "tl-tr?")
19759      */
19760     subMenuAlign : "tl-tr?",
19761     /**
19762      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19763      * relative to its element of origin (defaults to "tl-bl?")
19764      */
19765     defaultAlign : "tl-bl?",
19766     /**
19767      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19768      */
19769     allowOtherMenus : false,
19770     /**
19771      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19772      */
19773     registerMenu : true,
19774
19775     hidden:true,
19776
19777     // private
19778     render : function(){
19779         if(this.el){
19780             return;
19781         }
19782         var el = this.el = new Roo.Layer({
19783             cls: "x-menu",
19784             shadow:this.shadow,
19785             constrain: false,
19786             parentEl: this.parentEl || document.body,
19787             zindex:15000
19788         });
19789
19790         this.keyNav = new Roo.menu.MenuNav(this);
19791
19792         if(this.plain){
19793             el.addClass("x-menu-plain");
19794         }
19795         if(this.cls){
19796             el.addClass(this.cls);
19797         }
19798         // generic focus element
19799         this.focusEl = el.createChild({
19800             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19801         });
19802         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19803         ul.on("click", this.onClick, this);
19804         ul.on("mouseover", this.onMouseOver, this);
19805         ul.on("mouseout", this.onMouseOut, this);
19806         this.items.each(function(item){
19807             var li = document.createElement("li");
19808             li.className = "x-menu-list-item";
19809             ul.dom.appendChild(li);
19810             item.render(li, this);
19811         }, this);
19812         this.ul = ul;
19813         this.autoWidth();
19814     },
19815
19816     // private
19817     autoWidth : function(){
19818         var el = this.el, ul = this.ul;
19819         if(!el){
19820             return;
19821         }
19822         var w = this.width;
19823         if(w){
19824             el.setWidth(w);
19825         }else if(Roo.isIE){
19826             el.setWidth(this.minWidth);
19827             var t = el.dom.offsetWidth; // force recalc
19828             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19829         }
19830     },
19831
19832     // private
19833     delayAutoWidth : function(){
19834         if(this.rendered){
19835             if(!this.awTask){
19836                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19837             }
19838             this.awTask.delay(20);
19839         }
19840     },
19841
19842     // private
19843     findTargetItem : function(e){
19844         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19845         if(t && t.menuItemId){
19846             return this.items.get(t.menuItemId);
19847         }
19848     },
19849
19850     // private
19851     onClick : function(e){
19852         var t;
19853         if(t = this.findTargetItem(e)){
19854             t.onClick(e);
19855             this.fireEvent("click", this, t, e);
19856         }
19857     },
19858
19859     // private
19860     setActiveItem : function(item, autoExpand){
19861         if(item != this.activeItem){
19862             if(this.activeItem){
19863                 this.activeItem.deactivate();
19864             }
19865             this.activeItem = item;
19866             item.activate(autoExpand);
19867         }else if(autoExpand){
19868             item.expandMenu();
19869         }
19870     },
19871
19872     // private
19873     tryActivate : function(start, step){
19874         var items = this.items;
19875         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19876             var item = items.get(i);
19877             if(!item.disabled && item.canActivate){
19878                 this.setActiveItem(item, false);
19879                 return item;
19880             }
19881         }
19882         return false;
19883     },
19884
19885     // private
19886     onMouseOver : function(e){
19887         var t;
19888         if(t = this.findTargetItem(e)){
19889             if(t.canActivate && !t.disabled){
19890                 this.setActiveItem(t, true);
19891             }
19892         }
19893         this.fireEvent("mouseover", this, e, t);
19894     },
19895
19896     // private
19897     onMouseOut : function(e){
19898         var t;
19899         if(t = this.findTargetItem(e)){
19900             if(t == this.activeItem && t.shouldDeactivate(e)){
19901                 this.activeItem.deactivate();
19902                 delete this.activeItem;
19903             }
19904         }
19905         this.fireEvent("mouseout", this, e, t);
19906     },
19907
19908     /**
19909      * Read-only.  Returns true if the menu is currently displayed, else false.
19910      * @type Boolean
19911      */
19912     isVisible : function(){
19913         return this.el && !this.hidden;
19914     },
19915
19916     /**
19917      * Displays this menu relative to another element
19918      * @param {String/HTMLElement/Roo.Element} element The element to align to
19919      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19920      * the element (defaults to this.defaultAlign)
19921      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19922      */
19923     show : function(el, pos, parentMenu){
19924         this.parentMenu = parentMenu;
19925         if(!this.el){
19926             this.render();
19927         }
19928         this.fireEvent("beforeshow", this);
19929         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19930     },
19931
19932     /**
19933      * Displays this menu at a specific xy position
19934      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19935      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19936      */
19937     showAt : function(xy, parentMenu, /* private: */_e){
19938         this.parentMenu = parentMenu;
19939         if(!this.el){
19940             this.render();
19941         }
19942         if(_e !== false){
19943             this.fireEvent("beforeshow", this);
19944             xy = this.el.adjustForConstraints(xy);
19945         }
19946         this.el.setXY(xy);
19947         this.el.show();
19948         this.hidden = false;
19949         this.focus();
19950         this.fireEvent("show", this);
19951     },
19952
19953     focus : function(){
19954         if(!this.hidden){
19955             this.doFocus.defer(50, this);
19956         }
19957     },
19958
19959     doFocus : function(){
19960         if(!this.hidden){
19961             this.focusEl.focus();
19962         }
19963     },
19964
19965     /**
19966      * Hides this menu and optionally all parent menus
19967      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19968      */
19969     hide : function(deep){
19970         if(this.el && this.isVisible()){
19971             this.fireEvent("beforehide", this);
19972             if(this.activeItem){
19973                 this.activeItem.deactivate();
19974                 this.activeItem = null;
19975             }
19976             this.el.hide();
19977             this.hidden = true;
19978             this.fireEvent("hide", this);
19979         }
19980         if(deep === true && this.parentMenu){
19981             this.parentMenu.hide(true);
19982         }
19983     },
19984
19985     /**
19986      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19987      * Any of the following are valid:
19988      * <ul>
19989      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19990      * <li>An HTMLElement object which will be converted to a menu item</li>
19991      * <li>A menu item config object that will be created as a new menu item</li>
19992      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19993      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19994      * </ul>
19995      * Usage:
19996      * <pre><code>
19997 // Create the menu
19998 var menu = new Roo.menu.Menu();
19999
20000 // Create a menu item to add by reference
20001 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20002
20003 // Add a bunch of items at once using different methods.
20004 // Only the last item added will be returned.
20005 var item = menu.add(
20006     menuItem,                // add existing item by ref
20007     'Dynamic Item',          // new TextItem
20008     '-',                     // new separator
20009     { text: 'Config Item' }  // new item by config
20010 );
20011 </code></pre>
20012      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20013      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20014      */
20015     add : function(){
20016         var a = arguments, l = a.length, item;
20017         for(var i = 0; i < l; i++){
20018             var el = a[i];
20019             if ((typeof(el) == "object") && el.xtype && el.xns) {
20020                 el = Roo.factory(el, Roo.menu);
20021             }
20022             
20023             if(el.render){ // some kind of Item
20024                 item = this.addItem(el);
20025             }else if(typeof el == "string"){ // string
20026                 if(el == "separator" || el == "-"){
20027                     item = this.addSeparator();
20028                 }else{
20029                     item = this.addText(el);
20030                 }
20031             }else if(el.tagName || el.el){ // element
20032                 item = this.addElement(el);
20033             }else if(typeof el == "object"){ // must be menu item config?
20034                 item = this.addMenuItem(el);
20035             }
20036         }
20037         return item;
20038     },
20039
20040     /**
20041      * Returns this menu's underlying {@link Roo.Element} object
20042      * @return {Roo.Element} The element
20043      */
20044     getEl : function(){
20045         if(!this.el){
20046             this.render();
20047         }
20048         return this.el;
20049     },
20050
20051     /**
20052      * Adds a separator bar to the menu
20053      * @return {Roo.menu.Item} The menu item that was added
20054      */
20055     addSeparator : function(){
20056         return this.addItem(new Roo.menu.Separator());
20057     },
20058
20059     /**
20060      * Adds an {@link Roo.Element} object to the menu
20061      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20062      * @return {Roo.menu.Item} The menu item that was added
20063      */
20064     addElement : function(el){
20065         return this.addItem(new Roo.menu.BaseItem(el));
20066     },
20067
20068     /**
20069      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20070      * @param {Roo.menu.Item} item The menu item to add
20071      * @return {Roo.menu.Item} The menu item that was added
20072      */
20073     addItem : function(item){
20074         this.items.add(item);
20075         if(this.ul){
20076             var li = document.createElement("li");
20077             li.className = "x-menu-list-item";
20078             this.ul.dom.appendChild(li);
20079             item.render(li, this);
20080             this.delayAutoWidth();
20081         }
20082         return item;
20083     },
20084
20085     /**
20086      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20087      * @param {Object} config A MenuItem config object
20088      * @return {Roo.menu.Item} The menu item that was added
20089      */
20090     addMenuItem : function(config){
20091         if(!(config instanceof Roo.menu.Item)){
20092             if(typeof config.checked == "boolean"){ // must be check menu item config?
20093                 config = new Roo.menu.CheckItem(config);
20094             }else{
20095                 config = new Roo.menu.Item(config);
20096             }
20097         }
20098         return this.addItem(config);
20099     },
20100
20101     /**
20102      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20103      * @param {String} text The text to display in the menu item
20104      * @return {Roo.menu.Item} The menu item that was added
20105      */
20106     addText : function(text){
20107         return this.addItem(new Roo.menu.TextItem({ text : text }));
20108     },
20109
20110     /**
20111      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20112      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20113      * @param {Roo.menu.Item} item The menu item to add
20114      * @return {Roo.menu.Item} The menu item that was added
20115      */
20116     insert : function(index, item){
20117         this.items.insert(index, item);
20118         if(this.ul){
20119             var li = document.createElement("li");
20120             li.className = "x-menu-list-item";
20121             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20122             item.render(li, this);
20123             this.delayAutoWidth();
20124         }
20125         return item;
20126     },
20127
20128     /**
20129      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20130      * @param {Roo.menu.Item} item The menu item to remove
20131      */
20132     remove : function(item){
20133         this.items.removeKey(item.id);
20134         item.destroy();
20135     },
20136
20137     /**
20138      * Removes and destroys all items in the menu
20139      */
20140     removeAll : function(){
20141         var f;
20142         while(f = this.items.first()){
20143             this.remove(f);
20144         }
20145     }
20146 });
20147
20148 // MenuNav is a private utility class used internally by the Menu
20149 Roo.menu.MenuNav = function(menu){
20150     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20151     this.scope = this.menu = menu;
20152 };
20153
20154 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20155     doRelay : function(e, h){
20156         var k = e.getKey();
20157         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20158             this.menu.tryActivate(0, 1);
20159             return false;
20160         }
20161         return h.call(this.scope || this, e, this.menu);
20162     },
20163
20164     up : function(e, m){
20165         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20166             m.tryActivate(m.items.length-1, -1);
20167         }
20168     },
20169
20170     down : function(e, m){
20171         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20172             m.tryActivate(0, 1);
20173         }
20174     },
20175
20176     right : function(e, m){
20177         if(m.activeItem){
20178             m.activeItem.expandMenu(true);
20179         }
20180     },
20181
20182     left : function(e, m){
20183         m.hide();
20184         if(m.parentMenu && m.parentMenu.activeItem){
20185             m.parentMenu.activeItem.activate();
20186         }
20187     },
20188
20189     enter : function(e, m){
20190         if(m.activeItem){
20191             e.stopPropagation();
20192             m.activeItem.onClick(e);
20193             m.fireEvent("click", this, m.activeItem);
20194             return true;
20195         }
20196     }
20197 });/*
20198  * Based on:
20199  * Ext JS Library 1.1.1
20200  * Copyright(c) 2006-2007, Ext JS, LLC.
20201  *
20202  * Originally Released Under LGPL - original licence link has changed is not relivant.
20203  *
20204  * Fork - LGPL
20205  * <script type="text/javascript">
20206  */
20207  
20208 /**
20209  * @class Roo.menu.MenuMgr
20210  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20211  * @singleton
20212  */
20213 Roo.menu.MenuMgr = function(){
20214    var menus, active, groups = {}, attached = false, lastShow = new Date();
20215
20216    // private - called when first menu is created
20217    function init(){
20218        menus = {};
20219        active = new Roo.util.MixedCollection();
20220        Roo.get(document).addKeyListener(27, function(){
20221            if(active.length > 0){
20222                hideAll();
20223            }
20224        });
20225    }
20226
20227    // private
20228    function hideAll(){
20229        if(active && active.length > 0){
20230            var c = active.clone();
20231            c.each(function(m){
20232                m.hide();
20233            });
20234        }
20235    }
20236
20237    // private
20238    function onHide(m){
20239        active.remove(m);
20240        if(active.length < 1){
20241            Roo.get(document).un("mousedown", onMouseDown);
20242            attached = false;
20243        }
20244    }
20245
20246    // private
20247    function onShow(m){
20248        var last = active.last();
20249        lastShow = new Date();
20250        active.add(m);
20251        if(!attached){
20252            Roo.get(document).on("mousedown", onMouseDown);
20253            attached = true;
20254        }
20255        if(m.parentMenu){
20256           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20257           m.parentMenu.activeChild = m;
20258        }else if(last && last.isVisible()){
20259           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20260        }
20261    }
20262
20263    // private
20264    function onBeforeHide(m){
20265        if(m.activeChild){
20266            m.activeChild.hide();
20267        }
20268        if(m.autoHideTimer){
20269            clearTimeout(m.autoHideTimer);
20270            delete m.autoHideTimer;
20271        }
20272    }
20273
20274    // private
20275    function onBeforeShow(m){
20276        var pm = m.parentMenu;
20277        if(!pm && !m.allowOtherMenus){
20278            hideAll();
20279        }else if(pm && pm.activeChild && active != m){
20280            pm.activeChild.hide();
20281        }
20282    }
20283
20284    // private
20285    function onMouseDown(e){
20286        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20287            hideAll();
20288        }
20289    }
20290
20291    // private
20292    function onBeforeCheck(mi, state){
20293        if(state){
20294            var g = groups[mi.group];
20295            for(var i = 0, l = g.length; i < l; i++){
20296                if(g[i] != mi){
20297                    g[i].setChecked(false);
20298                }
20299            }
20300        }
20301    }
20302
20303    return {
20304
20305        /**
20306         * Hides all menus that are currently visible
20307         */
20308        hideAll : function(){
20309             hideAll();  
20310        },
20311
20312        // private
20313        register : function(menu){
20314            if(!menus){
20315                init();
20316            }
20317            menus[menu.id] = menu;
20318            menu.on("beforehide", onBeforeHide);
20319            menu.on("hide", onHide);
20320            menu.on("beforeshow", onBeforeShow);
20321            menu.on("show", onShow);
20322            var g = menu.group;
20323            if(g && menu.events["checkchange"]){
20324                if(!groups[g]){
20325                    groups[g] = [];
20326                }
20327                groups[g].push(menu);
20328                menu.on("checkchange", onCheck);
20329            }
20330        },
20331
20332         /**
20333          * Returns a {@link Roo.menu.Menu} object
20334          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20335          * be used to generate and return a new Menu instance.
20336          */
20337        get : function(menu){
20338            if(typeof menu == "string"){ // menu id
20339                return menus[menu];
20340            }else if(menu.events){  // menu instance
20341                return menu;
20342            }else if(typeof menu.length == 'number'){ // array of menu items?
20343                return new Roo.menu.Menu({items:menu});
20344            }else{ // otherwise, must be a config
20345                return new Roo.menu.Menu(menu);
20346            }
20347        },
20348
20349        // private
20350        unregister : function(menu){
20351            delete menus[menu.id];
20352            menu.un("beforehide", onBeforeHide);
20353            menu.un("hide", onHide);
20354            menu.un("beforeshow", onBeforeShow);
20355            menu.un("show", onShow);
20356            var g = menu.group;
20357            if(g && menu.events["checkchange"]){
20358                groups[g].remove(menu);
20359                menu.un("checkchange", onCheck);
20360            }
20361        },
20362
20363        // private
20364        registerCheckable : function(menuItem){
20365            var g = menuItem.group;
20366            if(g){
20367                if(!groups[g]){
20368                    groups[g] = [];
20369                }
20370                groups[g].push(menuItem);
20371                menuItem.on("beforecheckchange", onBeforeCheck);
20372            }
20373        },
20374
20375        // private
20376        unregisterCheckable : function(menuItem){
20377            var g = menuItem.group;
20378            if(g){
20379                groups[g].remove(menuItem);
20380                menuItem.un("beforecheckchange", onBeforeCheck);
20381            }
20382        }
20383    };
20384 }();/*
20385  * Based on:
20386  * Ext JS Library 1.1.1
20387  * Copyright(c) 2006-2007, Ext JS, LLC.
20388  *
20389  * Originally Released Under LGPL - original licence link has changed is not relivant.
20390  *
20391  * Fork - LGPL
20392  * <script type="text/javascript">
20393  */
20394  
20395
20396 /**
20397  * @class Roo.menu.BaseItem
20398  * @extends Roo.Component
20399  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20400  * management and base configuration options shared by all menu components.
20401  * @constructor
20402  * Creates a new BaseItem
20403  * @param {Object} config Configuration options
20404  */
20405 Roo.menu.BaseItem = function(config){
20406     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20407
20408     this.addEvents({
20409         /**
20410          * @event click
20411          * Fires when this item is clicked
20412          * @param {Roo.menu.BaseItem} this
20413          * @param {Roo.EventObject} e
20414          */
20415         click: true,
20416         /**
20417          * @event activate
20418          * Fires when this item is activated
20419          * @param {Roo.menu.BaseItem} this
20420          */
20421         activate : true,
20422         /**
20423          * @event deactivate
20424          * Fires when this item is deactivated
20425          * @param {Roo.menu.BaseItem} this
20426          */
20427         deactivate : true
20428     });
20429
20430     if(this.handler){
20431         this.on("click", this.handler, this.scope, true);
20432     }
20433 };
20434
20435 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20436     /**
20437      * @cfg {Function} handler
20438      * A function that will handle the click event of this menu item (defaults to undefined)
20439      */
20440     /**
20441      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20442      */
20443     canActivate : false,
20444     /**
20445      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20446      */
20447     activeClass : "x-menu-item-active",
20448     /**
20449      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20450      */
20451     hideOnClick : true,
20452     /**
20453      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20454      */
20455     hideDelay : 100,
20456
20457     // private
20458     ctype: "Roo.menu.BaseItem",
20459
20460     // private
20461     actionMode : "container",
20462
20463     // private
20464     render : function(container, parentMenu){
20465         this.parentMenu = parentMenu;
20466         Roo.menu.BaseItem.superclass.render.call(this, container);
20467         this.container.menuItemId = this.id;
20468     },
20469
20470     // private
20471     onRender : function(container, position){
20472         this.el = Roo.get(this.el);
20473         container.dom.appendChild(this.el.dom);
20474     },
20475
20476     // private
20477     onClick : function(e){
20478         if(!this.disabled && this.fireEvent("click", this, e) !== false
20479                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20480             this.handleClick(e);
20481         }else{
20482             e.stopEvent();
20483         }
20484     },
20485
20486     // private
20487     activate : function(){
20488         if(this.disabled){
20489             return false;
20490         }
20491         var li = this.container;
20492         li.addClass(this.activeClass);
20493         this.region = li.getRegion().adjust(2, 2, -2, -2);
20494         this.fireEvent("activate", this);
20495         return true;
20496     },
20497
20498     // private
20499     deactivate : function(){
20500         this.container.removeClass(this.activeClass);
20501         this.fireEvent("deactivate", this);
20502     },
20503
20504     // private
20505     shouldDeactivate : function(e){
20506         return !this.region || !this.region.contains(e.getPoint());
20507     },
20508
20509     // private
20510     handleClick : function(e){
20511         if(this.hideOnClick){
20512             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20513         }
20514     },
20515
20516     // private
20517     expandMenu : function(autoActivate){
20518         // do nothing
20519     },
20520
20521     // private
20522     hideMenu : function(){
20523         // do nothing
20524     }
20525 });/*
20526  * Based on:
20527  * Ext JS Library 1.1.1
20528  * Copyright(c) 2006-2007, Ext JS, LLC.
20529  *
20530  * Originally Released Under LGPL - original licence link has changed is not relivant.
20531  *
20532  * Fork - LGPL
20533  * <script type="text/javascript">
20534  */
20535  
20536 /**
20537  * @class Roo.menu.Adapter
20538  * @extends Roo.menu.BaseItem
20539  * 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.
20540  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20541  * @constructor
20542  * Creates a new Adapter
20543  * @param {Object} config Configuration options
20544  */
20545 Roo.menu.Adapter = function(component, config){
20546     Roo.menu.Adapter.superclass.constructor.call(this, config);
20547     this.component = component;
20548 };
20549 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20550     // private
20551     canActivate : true,
20552
20553     // private
20554     onRender : function(container, position){
20555         this.component.render(container);
20556         this.el = this.component.getEl();
20557     },
20558
20559     // private
20560     activate : function(){
20561         if(this.disabled){
20562             return false;
20563         }
20564         this.component.focus();
20565         this.fireEvent("activate", this);
20566         return true;
20567     },
20568
20569     // private
20570     deactivate : function(){
20571         this.fireEvent("deactivate", this);
20572     },
20573
20574     // private
20575     disable : function(){
20576         this.component.disable();
20577         Roo.menu.Adapter.superclass.disable.call(this);
20578     },
20579
20580     // private
20581     enable : function(){
20582         this.component.enable();
20583         Roo.menu.Adapter.superclass.enable.call(this);
20584     }
20585 });/*
20586  * Based on:
20587  * Ext JS Library 1.1.1
20588  * Copyright(c) 2006-2007, Ext JS, LLC.
20589  *
20590  * Originally Released Under LGPL - original licence link has changed is not relivant.
20591  *
20592  * Fork - LGPL
20593  * <script type="text/javascript">
20594  */
20595
20596 /**
20597  * @class Roo.menu.TextItem
20598  * @extends Roo.menu.BaseItem
20599  * Adds a static text string to a menu, usually used as either a heading or group separator.
20600  * Note: old style constructor with text is still supported.
20601  * 
20602  * @constructor
20603  * Creates a new TextItem
20604  * @param {Object} cfg Configuration
20605  */
20606 Roo.menu.TextItem = function(cfg){
20607     if (typeof(cfg) == 'string') {
20608         this.text = cfg;
20609     } else {
20610         Roo.apply(this,cfg);
20611     }
20612     
20613     Roo.menu.TextItem.superclass.constructor.call(this);
20614 };
20615
20616 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20617     /**
20618      * @cfg {Boolean} text Text to show on item.
20619      */
20620     text : '',
20621     
20622     /**
20623      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20624      */
20625     hideOnClick : false,
20626     /**
20627      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20628      */
20629     itemCls : "x-menu-text",
20630
20631     // private
20632     onRender : function(){
20633         var s = document.createElement("span");
20634         s.className = this.itemCls;
20635         s.innerHTML = this.text;
20636         this.el = s;
20637         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20638     }
20639 });/*
20640  * Based on:
20641  * Ext JS Library 1.1.1
20642  * Copyright(c) 2006-2007, Ext JS, LLC.
20643  *
20644  * Originally Released Under LGPL - original licence link has changed is not relivant.
20645  *
20646  * Fork - LGPL
20647  * <script type="text/javascript">
20648  */
20649
20650 /**
20651  * @class Roo.menu.Separator
20652  * @extends Roo.menu.BaseItem
20653  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20654  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20655  * @constructor
20656  * @param {Object} config Configuration options
20657  */
20658 Roo.menu.Separator = function(config){
20659     Roo.menu.Separator.superclass.constructor.call(this, config);
20660 };
20661
20662 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20663     /**
20664      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20665      */
20666     itemCls : "x-menu-sep",
20667     /**
20668      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20669      */
20670     hideOnClick : false,
20671
20672     // private
20673     onRender : function(li){
20674         var s = document.createElement("span");
20675         s.className = this.itemCls;
20676         s.innerHTML = "&#160;";
20677         this.el = s;
20678         li.addClass("x-menu-sep-li");
20679         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20680     }
20681 });/*
20682  * Based on:
20683  * Ext JS Library 1.1.1
20684  * Copyright(c) 2006-2007, Ext JS, LLC.
20685  *
20686  * Originally Released Under LGPL - original licence link has changed is not relivant.
20687  *
20688  * Fork - LGPL
20689  * <script type="text/javascript">
20690  */
20691 /**
20692  * @class Roo.menu.Item
20693  * @extends Roo.menu.BaseItem
20694  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20695  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20696  * activation and click handling.
20697  * @constructor
20698  * Creates a new Item
20699  * @param {Object} config Configuration options
20700  */
20701 Roo.menu.Item = function(config){
20702     Roo.menu.Item.superclass.constructor.call(this, config);
20703     if(this.menu){
20704         this.menu = Roo.menu.MenuMgr.get(this.menu);
20705     }
20706 };
20707 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20708     
20709     /**
20710      * @cfg {String} text
20711      * The text to show on the menu item.
20712      */
20713     text: '',
20714      /**
20715      * @cfg {String} HTML to render in menu
20716      * The text to show on the menu item (HTML version).
20717      */
20718     html: '',
20719     /**
20720      * @cfg {String} icon
20721      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20722      */
20723     icon: undefined,
20724     /**
20725      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20726      */
20727     itemCls : "x-menu-item",
20728     /**
20729      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20730      */
20731     canActivate : true,
20732     /**
20733      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20734      */
20735     showDelay: 200,
20736     // doc'd in BaseItem
20737     hideDelay: 200,
20738
20739     // private
20740     ctype: "Roo.menu.Item",
20741     
20742     // private
20743     onRender : function(container, position){
20744         var el = document.createElement("a");
20745         el.hideFocus = true;
20746         el.unselectable = "on";
20747         el.href = this.href || "#";
20748         if(this.hrefTarget){
20749             el.target = this.hrefTarget;
20750         }
20751         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20752         
20753         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20754         
20755         el.innerHTML = String.format(
20756                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20757                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20758         this.el = el;
20759         Roo.menu.Item.superclass.onRender.call(this, container, position);
20760     },
20761
20762     /**
20763      * Sets the text to display in this menu item
20764      * @param {String} text The text to display
20765      * @param {Boolean} isHTML true to indicate text is pure html.
20766      */
20767     setText : function(text, isHTML){
20768         if (isHTML) {
20769             this.html = text;
20770         } else {
20771             this.text = text;
20772             this.html = '';
20773         }
20774         if(this.rendered){
20775             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20776      
20777             this.el.update(String.format(
20778                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20779                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20780             this.parentMenu.autoWidth();
20781         }
20782     },
20783
20784     // private
20785     handleClick : function(e){
20786         if(!this.href){ // if no link defined, stop the event automatically
20787             e.stopEvent();
20788         }
20789         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20790     },
20791
20792     // private
20793     activate : function(autoExpand){
20794         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20795             this.focus();
20796             if(autoExpand){
20797                 this.expandMenu();
20798             }
20799         }
20800         return true;
20801     },
20802
20803     // private
20804     shouldDeactivate : function(e){
20805         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20806             if(this.menu && this.menu.isVisible()){
20807                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20808             }
20809             return true;
20810         }
20811         return false;
20812     },
20813
20814     // private
20815     deactivate : function(){
20816         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20817         this.hideMenu();
20818     },
20819
20820     // private
20821     expandMenu : function(autoActivate){
20822         if(!this.disabled && this.menu){
20823             clearTimeout(this.hideTimer);
20824             delete this.hideTimer;
20825             if(!this.menu.isVisible() && !this.showTimer){
20826                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20827             }else if (this.menu.isVisible() && autoActivate){
20828                 this.menu.tryActivate(0, 1);
20829             }
20830         }
20831     },
20832
20833     // private
20834     deferExpand : function(autoActivate){
20835         delete this.showTimer;
20836         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20837         if(autoActivate){
20838             this.menu.tryActivate(0, 1);
20839         }
20840     },
20841
20842     // private
20843     hideMenu : function(){
20844         clearTimeout(this.showTimer);
20845         delete this.showTimer;
20846         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20847             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20848         }
20849     },
20850
20851     // private
20852     deferHide : function(){
20853         delete this.hideTimer;
20854         this.menu.hide();
20855     }
20856 });/*
20857  * Based on:
20858  * Ext JS Library 1.1.1
20859  * Copyright(c) 2006-2007, Ext JS, LLC.
20860  *
20861  * Originally Released Under LGPL - original licence link has changed is not relivant.
20862  *
20863  * Fork - LGPL
20864  * <script type="text/javascript">
20865  */
20866  
20867 /**
20868  * @class Roo.menu.CheckItem
20869  * @extends Roo.menu.Item
20870  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20871  * @constructor
20872  * Creates a new CheckItem
20873  * @param {Object} config Configuration options
20874  */
20875 Roo.menu.CheckItem = function(config){
20876     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20877     this.addEvents({
20878         /**
20879          * @event beforecheckchange
20880          * Fires before the checked value is set, providing an opportunity to cancel if needed
20881          * @param {Roo.menu.CheckItem} this
20882          * @param {Boolean} checked The new checked value that will be set
20883          */
20884         "beforecheckchange" : true,
20885         /**
20886          * @event checkchange
20887          * Fires after the checked value has been set
20888          * @param {Roo.menu.CheckItem} this
20889          * @param {Boolean} checked The checked value that was set
20890          */
20891         "checkchange" : true
20892     });
20893     if(this.checkHandler){
20894         this.on('checkchange', this.checkHandler, this.scope);
20895     }
20896 };
20897 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20898     /**
20899      * @cfg {String} group
20900      * All check items with the same group name will automatically be grouped into a single-select
20901      * radio button group (defaults to '')
20902      */
20903     /**
20904      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20905      */
20906     itemCls : "x-menu-item x-menu-check-item",
20907     /**
20908      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20909      */
20910     groupClass : "x-menu-group-item",
20911
20912     /**
20913      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20914      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20915      * initialized with checked = true will be rendered as checked.
20916      */
20917     checked: false,
20918
20919     // private
20920     ctype: "Roo.menu.CheckItem",
20921
20922     // private
20923     onRender : function(c){
20924         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20925         if(this.group){
20926             this.el.addClass(this.groupClass);
20927         }
20928         Roo.menu.MenuMgr.registerCheckable(this);
20929         if(this.checked){
20930             this.checked = false;
20931             this.setChecked(true, true);
20932         }
20933     },
20934
20935     // private
20936     destroy : function(){
20937         if(this.rendered){
20938             Roo.menu.MenuMgr.unregisterCheckable(this);
20939         }
20940         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20941     },
20942
20943     /**
20944      * Set the checked state of this item
20945      * @param {Boolean} checked The new checked value
20946      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20947      */
20948     setChecked : function(state, suppressEvent){
20949         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20950             if(this.container){
20951                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20952             }
20953             this.checked = state;
20954             if(suppressEvent !== true){
20955                 this.fireEvent("checkchange", this, state);
20956             }
20957         }
20958     },
20959
20960     // private
20961     handleClick : function(e){
20962        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20963            this.setChecked(!this.checked);
20964        }
20965        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20966     }
20967 });/*
20968  * Based on:
20969  * Ext JS Library 1.1.1
20970  * Copyright(c) 2006-2007, Ext JS, LLC.
20971  *
20972  * Originally Released Under LGPL - original licence link has changed is not relivant.
20973  *
20974  * Fork - LGPL
20975  * <script type="text/javascript">
20976  */
20977  
20978 /**
20979  * @class Roo.menu.DateItem
20980  * @extends Roo.menu.Adapter
20981  * A menu item that wraps the {@link Roo.DatPicker} component.
20982  * @constructor
20983  * Creates a new DateItem
20984  * @param {Object} config Configuration options
20985  */
20986 Roo.menu.DateItem = function(config){
20987     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20988     /** The Roo.DatePicker object @type Roo.DatePicker */
20989     this.picker = this.component;
20990     this.addEvents({select: true});
20991     
20992     this.picker.on("render", function(picker){
20993         picker.getEl().swallowEvent("click");
20994         picker.container.addClass("x-menu-date-item");
20995     });
20996
20997     this.picker.on("select", this.onSelect, this);
20998 };
20999
21000 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21001     // private
21002     onSelect : function(picker, date){
21003         this.fireEvent("select", this, date, picker);
21004         Roo.menu.DateItem.superclass.handleClick.call(this);
21005     }
21006 });/*
21007  * Based on:
21008  * Ext JS Library 1.1.1
21009  * Copyright(c) 2006-2007, Ext JS, LLC.
21010  *
21011  * Originally Released Under LGPL - original licence link has changed is not relivant.
21012  *
21013  * Fork - LGPL
21014  * <script type="text/javascript">
21015  */
21016  
21017 /**
21018  * @class Roo.menu.ColorItem
21019  * @extends Roo.menu.Adapter
21020  * A menu item that wraps the {@link Roo.ColorPalette} component.
21021  * @constructor
21022  * Creates a new ColorItem
21023  * @param {Object} config Configuration options
21024  */
21025 Roo.menu.ColorItem = function(config){
21026     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21027     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21028     this.palette = this.component;
21029     this.relayEvents(this.palette, ["select"]);
21030     if(this.selectHandler){
21031         this.on('select', this.selectHandler, this.scope);
21032     }
21033 };
21034 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21035  * Based on:
21036  * Ext JS Library 1.1.1
21037  * Copyright(c) 2006-2007, Ext JS, LLC.
21038  *
21039  * Originally Released Under LGPL - original licence link has changed is not relivant.
21040  *
21041  * Fork - LGPL
21042  * <script type="text/javascript">
21043  */
21044  
21045
21046 /**
21047  * @class Roo.menu.DateMenu
21048  * @extends Roo.menu.Menu
21049  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21050  * @constructor
21051  * Creates a new DateMenu
21052  * @param {Object} config Configuration options
21053  */
21054 Roo.menu.DateMenu = function(config){
21055     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21056     this.plain = true;
21057     var di = new Roo.menu.DateItem(config);
21058     this.add(di);
21059     /**
21060      * The {@link Roo.DatePicker} instance for this DateMenu
21061      * @type DatePicker
21062      */
21063     this.picker = di.picker;
21064     /**
21065      * @event select
21066      * @param {DatePicker} picker
21067      * @param {Date} date
21068      */
21069     this.relayEvents(di, ["select"]);
21070     this.on('beforeshow', function(){
21071         if(this.picker){
21072             this.picker.hideMonthPicker(false);
21073         }
21074     }, this);
21075 };
21076 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21077     cls:'x-date-menu'
21078 });/*
21079  * Based on:
21080  * Ext JS Library 1.1.1
21081  * Copyright(c) 2006-2007, Ext JS, LLC.
21082  *
21083  * Originally Released Under LGPL - original licence link has changed is not relivant.
21084  *
21085  * Fork - LGPL
21086  * <script type="text/javascript">
21087  */
21088  
21089
21090 /**
21091  * @class Roo.menu.ColorMenu
21092  * @extends Roo.menu.Menu
21093  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21094  * @constructor
21095  * Creates a new ColorMenu
21096  * @param {Object} config Configuration options
21097  */
21098 Roo.menu.ColorMenu = function(config){
21099     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21100     this.plain = true;
21101     var ci = new Roo.menu.ColorItem(config);
21102     this.add(ci);
21103     /**
21104      * The {@link Roo.ColorPalette} instance for this ColorMenu
21105      * @type ColorPalette
21106      */
21107     this.palette = ci.palette;
21108     /**
21109      * @event select
21110      * @param {ColorPalette} palette
21111      * @param {String} color
21112      */
21113     this.relayEvents(ci, ["select"]);
21114 };
21115 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21116  * Based on:
21117  * Ext JS Library 1.1.1
21118  * Copyright(c) 2006-2007, Ext JS, LLC.
21119  *
21120  * Originally Released Under LGPL - original licence link has changed is not relivant.
21121  *
21122  * Fork - LGPL
21123  * <script type="text/javascript">
21124  */
21125  
21126 /**
21127  * @class Roo.form.Field
21128  * @extends Roo.BoxComponent
21129  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21130  * @constructor
21131  * Creates a new Field
21132  * @param {Object} config Configuration options
21133  */
21134 Roo.form.Field = function(config){
21135     Roo.form.Field.superclass.constructor.call(this, config);
21136 };
21137
21138 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21139     /**
21140      * @cfg {String} fieldLabel Label to use when rendering a form.
21141      */
21142        /**
21143      * @cfg {String} qtip Mouse over tip
21144      */
21145      
21146     /**
21147      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21148      */
21149     invalidClass : "x-form-invalid",
21150     /**
21151      * @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")
21152      */
21153     invalidText : "The value in this field is invalid",
21154     /**
21155      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21156      */
21157     focusClass : "x-form-focus",
21158     /**
21159      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21160       automatic validation (defaults to "keyup").
21161      */
21162     validationEvent : "keyup",
21163     /**
21164      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21165      */
21166     validateOnBlur : true,
21167     /**
21168      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21169      */
21170     validationDelay : 250,
21171     /**
21172      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21173      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21174      */
21175     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21176     /**
21177      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21178      */
21179     fieldClass : "x-form-field",
21180     /**
21181      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21182      *<pre>
21183 Value         Description
21184 -----------   ----------------------------------------------------------------------
21185 qtip          Display a quick tip when the user hovers over the field
21186 title         Display a default browser title attribute popup
21187 under         Add a block div beneath the field containing the error text
21188 side          Add an error icon to the right of the field with a popup on hover
21189 [element id]  Add the error text directly to the innerHTML of the specified element
21190 </pre>
21191      */
21192     msgTarget : 'qtip',
21193     /**
21194      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21195      */
21196     msgFx : 'normal',
21197
21198     /**
21199      * @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.
21200      */
21201     readOnly : false,
21202
21203     /**
21204      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21205      */
21206     disabled : false,
21207
21208     /**
21209      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21210      */
21211     inputType : undefined,
21212     
21213     /**
21214      * @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).
21215          */
21216         tabIndex : undefined,
21217         
21218     // private
21219     isFormField : true,
21220
21221     // private
21222     hasFocus : false,
21223     /**
21224      * @property {Roo.Element} fieldEl
21225      * Element Containing the rendered Field (with label etc.)
21226      */
21227     /**
21228      * @cfg {Mixed} value A value to initialize this field with.
21229      */
21230     value : undefined,
21231
21232     /**
21233      * @cfg {String} name The field's HTML name attribute.
21234      */
21235     /**
21236      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21237      */
21238
21239         // private ??
21240         initComponent : function(){
21241         Roo.form.Field.superclass.initComponent.call(this);
21242         this.addEvents({
21243             /**
21244              * @event focus
21245              * Fires when this field receives input focus.
21246              * @param {Roo.form.Field} this
21247              */
21248             focus : true,
21249             /**
21250              * @event blur
21251              * Fires when this field loses input focus.
21252              * @param {Roo.form.Field} this
21253              */
21254             blur : true,
21255             /**
21256              * @event specialkey
21257              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21258              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21259              * @param {Roo.form.Field} this
21260              * @param {Roo.EventObject} e The event object
21261              */
21262             specialkey : true,
21263             /**
21264              * @event change
21265              * Fires just before the field blurs if the field value has changed.
21266              * @param {Roo.form.Field} this
21267              * @param {Mixed} newValue The new value
21268              * @param {Mixed} oldValue The original value
21269              */
21270             change : true,
21271             /**
21272              * @event invalid
21273              * Fires after the field has been marked as invalid.
21274              * @param {Roo.form.Field} this
21275              * @param {String} msg The validation message
21276              */
21277             invalid : true,
21278             /**
21279              * @event valid
21280              * Fires after the field has been validated with no errors.
21281              * @param {Roo.form.Field} this
21282              */
21283             valid : true,
21284              /**
21285              * @event keyup
21286              * Fires after the key up
21287              * @param {Roo.form.Field} this
21288              * @param {Roo.EventObject}  e The event Object
21289              */
21290             keyup : true
21291         });
21292     },
21293
21294     /**
21295      * Returns the name attribute of the field if available
21296      * @return {String} name The field name
21297      */
21298     getName: function(){
21299          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21300     },
21301
21302     // private
21303     onRender : function(ct, position){
21304         Roo.form.Field.superclass.onRender.call(this, ct, position);
21305         if(!this.el){
21306             var cfg = this.getAutoCreate();
21307             if(!cfg.name){
21308                 cfg.name = this.name || this.id;
21309             }
21310             if(this.inputType){
21311                 cfg.type = this.inputType;
21312             }
21313             this.el = ct.createChild(cfg, position);
21314         }
21315         var type = this.el.dom.type;
21316         if(type){
21317             if(type == 'password'){
21318                 type = 'text';
21319             }
21320             this.el.addClass('x-form-'+type);
21321         }
21322         if(this.readOnly){
21323             this.el.dom.readOnly = true;
21324         }
21325         if(this.tabIndex !== undefined){
21326             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21327         }
21328
21329         this.el.addClass([this.fieldClass, this.cls]);
21330         this.initValue();
21331     },
21332
21333     /**
21334      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21335      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21336      * @return {Roo.form.Field} this
21337      */
21338     applyTo : function(target){
21339         this.allowDomMove = false;
21340         this.el = Roo.get(target);
21341         this.render(this.el.dom.parentNode);
21342         return this;
21343     },
21344
21345     // private
21346     initValue : function(){
21347         if(this.value !== undefined){
21348             this.setValue(this.value);
21349         }else if(this.el.dom.value.length > 0){
21350             this.setValue(this.el.dom.value);
21351         }
21352     },
21353
21354     /**
21355      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21356      */
21357     isDirty : function() {
21358         if(this.disabled) {
21359             return false;
21360         }
21361         return String(this.getValue()) !== String(this.originalValue);
21362     },
21363
21364     // private
21365     afterRender : function(){
21366         Roo.form.Field.superclass.afterRender.call(this);
21367         this.initEvents();
21368     },
21369
21370     // private
21371     fireKey : function(e){
21372         //Roo.log('field ' + e.getKey());
21373         if(e.isNavKeyPress()){
21374             this.fireEvent("specialkey", this, e);
21375         }
21376     },
21377
21378     /**
21379      * Resets the current field value to the originally loaded value and clears any validation messages
21380      */
21381     reset : function(){
21382         this.setValue(this.originalValue);
21383         this.clearInvalid();
21384     },
21385
21386     // private
21387     initEvents : function(){
21388         // safari killled keypress - so keydown is now used..
21389         this.el.on("keydown" , this.fireKey,  this);
21390         this.el.on("focus", this.onFocus,  this);
21391         this.el.on("blur", this.onBlur,  this);
21392         this.el.relayEvent('keyup', this);
21393
21394         // reference to original value for reset
21395         this.originalValue = this.getValue();
21396     },
21397
21398     // private
21399     onFocus : function(){
21400         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21401             this.el.addClass(this.focusClass);
21402         }
21403         if(!this.hasFocus){
21404             this.hasFocus = true;
21405             this.startValue = this.getValue();
21406             this.fireEvent("focus", this);
21407         }
21408     },
21409
21410     beforeBlur : Roo.emptyFn,
21411
21412     // private
21413     onBlur : function(){
21414         this.beforeBlur();
21415         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21416             this.el.removeClass(this.focusClass);
21417         }
21418         this.hasFocus = false;
21419         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21420             this.validate();
21421         }
21422         var v = this.getValue();
21423         if(String(v) !== String(this.startValue)){
21424             this.fireEvent('change', this, v, this.startValue);
21425         }
21426         this.fireEvent("blur", this);
21427     },
21428
21429     /**
21430      * Returns whether or not the field value is currently valid
21431      * @param {Boolean} preventMark True to disable marking the field invalid
21432      * @return {Boolean} True if the value is valid, else false
21433      */
21434     isValid : function(preventMark){
21435         if(this.disabled){
21436             return true;
21437         }
21438         var restore = this.preventMark;
21439         this.preventMark = preventMark === true;
21440         var v = this.validateValue(this.processValue(this.getRawValue()));
21441         this.preventMark = restore;
21442         return v;
21443     },
21444
21445     /**
21446      * Validates the field value
21447      * @return {Boolean} True if the value is valid, else false
21448      */
21449     validate : function(){
21450         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21451             this.clearInvalid();
21452             return true;
21453         }
21454         return false;
21455     },
21456
21457     processValue : function(value){
21458         return value;
21459     },
21460
21461     // private
21462     // Subclasses should provide the validation implementation by overriding this
21463     validateValue : function(value){
21464         return true;
21465     },
21466
21467     /**
21468      * Mark this field as invalid
21469      * @param {String} msg The validation message
21470      */
21471     markInvalid : function(msg){
21472         if(!this.rendered || this.preventMark){ // not rendered
21473             return;
21474         }
21475         this.el.addClass(this.invalidClass);
21476         msg = msg || this.invalidText;
21477         switch(this.msgTarget){
21478             case 'qtip':
21479                 this.el.dom.qtip = msg;
21480                 this.el.dom.qclass = 'x-form-invalid-tip';
21481                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21482                     Roo.QuickTips.enable();
21483                 }
21484                 break;
21485             case 'title':
21486                 this.el.dom.title = msg;
21487                 break;
21488             case 'under':
21489                 if(!this.errorEl){
21490                     var elp = this.el.findParent('.x-form-element', 5, true);
21491                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21492                     this.errorEl.setWidth(elp.getWidth(true)-20);
21493                 }
21494                 this.errorEl.update(msg);
21495                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21496                 break;
21497             case 'side':
21498                 if(!this.errorIcon){
21499                     var elp = this.el.findParent('.x-form-element', 5, true);
21500                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21501                 }
21502                 this.alignErrorIcon();
21503                 this.errorIcon.dom.qtip = msg;
21504                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21505                 this.errorIcon.show();
21506                 this.on('resize', this.alignErrorIcon, this);
21507                 break;
21508             default:
21509                 var t = Roo.getDom(this.msgTarget);
21510                 t.innerHTML = msg;
21511                 t.style.display = this.msgDisplay;
21512                 break;
21513         }
21514         this.fireEvent('invalid', this, msg);
21515     },
21516
21517     // private
21518     alignErrorIcon : function(){
21519         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21520     },
21521
21522     /**
21523      * Clear any invalid styles/messages for this field
21524      */
21525     clearInvalid : function(){
21526         if(!this.rendered || this.preventMark){ // not rendered
21527             return;
21528         }
21529         this.el.removeClass(this.invalidClass);
21530         switch(this.msgTarget){
21531             case 'qtip':
21532                 this.el.dom.qtip = '';
21533                 break;
21534             case 'title':
21535                 this.el.dom.title = '';
21536                 break;
21537             case 'under':
21538                 if(this.errorEl){
21539                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21540                 }
21541                 break;
21542             case 'side':
21543                 if(this.errorIcon){
21544                     this.errorIcon.dom.qtip = '';
21545                     this.errorIcon.hide();
21546                     this.un('resize', this.alignErrorIcon, this);
21547                 }
21548                 break;
21549             default:
21550                 var t = Roo.getDom(this.msgTarget);
21551                 t.innerHTML = '';
21552                 t.style.display = 'none';
21553                 break;
21554         }
21555         this.fireEvent('valid', this);
21556     },
21557
21558     /**
21559      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21560      * @return {Mixed} value The field value
21561      */
21562     getRawValue : function(){
21563         var v = this.el.getValue();
21564         if(v === this.emptyText){
21565             v = '';
21566         }
21567         return v;
21568     },
21569
21570     /**
21571      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21572      * @return {Mixed} value The field value
21573      */
21574     getValue : function(){
21575         var v = this.el.getValue();
21576         if(v === this.emptyText || v === undefined){
21577             v = '';
21578         }
21579         return v;
21580     },
21581
21582     /**
21583      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21584      * @param {Mixed} value The value to set
21585      */
21586     setRawValue : function(v){
21587         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21588     },
21589
21590     /**
21591      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21592      * @param {Mixed} value The value to set
21593      */
21594     setValue : function(v){
21595         this.value = v;
21596         if(this.rendered){
21597             this.el.dom.value = (v === null || v === undefined ? '' : v);
21598              this.validate();
21599         }
21600     },
21601
21602     adjustSize : function(w, h){
21603         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21604         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21605         return s;
21606     },
21607
21608     adjustWidth : function(tag, w){
21609         tag = tag.toLowerCase();
21610         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21611             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21612                 if(tag == 'input'){
21613                     return w + 2;
21614                 }
21615                 if(tag = 'textarea'){
21616                     return w-2;
21617                 }
21618             }else if(Roo.isOpera){
21619                 if(tag == 'input'){
21620                     return w + 2;
21621                 }
21622                 if(tag = 'textarea'){
21623                     return w-2;
21624                 }
21625             }
21626         }
21627         return w;
21628     }
21629 });
21630
21631
21632 // anything other than normal should be considered experimental
21633 Roo.form.Field.msgFx = {
21634     normal : {
21635         show: function(msgEl, f){
21636             msgEl.setDisplayed('block');
21637         },
21638
21639         hide : function(msgEl, f){
21640             msgEl.setDisplayed(false).update('');
21641         }
21642     },
21643
21644     slide : {
21645         show: function(msgEl, f){
21646             msgEl.slideIn('t', {stopFx:true});
21647         },
21648
21649         hide : function(msgEl, f){
21650             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21651         }
21652     },
21653
21654     slideRight : {
21655         show: function(msgEl, f){
21656             msgEl.fixDisplay();
21657             msgEl.alignTo(f.el, 'tl-tr');
21658             msgEl.slideIn('l', {stopFx:true});
21659         },
21660
21661         hide : function(msgEl, f){
21662             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21663         }
21664     }
21665 };/*
21666  * Based on:
21667  * Ext JS Library 1.1.1
21668  * Copyright(c) 2006-2007, Ext JS, LLC.
21669  *
21670  * Originally Released Under LGPL - original licence link has changed is not relivant.
21671  *
21672  * Fork - LGPL
21673  * <script type="text/javascript">
21674  */
21675  
21676
21677 /**
21678  * @class Roo.form.TextField
21679  * @extends Roo.form.Field
21680  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21681  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21682  * @constructor
21683  * Creates a new TextField
21684  * @param {Object} config Configuration options
21685  */
21686 Roo.form.TextField = function(config){
21687     Roo.form.TextField.superclass.constructor.call(this, config);
21688     this.addEvents({
21689         /**
21690          * @event autosize
21691          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21692          * according to the default logic, but this event provides a hook for the developer to apply additional
21693          * logic at runtime to resize the field if needed.
21694              * @param {Roo.form.Field} this This text field
21695              * @param {Number} width The new field width
21696              */
21697         autosize : true
21698     });
21699 };
21700
21701 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21702     /**
21703      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21704      */
21705     grow : false,
21706     /**
21707      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21708      */
21709     growMin : 30,
21710     /**
21711      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21712      */
21713     growMax : 800,
21714     /**
21715      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21716      */
21717     vtype : null,
21718     /**
21719      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21720      */
21721     maskRe : null,
21722     /**
21723      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21724      */
21725     disableKeyFilter : false,
21726     /**
21727      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21728      */
21729     allowBlank : true,
21730     /**
21731      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21732      */
21733     minLength : 0,
21734     /**
21735      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21736      */
21737     maxLength : Number.MAX_VALUE,
21738     /**
21739      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21740      */
21741     minLengthText : "The minimum length for this field is {0}",
21742     /**
21743      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21744      */
21745     maxLengthText : "The maximum length for this field is {0}",
21746     /**
21747      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21748      */
21749     selectOnFocus : false,
21750     /**
21751      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21752      */
21753     blankText : "This field is required",
21754     /**
21755      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21756      * If available, this function will be called only after the basic validators all return true, and will be passed the
21757      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21758      */
21759     validator : null,
21760     /**
21761      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21762      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21763      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21764      */
21765     regex : null,
21766     /**
21767      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21768      */
21769     regexText : "",
21770     /**
21771      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21772      */
21773     emptyText : null,
21774     /**
21775      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21776      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21777      */
21778     emptyClass : 'x-form-empty-field',
21779
21780     // private
21781     initEvents : function(){
21782         Roo.form.TextField.superclass.initEvents.call(this);
21783         if(this.validationEvent == 'keyup'){
21784             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21785             this.el.on('keyup', this.filterValidation, this);
21786         }
21787         else if(this.validationEvent !== false){
21788             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21789         }
21790         if(this.selectOnFocus || this.emptyText){
21791             this.on("focus", this.preFocus, this);
21792             if(this.emptyText){
21793                 this.on('blur', this.postBlur, this);
21794                 this.applyEmptyText();
21795             }
21796         }
21797         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21798             this.el.on("keypress", this.filterKeys, this);
21799         }
21800         if(this.grow){
21801             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21802             this.el.on("click", this.autoSize,  this);
21803         }
21804     },
21805
21806     processValue : function(value){
21807         if(this.stripCharsRe){
21808             var newValue = value.replace(this.stripCharsRe, '');
21809             if(newValue !== value){
21810                 this.setRawValue(newValue);
21811                 return newValue;
21812             }
21813         }
21814         return value;
21815     },
21816
21817     filterValidation : function(e){
21818         if(!e.isNavKeyPress()){
21819             this.validationTask.delay(this.validationDelay);
21820         }
21821     },
21822
21823     // private
21824     onKeyUp : function(e){
21825         if(!e.isNavKeyPress()){
21826             this.autoSize();
21827         }
21828     },
21829
21830     /**
21831      * Resets the current field value to the originally-loaded value and clears any validation messages.
21832      * Also adds emptyText and emptyClass if the original value was blank.
21833      */
21834     reset : function(){
21835         Roo.form.TextField.superclass.reset.call(this);
21836         this.applyEmptyText();
21837     },
21838
21839     applyEmptyText : function(){
21840         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21841             this.setRawValue(this.emptyText);
21842             this.el.addClass(this.emptyClass);
21843         }
21844     },
21845
21846     // private
21847     preFocus : function(){
21848         if(this.emptyText){
21849             if(this.el.dom.value == this.emptyText){
21850                 this.setRawValue('');
21851             }
21852             this.el.removeClass(this.emptyClass);
21853         }
21854         if(this.selectOnFocus){
21855             this.el.dom.select();
21856         }
21857     },
21858
21859     // private
21860     postBlur : function(){
21861         this.applyEmptyText();
21862     },
21863
21864     // private
21865     filterKeys : function(e){
21866         var k = e.getKey();
21867         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21868             return;
21869         }
21870         var c = e.getCharCode(), cc = String.fromCharCode(c);
21871         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21872             return;
21873         }
21874         if(!this.maskRe.test(cc)){
21875             e.stopEvent();
21876         }
21877     },
21878
21879     setValue : function(v){
21880         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21881             this.el.removeClass(this.emptyClass);
21882         }
21883         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21884         this.applyEmptyText();
21885         this.autoSize();
21886     },
21887
21888     /**
21889      * Validates a value according to the field's validation rules and marks the field as invalid
21890      * if the validation fails
21891      * @param {Mixed} value The value to validate
21892      * @return {Boolean} True if the value is valid, else false
21893      */
21894     validateValue : function(value){
21895         if(value.length < 1 || value === this.emptyText){ // if it's blank
21896              if(this.allowBlank){
21897                 this.clearInvalid();
21898                 return true;
21899              }else{
21900                 this.markInvalid(this.blankText);
21901                 return false;
21902              }
21903         }
21904         if(value.length < this.minLength){
21905             this.markInvalid(String.format(this.minLengthText, this.minLength));
21906             return false;
21907         }
21908         if(value.length > this.maxLength){
21909             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21910             return false;
21911         }
21912         if(this.vtype){
21913             var vt = Roo.form.VTypes;
21914             if(!vt[this.vtype](value, this)){
21915                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21916                 return false;
21917             }
21918         }
21919         if(typeof this.validator == "function"){
21920             var msg = this.validator(value);
21921             if(msg !== true){
21922                 this.markInvalid(msg);
21923                 return false;
21924             }
21925         }
21926         if(this.regex && !this.regex.test(value)){
21927             this.markInvalid(this.regexText);
21928             return false;
21929         }
21930         return true;
21931     },
21932
21933     /**
21934      * Selects text in this field
21935      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21936      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21937      */
21938     selectText : function(start, end){
21939         var v = this.getRawValue();
21940         if(v.length > 0){
21941             start = start === undefined ? 0 : start;
21942             end = end === undefined ? v.length : end;
21943             var d = this.el.dom;
21944             if(d.setSelectionRange){
21945                 d.setSelectionRange(start, end);
21946             }else if(d.createTextRange){
21947                 var range = d.createTextRange();
21948                 range.moveStart("character", start);
21949                 range.moveEnd("character", v.length-end);
21950                 range.select();
21951             }
21952         }
21953     },
21954
21955     /**
21956      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21957      * This only takes effect if grow = true, and fires the autosize event.
21958      */
21959     autoSize : function(){
21960         if(!this.grow || !this.rendered){
21961             return;
21962         }
21963         if(!this.metrics){
21964             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21965         }
21966         var el = this.el;
21967         var v = el.dom.value;
21968         var d = document.createElement('div');
21969         d.appendChild(document.createTextNode(v));
21970         v = d.innerHTML;
21971         d = null;
21972         v += "&#160;";
21973         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21974         this.el.setWidth(w);
21975         this.fireEvent("autosize", this, w);
21976     }
21977 });/*
21978  * Based on:
21979  * Ext JS Library 1.1.1
21980  * Copyright(c) 2006-2007, Ext JS, LLC.
21981  *
21982  * Originally Released Under LGPL - original licence link has changed is not relivant.
21983  *
21984  * Fork - LGPL
21985  * <script type="text/javascript">
21986  */
21987  
21988 /**
21989  * @class Roo.form.Hidden
21990  * @extends Roo.form.TextField
21991  * Simple Hidden element used on forms 
21992  * 
21993  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21994  * 
21995  * @constructor
21996  * Creates a new Hidden form element.
21997  * @param {Object} config Configuration options
21998  */
21999
22000
22001
22002 // easy hidden field...
22003 Roo.form.Hidden = function(config){
22004     Roo.form.Hidden.superclass.constructor.call(this, config);
22005 };
22006   
22007 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22008     fieldLabel:      '',
22009     inputType:      'hidden',
22010     width:          50,
22011     allowBlank:     true,
22012     labelSeparator: '',
22013     hidden:         true,
22014     itemCls :       'x-form-item-display-none'
22015
22016
22017 });
22018
22019
22020 /*
22021  * Based on:
22022  * Ext JS Library 1.1.1
22023  * Copyright(c) 2006-2007, Ext JS, LLC.
22024  *
22025  * Originally Released Under LGPL - original licence link has changed is not relivant.
22026  *
22027  * Fork - LGPL
22028  * <script type="text/javascript">
22029  */
22030  
22031 /**
22032  * @class Roo.form.TriggerField
22033  * @extends Roo.form.TextField
22034  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22035  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22036  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22037  * for which you can provide a custom implementation.  For example:
22038  * <pre><code>
22039 var trigger = new Roo.form.TriggerField();
22040 trigger.onTriggerClick = myTriggerFn;
22041 trigger.applyTo('my-field');
22042 </code></pre>
22043  *
22044  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22045  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22046  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22047  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22048  * @constructor
22049  * Create a new TriggerField.
22050  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22051  * to the base TextField)
22052  */
22053 Roo.form.TriggerField = function(config){
22054     this.mimicing = false;
22055     Roo.form.TriggerField.superclass.constructor.call(this, config);
22056 };
22057
22058 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22059     /**
22060      * @cfg {String} triggerClass A CSS class to apply to the trigger
22061      */
22062     /**
22063      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22064      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22065      */
22066     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22067     /**
22068      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22069      */
22070     hideTrigger:false,
22071
22072     /** @cfg {Boolean} grow @hide */
22073     /** @cfg {Number} growMin @hide */
22074     /** @cfg {Number} growMax @hide */
22075
22076     /**
22077      * @hide 
22078      * @method
22079      */
22080     autoSize: Roo.emptyFn,
22081     // private
22082     monitorTab : true,
22083     // private
22084     deferHeight : true,
22085
22086     
22087     actionMode : 'wrap',
22088     // private
22089     onResize : function(w, h){
22090         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22091         if(typeof w == 'number'){
22092             var x = w - this.trigger.getWidth();
22093             this.el.setWidth(this.adjustWidth('input', x));
22094             this.trigger.setStyle('left', x+'px');
22095         }
22096     },
22097
22098     // private
22099     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22100
22101     // private
22102     getResizeEl : function(){
22103         return this.wrap;
22104     },
22105
22106     // private
22107     getPositionEl : function(){
22108         return this.wrap;
22109     },
22110
22111     // private
22112     alignErrorIcon : function(){
22113         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22114     },
22115
22116     // private
22117     onRender : function(ct, position){
22118         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22119         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22120         this.trigger = this.wrap.createChild(this.triggerConfig ||
22121                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22122         if(this.hideTrigger){
22123             this.trigger.setDisplayed(false);
22124         }
22125         this.initTrigger();
22126         if(!this.width){
22127             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22128         }
22129     },
22130
22131     // private
22132     initTrigger : function(){
22133         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22134         this.trigger.addClassOnOver('x-form-trigger-over');
22135         this.trigger.addClassOnClick('x-form-trigger-click');
22136     },
22137
22138     // private
22139     onDestroy : function(){
22140         if(this.trigger){
22141             this.trigger.removeAllListeners();
22142             this.trigger.remove();
22143         }
22144         if(this.wrap){
22145             this.wrap.remove();
22146         }
22147         Roo.form.TriggerField.superclass.onDestroy.call(this);
22148     },
22149
22150     // private
22151     onFocus : function(){
22152         Roo.form.TriggerField.superclass.onFocus.call(this);
22153         if(!this.mimicing){
22154             this.wrap.addClass('x-trigger-wrap-focus');
22155             this.mimicing = true;
22156             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22157             if(this.monitorTab){
22158                 this.el.on("keydown", this.checkTab, this);
22159             }
22160         }
22161     },
22162
22163     // private
22164     checkTab : function(e){
22165         if(e.getKey() == e.TAB){
22166             this.triggerBlur();
22167         }
22168     },
22169
22170     // private
22171     onBlur : function(){
22172         // do nothing
22173     },
22174
22175     // private
22176     mimicBlur : function(e, t){
22177         if(!this.wrap.contains(t) && this.validateBlur()){
22178             this.triggerBlur();
22179         }
22180     },
22181
22182     // private
22183     triggerBlur : function(){
22184         this.mimicing = false;
22185         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22186         if(this.monitorTab){
22187             this.el.un("keydown", this.checkTab, this);
22188         }
22189         this.wrap.removeClass('x-trigger-wrap-focus');
22190         Roo.form.TriggerField.superclass.onBlur.call(this);
22191     },
22192
22193     // private
22194     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22195     validateBlur : function(e, t){
22196         return true;
22197     },
22198
22199     // private
22200     onDisable : function(){
22201         Roo.form.TriggerField.superclass.onDisable.call(this);
22202         if(this.wrap){
22203             this.wrap.addClass('x-item-disabled');
22204         }
22205     },
22206
22207     // private
22208     onEnable : function(){
22209         Roo.form.TriggerField.superclass.onEnable.call(this);
22210         if(this.wrap){
22211             this.wrap.removeClass('x-item-disabled');
22212         }
22213     },
22214
22215     // private
22216     onShow : function(){
22217         var ae = this.getActionEl();
22218         
22219         if(ae){
22220             ae.dom.style.display = '';
22221             ae.dom.style.visibility = 'visible';
22222         }
22223     },
22224
22225     // private
22226     
22227     onHide : function(){
22228         var ae = this.getActionEl();
22229         ae.dom.style.display = 'none';
22230     },
22231
22232     /**
22233      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22234      * by an implementing function.
22235      * @method
22236      * @param {EventObject} e
22237      */
22238     onTriggerClick : Roo.emptyFn
22239 });
22240
22241 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22242 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22243 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22244 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22245     initComponent : function(){
22246         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22247
22248         this.triggerConfig = {
22249             tag:'span', cls:'x-form-twin-triggers', cn:[
22250             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22251             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22252         ]};
22253     },
22254
22255     getTrigger : function(index){
22256         return this.triggers[index];
22257     },
22258
22259     initTrigger : function(){
22260         var ts = this.trigger.select('.x-form-trigger', true);
22261         this.wrap.setStyle('overflow', 'hidden');
22262         var triggerField = this;
22263         ts.each(function(t, all, index){
22264             t.hide = function(){
22265                 var w = triggerField.wrap.getWidth();
22266                 this.dom.style.display = 'none';
22267                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22268             };
22269             t.show = function(){
22270                 var w = triggerField.wrap.getWidth();
22271                 this.dom.style.display = '';
22272                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22273             };
22274             var triggerIndex = 'Trigger'+(index+1);
22275
22276             if(this['hide'+triggerIndex]){
22277                 t.dom.style.display = 'none';
22278             }
22279             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22280             t.addClassOnOver('x-form-trigger-over');
22281             t.addClassOnClick('x-form-trigger-click');
22282         }, this);
22283         this.triggers = ts.elements;
22284     },
22285
22286     onTrigger1Click : Roo.emptyFn,
22287     onTrigger2Click : Roo.emptyFn
22288 });/*
22289  * Based on:
22290  * Ext JS Library 1.1.1
22291  * Copyright(c) 2006-2007, Ext JS, LLC.
22292  *
22293  * Originally Released Under LGPL - original licence link has changed is not relivant.
22294  *
22295  * Fork - LGPL
22296  * <script type="text/javascript">
22297  */
22298  
22299 /**
22300  * @class Roo.form.TextArea
22301  * @extends Roo.form.TextField
22302  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22303  * support for auto-sizing.
22304  * @constructor
22305  * Creates a new TextArea
22306  * @param {Object} config Configuration options
22307  */
22308 Roo.form.TextArea = function(config){
22309     Roo.form.TextArea.superclass.constructor.call(this, config);
22310     // these are provided exchanges for backwards compat
22311     // minHeight/maxHeight were replaced by growMin/growMax to be
22312     // compatible with TextField growing config values
22313     if(this.minHeight !== undefined){
22314         this.growMin = this.minHeight;
22315     }
22316     if(this.maxHeight !== undefined){
22317         this.growMax = this.maxHeight;
22318     }
22319 };
22320
22321 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22322     /**
22323      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22324      */
22325     growMin : 60,
22326     /**
22327      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22328      */
22329     growMax: 1000,
22330     /**
22331      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22332      * in the field (equivalent to setting overflow: hidden, defaults to false)
22333      */
22334     preventScrollbars: false,
22335     /**
22336      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22337      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22338      */
22339
22340     // private
22341     onRender : function(ct, position){
22342         if(!this.el){
22343             this.defaultAutoCreate = {
22344                 tag: "textarea",
22345                 style:"width:300px;height:60px;",
22346                 autocomplete: "off"
22347             };
22348         }
22349         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22350         if(this.grow){
22351             this.textSizeEl = Roo.DomHelper.append(document.body, {
22352                 tag: "pre", cls: "x-form-grow-sizer"
22353             });
22354             if(this.preventScrollbars){
22355                 this.el.setStyle("overflow", "hidden");
22356             }
22357             this.el.setHeight(this.growMin);
22358         }
22359     },
22360
22361     onDestroy : function(){
22362         if(this.textSizeEl){
22363             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22364         }
22365         Roo.form.TextArea.superclass.onDestroy.call(this);
22366     },
22367
22368     // private
22369     onKeyUp : function(e){
22370         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22371             this.autoSize();
22372         }
22373     },
22374
22375     /**
22376      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22377      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22378      */
22379     autoSize : function(){
22380         if(!this.grow || !this.textSizeEl){
22381             return;
22382         }
22383         var el = this.el;
22384         var v = el.dom.value;
22385         var ts = this.textSizeEl;
22386
22387         ts.innerHTML = '';
22388         ts.appendChild(document.createTextNode(v));
22389         v = ts.innerHTML;
22390
22391         Roo.fly(ts).setWidth(this.el.getWidth());
22392         if(v.length < 1){
22393             v = "&#160;&#160;";
22394         }else{
22395             if(Roo.isIE){
22396                 v = v.replace(/\n/g, '<p>&#160;</p>');
22397             }
22398             v += "&#160;\n&#160;";
22399         }
22400         ts.innerHTML = v;
22401         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22402         if(h != this.lastHeight){
22403             this.lastHeight = h;
22404             this.el.setHeight(h);
22405             this.fireEvent("autosize", this, h);
22406         }
22407     }
22408 });/*
22409  * Based on:
22410  * Ext JS Library 1.1.1
22411  * Copyright(c) 2006-2007, Ext JS, LLC.
22412  *
22413  * Originally Released Under LGPL - original licence link has changed is not relivant.
22414  *
22415  * Fork - LGPL
22416  * <script type="text/javascript">
22417  */
22418  
22419
22420 /**
22421  * @class Roo.form.NumberField
22422  * @extends Roo.form.TextField
22423  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22424  * @constructor
22425  * Creates a new NumberField
22426  * @param {Object} config Configuration options
22427  */
22428 Roo.form.NumberField = function(config){
22429     Roo.form.NumberField.superclass.constructor.call(this, config);
22430 };
22431
22432 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22433     /**
22434      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22435      */
22436     fieldClass: "x-form-field x-form-num-field",
22437     /**
22438      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22439      */
22440     allowDecimals : true,
22441     /**
22442      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22443      */
22444     decimalSeparator : ".",
22445     /**
22446      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22447      */
22448     decimalPrecision : 2,
22449     /**
22450      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22451      */
22452     allowNegative : true,
22453     /**
22454      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22455      */
22456     minValue : Number.NEGATIVE_INFINITY,
22457     /**
22458      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22459      */
22460     maxValue : Number.MAX_VALUE,
22461     /**
22462      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22463      */
22464     minText : "The minimum value for this field is {0}",
22465     /**
22466      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22467      */
22468     maxText : "The maximum value for this field is {0}",
22469     /**
22470      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22471      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22472      */
22473     nanText : "{0} is not a valid number",
22474
22475     // private
22476     initEvents : function(){
22477         Roo.form.NumberField.superclass.initEvents.call(this);
22478         var allowed = "0123456789";
22479         if(this.allowDecimals){
22480             allowed += this.decimalSeparator;
22481         }
22482         if(this.allowNegative){
22483             allowed += "-";
22484         }
22485         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22486         var keyPress = function(e){
22487             var k = e.getKey();
22488             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22489                 return;
22490             }
22491             var c = e.getCharCode();
22492             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22493                 e.stopEvent();
22494             }
22495         };
22496         this.el.on("keypress", keyPress, this);
22497     },
22498
22499     // private
22500     validateValue : function(value){
22501         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22502             return false;
22503         }
22504         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22505              return true;
22506         }
22507         var num = this.parseValue(value);
22508         if(isNaN(num)){
22509             this.markInvalid(String.format(this.nanText, value));
22510             return false;
22511         }
22512         if(num < this.minValue){
22513             this.markInvalid(String.format(this.minText, this.minValue));
22514             return false;
22515         }
22516         if(num > this.maxValue){
22517             this.markInvalid(String.format(this.maxText, this.maxValue));
22518             return false;
22519         }
22520         return true;
22521     },
22522
22523     getValue : function(){
22524         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22525     },
22526
22527     // private
22528     parseValue : function(value){
22529         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22530         return isNaN(value) ? '' : value;
22531     },
22532
22533     // private
22534     fixPrecision : function(value){
22535         var nan = isNaN(value);
22536         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22537             return nan ? '' : value;
22538         }
22539         return parseFloat(value).toFixed(this.decimalPrecision);
22540     },
22541
22542     setValue : function(v){
22543         v = this.fixPrecision(v);
22544         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22545     },
22546
22547     // private
22548     decimalPrecisionFcn : function(v){
22549         return Math.floor(v);
22550     },
22551
22552     beforeBlur : function(){
22553         var v = this.parseValue(this.getRawValue());
22554         if(v){
22555             this.setValue(v);
22556         }
22557     }
22558 });/*
22559  * Based on:
22560  * Ext JS Library 1.1.1
22561  * Copyright(c) 2006-2007, Ext JS, LLC.
22562  *
22563  * Originally Released Under LGPL - original licence link has changed is not relivant.
22564  *
22565  * Fork - LGPL
22566  * <script type="text/javascript">
22567  */
22568  
22569 /**
22570  * @class Roo.form.DateField
22571  * @extends Roo.form.TriggerField
22572  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22573 * @constructor
22574 * Create a new DateField
22575 * @param {Object} config
22576  */
22577 Roo.form.DateField = function(config){
22578     Roo.form.DateField.superclass.constructor.call(this, config);
22579     
22580       this.addEvents({
22581          
22582         /**
22583          * @event select
22584          * Fires when a date is selected
22585              * @param {Roo.form.DateField} combo This combo box
22586              * @param {Date} date The date selected
22587              */
22588         'select' : true
22589          
22590     });
22591     
22592     
22593     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22594     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22595     this.ddMatch = null;
22596     if(this.disabledDates){
22597         var dd = this.disabledDates;
22598         var re = "(?:";
22599         for(var i = 0; i < dd.length; i++){
22600             re += dd[i];
22601             if(i != dd.length-1) re += "|";
22602         }
22603         this.ddMatch = new RegExp(re + ")");
22604     }
22605 };
22606
22607 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22608     /**
22609      * @cfg {String} format
22610      * The default date format string which can be overriden for localization support.  The format must be
22611      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22612      */
22613     format : "m/d/y",
22614     /**
22615      * @cfg {String} altFormats
22616      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22617      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22618      */
22619     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22620     /**
22621      * @cfg {Array} disabledDays
22622      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22623      */
22624     disabledDays : null,
22625     /**
22626      * @cfg {String} disabledDaysText
22627      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22628      */
22629     disabledDaysText : "Disabled",
22630     /**
22631      * @cfg {Array} disabledDates
22632      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22633      * expression so they are very powerful. Some examples:
22634      * <ul>
22635      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22636      * <li>["03/08", "09/16"] would disable those days for every year</li>
22637      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22638      * <li>["03/../2006"] would disable every day in March 2006</li>
22639      * <li>["^03"] would disable every day in every March</li>
22640      * </ul>
22641      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22642      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22643      */
22644     disabledDates : null,
22645     /**
22646      * @cfg {String} disabledDatesText
22647      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22648      */
22649     disabledDatesText : "Disabled",
22650     /**
22651      * @cfg {Date/String} minValue
22652      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22653      * valid format (defaults to null).
22654      */
22655     minValue : null,
22656     /**
22657      * @cfg {Date/String} maxValue
22658      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22659      * valid format (defaults to null).
22660      */
22661     maxValue : null,
22662     /**
22663      * @cfg {String} minText
22664      * The error text to display when the date in the cell is before minValue (defaults to
22665      * 'The date in this field must be after {minValue}').
22666      */
22667     minText : "The date in this field must be equal to or after {0}",
22668     /**
22669      * @cfg {String} maxText
22670      * The error text to display when the date in the cell is after maxValue (defaults to
22671      * 'The date in this field must be before {maxValue}').
22672      */
22673     maxText : "The date in this field must be equal to or before {0}",
22674     /**
22675      * @cfg {String} invalidText
22676      * The error text to display when the date in the field is invalid (defaults to
22677      * '{value} is not a valid date - it must be in the format {format}').
22678      */
22679     invalidText : "{0} is not a valid date - it must be in the format {1}",
22680     /**
22681      * @cfg {String} triggerClass
22682      * An additional CSS class used to style the trigger button.  The trigger will always get the
22683      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22684      * which displays a calendar icon).
22685      */
22686     triggerClass : 'x-form-date-trigger',
22687     
22688
22689     /**
22690      * @cfg {Boolean} useIso
22691      * if enabled, then the date field will use a hidden field to store the 
22692      * real value as iso formated date. default (false)
22693      */ 
22694     useIso : false,
22695     /**
22696      * @cfg {String/Object} autoCreate
22697      * A DomHelper element spec, or true for a default element spec (defaults to
22698      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22699      */ 
22700     // private
22701     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22702     
22703     // private
22704     hiddenField: false,
22705     
22706     onRender : function(ct, position)
22707     {
22708         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22709         if (this.useIso) {
22710             this.el.dom.removeAttribute('name'); 
22711             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22712                     'before', true);
22713             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22714             // prevent input submission
22715             this.hiddenName = this.name;
22716         }
22717             
22718             
22719     },
22720     
22721     // private
22722     validateValue : function(value)
22723     {
22724         value = this.formatDate(value);
22725         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22726             return false;
22727         }
22728         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22729              return true;
22730         }
22731         var svalue = value;
22732         value = this.parseDate(value);
22733         if(!value){
22734             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22735             return false;
22736         }
22737         var time = value.getTime();
22738         if(this.minValue && time < this.minValue.getTime()){
22739             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22740             return false;
22741         }
22742         if(this.maxValue && time > this.maxValue.getTime()){
22743             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22744             return false;
22745         }
22746         if(this.disabledDays){
22747             var day = value.getDay();
22748             for(var i = 0; i < this.disabledDays.length; i++) {
22749                 if(day === this.disabledDays[i]){
22750                     this.markInvalid(this.disabledDaysText);
22751                     return false;
22752                 }
22753             }
22754         }
22755         var fvalue = this.formatDate(value);
22756         if(this.ddMatch && this.ddMatch.test(fvalue)){
22757             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22758             return false;
22759         }
22760         return true;
22761     },
22762
22763     // private
22764     // Provides logic to override the default TriggerField.validateBlur which just returns true
22765     validateBlur : function(){
22766         return !this.menu || !this.menu.isVisible();
22767     },
22768
22769     /**
22770      * Returns the current date value of the date field.
22771      * @return {Date} The date value
22772      */
22773     getValue : function(){
22774         
22775         return  this.hiddenField ?
22776                 this.hiddenField.value :
22777                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22778     },
22779
22780     /**
22781      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22782      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22783      * (the default format used is "m/d/y").
22784      * <br />Usage:
22785      * <pre><code>
22786 //All of these calls set the same date value (May 4, 2006)
22787
22788 //Pass a date object:
22789 var dt = new Date('5/4/06');
22790 dateField.setValue(dt);
22791
22792 //Pass a date string (default format):
22793 dateField.setValue('5/4/06');
22794
22795 //Pass a date string (custom format):
22796 dateField.format = 'Y-m-d';
22797 dateField.setValue('2006-5-4');
22798 </code></pre>
22799      * @param {String/Date} date The date or valid date string
22800      */
22801     setValue : function(date){
22802         if (this.hiddenField) {
22803             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22804         }
22805         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22806     },
22807
22808     // private
22809     parseDate : function(value){
22810         if(!value || value instanceof Date){
22811             return value;
22812         }
22813         var v = Date.parseDate(value, this.format);
22814         if(!v && this.altFormats){
22815             if(!this.altFormatsArray){
22816                 this.altFormatsArray = this.altFormats.split("|");
22817             }
22818             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22819                 v = Date.parseDate(value, this.altFormatsArray[i]);
22820             }
22821         }
22822         return v;
22823     },
22824
22825     // private
22826     formatDate : function(date, fmt){
22827         return (!date || !(date instanceof Date)) ?
22828                date : date.dateFormat(fmt || this.format);
22829     },
22830
22831     // private
22832     menuListeners : {
22833         select: function(m, d){
22834             this.setValue(d);
22835             this.fireEvent('select', this, d);
22836         },
22837         show : function(){ // retain focus styling
22838             this.onFocus();
22839         },
22840         hide : function(){
22841             this.focus.defer(10, this);
22842             var ml = this.menuListeners;
22843             this.menu.un("select", ml.select,  this);
22844             this.menu.un("show", ml.show,  this);
22845             this.menu.un("hide", ml.hide,  this);
22846         }
22847     },
22848
22849     // private
22850     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22851     onTriggerClick : function(){
22852         if(this.disabled){
22853             return;
22854         }
22855         if(this.menu == null){
22856             this.menu = new Roo.menu.DateMenu();
22857         }
22858         Roo.apply(this.menu.picker,  {
22859             showClear: this.allowBlank,
22860             minDate : this.minValue,
22861             maxDate : this.maxValue,
22862             disabledDatesRE : this.ddMatch,
22863             disabledDatesText : this.disabledDatesText,
22864             disabledDays : this.disabledDays,
22865             disabledDaysText : this.disabledDaysText,
22866             format : this.useIso ? 'Y-m-d' : this.format,
22867             minText : String.format(this.minText, this.formatDate(this.minValue)),
22868             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22869         });
22870         this.menu.on(Roo.apply({}, this.menuListeners, {
22871             scope:this
22872         }));
22873         this.menu.picker.setValue(this.getValue() || new Date());
22874         this.menu.show(this.el, "tl-bl?");
22875     },
22876
22877     beforeBlur : function(){
22878         var v = this.parseDate(this.getRawValue());
22879         if(v){
22880             this.setValue(v);
22881         }
22882     }
22883
22884     /** @cfg {Boolean} grow @hide */
22885     /** @cfg {Number} growMin @hide */
22886     /** @cfg {Number} growMax @hide */
22887     /**
22888      * @hide
22889      * @method autoSize
22890      */
22891 });/*
22892  * Based on:
22893  * Ext JS Library 1.1.1
22894  * Copyright(c) 2006-2007, Ext JS, LLC.
22895  *
22896  * Originally Released Under LGPL - original licence link has changed is not relivant.
22897  *
22898  * Fork - LGPL
22899  * <script type="text/javascript">
22900  */
22901  
22902 /**
22903  * @class Roo.form.MonthField
22904  * @extends Roo.form.TriggerField
22905  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22906 * @constructor
22907 * Create a new MonthField
22908 * @param {Object} config
22909  */
22910 Roo.form.MonthField = function(config){
22911     
22912     Roo.form.MonthField.superclass.constructor.call(this, config);
22913     
22914       this.addEvents({
22915          
22916         /**
22917          * @event select
22918          * Fires when a date is selected
22919              * @param {Roo.form.MonthFieeld} combo This combo box
22920              * @param {Date} date The date selected
22921              */
22922         'select' : true
22923          
22924     });
22925     
22926     
22927     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22928     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22929     this.ddMatch = null;
22930     if(this.disabledDates){
22931         var dd = this.disabledDates;
22932         var re = "(?:";
22933         for(var i = 0; i < dd.length; i++){
22934             re += dd[i];
22935             if(i != dd.length-1) re += "|";
22936         }
22937         this.ddMatch = new RegExp(re + ")");
22938     }
22939 };
22940
22941 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22942     /**
22943      * @cfg {String} format
22944      * The default date format string which can be overriden for localization support.  The format must be
22945      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22946      */
22947     format : "M Y",
22948     /**
22949      * @cfg {String} altFormats
22950      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22951      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22952      */
22953     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22954     /**
22955      * @cfg {Array} disabledDays
22956      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22957      */
22958     disabledDays : [0,1,2,3,4,5,6],
22959     /**
22960      * @cfg {String} disabledDaysText
22961      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22962      */
22963     disabledDaysText : "Disabled",
22964     /**
22965      * @cfg {Array} disabledDates
22966      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22967      * expression so they are very powerful. Some examples:
22968      * <ul>
22969      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22970      * <li>["03/08", "09/16"] would disable those days for every year</li>
22971      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22972      * <li>["03/../2006"] would disable every day in March 2006</li>
22973      * <li>["^03"] would disable every day in every March</li>
22974      * </ul>
22975      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22976      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22977      */
22978     disabledDates : null,
22979     /**
22980      * @cfg {String} disabledDatesText
22981      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22982      */
22983     disabledDatesText : "Disabled",
22984     /**
22985      * @cfg {Date/String} minValue
22986      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22987      * valid format (defaults to null).
22988      */
22989     minValue : null,
22990     /**
22991      * @cfg {Date/String} maxValue
22992      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22993      * valid format (defaults to null).
22994      */
22995     maxValue : null,
22996     /**
22997      * @cfg {String} minText
22998      * The error text to display when the date in the cell is before minValue (defaults to
22999      * 'The date in this field must be after {minValue}').
23000      */
23001     minText : "The date in this field must be equal to or after {0}",
23002     /**
23003      * @cfg {String} maxTextf
23004      * The error text to display when the date in the cell is after maxValue (defaults to
23005      * 'The date in this field must be before {maxValue}').
23006      */
23007     maxText : "The date in this field must be equal to or before {0}",
23008     /**
23009      * @cfg {String} invalidText
23010      * The error text to display when the date in the field is invalid (defaults to
23011      * '{value} is not a valid date - it must be in the format {format}').
23012      */
23013     invalidText : "{0} is not a valid date - it must be in the format {1}",
23014     /**
23015      * @cfg {String} triggerClass
23016      * An additional CSS class used to style the trigger button.  The trigger will always get the
23017      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23018      * which displays a calendar icon).
23019      */
23020     triggerClass : 'x-form-date-trigger',
23021     
23022
23023     /**
23024      * @cfg {Boolean} useIso
23025      * if enabled, then the date field will use a hidden field to store the 
23026      * real value as iso formated date. default (true)
23027      */ 
23028     useIso : true,
23029     /**
23030      * @cfg {String/Object} autoCreate
23031      * A DomHelper element spec, or true for a default element spec (defaults to
23032      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23033      */ 
23034     // private
23035     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23036     
23037     // private
23038     hiddenField: false,
23039     
23040     hideMonthPicker : false,
23041     
23042     onRender : function(ct, position)
23043     {
23044         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23045         if (this.useIso) {
23046             this.el.dom.removeAttribute('name'); 
23047             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23048                     'before', true);
23049             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23050             // prevent input submission
23051             this.hiddenName = this.name;
23052         }
23053             
23054             
23055     },
23056     
23057     // private
23058     validateValue : function(value)
23059     {
23060         value = this.formatDate(value);
23061         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
23062             return false;
23063         }
23064         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23065              return true;
23066         }
23067         var svalue = value;
23068         value = this.parseDate(value);
23069         if(!value){
23070             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23071             return false;
23072         }
23073         var time = value.getTime();
23074         if(this.minValue && time < this.minValue.getTime()){
23075             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23076             return false;
23077         }
23078         if(this.maxValue && time > this.maxValue.getTime()){
23079             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23080             return false;
23081         }
23082         /*if(this.disabledDays){
23083             var day = value.getDay();
23084             for(var i = 0; i < this.disabledDays.length; i++) {
23085                 if(day === this.disabledDays[i]){
23086                     this.markInvalid(this.disabledDaysText);
23087                     return false;
23088                 }
23089             }
23090         }
23091         */
23092         var fvalue = this.formatDate(value);
23093         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23094             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23095             return false;
23096         }
23097         */
23098         return true;
23099     },
23100
23101     // private
23102     // Provides logic to override the default TriggerField.validateBlur which just returns true
23103     validateBlur : function(){
23104         return !this.menu || !this.menu.isVisible();
23105     },
23106
23107     /**
23108      * Returns the current date value of the date field.
23109      * @return {Date} The date value
23110      */
23111     getValue : function(){
23112         
23113         
23114         
23115         return  this.hiddenField ?
23116                 this.hiddenField.value :
23117                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
23118     },
23119
23120     /**
23121      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23122      * date, using monthField.format as the date format, according to the same rules as {@link Date#parseDate}
23123      * (the default format used is "m/d/y").
23124      * <br />Usage:
23125      * <pre><code>
23126 //All of these calls set the same date value (May 4, 2006)
23127
23128 //Pass a date object:
23129 var dt = new Date('5/4/06');
23130 monthField.setValue(dt);
23131
23132 //Pass a date string (default format):
23133 monthField.setValue('5/4/06');
23134
23135 //Pass a date string (custom format):
23136 monthField.format = 'Y-m-d';
23137 monthField.setValue('2006-5-4');
23138 </code></pre>
23139      * @param {String/Date} date The date or valid date string
23140      */
23141     setValue : function(date){
23142         if (this.hiddenField) {
23143             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23144         }
23145         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23146     },
23147
23148     // private
23149     parseDate : function(value){
23150         if(!value || value instanceof Date){
23151             return value;
23152         }
23153         var v = Date.parseDate(value, this.format);
23154         if(!v && this.altFormats){
23155             if(!this.altFormatsArray){
23156                 this.altFormatsArray = this.altFormats.split("|");
23157             }
23158             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23159                 v = Date.parseDate(value, this.altFormatsArray[i]);
23160             }
23161         }
23162         return v;
23163     },
23164
23165     // private
23166     formatDate : function(date, fmt){
23167         return (!date || !(date instanceof Date)) ?
23168                date : date.dateFormat(fmt || this.format);
23169     },
23170
23171     // private
23172     menuListeners : {
23173         select: function(m, d){
23174             this.setValue(d);
23175             this.fireEvent('select', this, d);
23176         },
23177         show : function(){ // retain focus styling
23178             this.onFocus();
23179         },
23180         hide : function(){
23181             this.focus.defer(10, this);
23182             var ml = this.menuListeners;
23183             this.menu.un("select", ml.select,  this);
23184             this.menu.un("show", ml.show,  this);
23185             this.menu.un("hide", ml.hide,  this);
23186         }
23187     },
23188     // private
23189     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23190     onTriggerClick : function(){
23191         if(this.disabled){
23192             return;
23193         }
23194         if(this.menu == null){
23195             this.menu = new Roo.menu.DateMenu();
23196         }
23197         
23198         Roo.apply(this.menu.picker,  {
23199             
23200             showClear: this.allowBlank,
23201             minDate : this.minValue,
23202             maxDate : this.maxValue,
23203             disabledDatesRE : this.ddMatch,
23204             disabledDatesText : this.disabledDatesText,
23205             
23206             format : this.format,
23207             minText : String.format(this.minText, this.formatDate(this.minValue)),
23208             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23209             
23210         });
23211         
23212         this.menu.on(Roo.apply({}, this.menuListeners, {
23213             scope:this
23214         }));
23215         
23216         var m = this.menu;
23217         var p = m.picker;
23218         p.format = this.useIso ? 'Y-m-d' : this.format;  // make sure they are the same..?
23219         Roo.log('picker set value');
23220         Roo.log(this.getValue());
23221         p.setValue(this.getValue() || new Date());
23222         m.show(this.el, "tl-bl?");
23223         
23224         // hidden the day picker
23225         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23226         
23227         (function() {
23228             p.showMonthPicker();
23229         }).defer(100);
23230         
23231         p.hideMonthPicker  = function(disableAnim){
23232             if(this.monthPicker){
23233                 if(disableAnim === true){
23234                     this.monthPicker.hide();
23235                 }else{
23236                     this.monthPicker.slideOut('t', {duration:.2});
23237                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth));
23238                     p.fireEvent("select", this, this.value);
23239                     m.hide();
23240                 }
23241             }
23242         }
23243     },
23244
23245     beforeBlur : function(){
23246         var v = this.parseDate(this.getRawValue());
23247         if(v){
23248             this.setValue(v);
23249         }
23250     }
23251
23252     /** @cfg {Boolean} grow @hide */
23253     /** @cfg {Number} growMin @hide */
23254     /** @cfg {Number} growMax @hide */
23255     /**
23256      * @hide
23257      * @method autoSize
23258      */
23259 });/*
23260  * Based on:
23261  * Ext JS Library 1.1.1
23262  * Copyright(c) 2006-2007, Ext JS, LLC.
23263  *
23264  * Originally Released Under LGPL - original licence link has changed is not relivant.
23265  *
23266  * Fork - LGPL
23267  * <script type="text/javascript">
23268  */
23269  
23270
23271 /**
23272  * @class Roo.form.ComboBox
23273  * @extends Roo.form.TriggerField
23274  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23275  * @constructor
23276  * Create a new ComboBox.
23277  * @param {Object} config Configuration options
23278  */
23279 Roo.form.ComboBox = function(config){
23280     Roo.form.ComboBox.superclass.constructor.call(this, config);
23281     this.addEvents({
23282         /**
23283          * @event expand
23284          * Fires when the dropdown list is expanded
23285              * @param {Roo.form.ComboBox} combo This combo box
23286              */
23287         'expand' : true,
23288         /**
23289          * @event collapse
23290          * Fires when the dropdown list is collapsed
23291              * @param {Roo.form.ComboBox} combo This combo box
23292              */
23293         'collapse' : true,
23294         /**
23295          * @event beforeselect
23296          * Fires before a list item is selected. Return false to cancel the selection.
23297              * @param {Roo.form.ComboBox} combo This combo box
23298              * @param {Roo.data.Record} record The data record returned from the underlying store
23299              * @param {Number} index The index of the selected item in the dropdown list
23300              */
23301         'beforeselect' : true,
23302         /**
23303          * @event select
23304          * Fires when a list item is selected
23305              * @param {Roo.form.ComboBox} combo This combo box
23306              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23307              * @param {Number} index The index of the selected item in the dropdown list
23308              */
23309         'select' : true,
23310         /**
23311          * @event beforequery
23312          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23313          * The event object passed has these properties:
23314              * @param {Roo.form.ComboBox} combo This combo box
23315              * @param {String} query The query
23316              * @param {Boolean} forceAll true to force "all" query
23317              * @param {Boolean} cancel true to cancel the query
23318              * @param {Object} e The query event object
23319              */
23320         'beforequery': true,
23321          /**
23322          * @event add
23323          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23324              * @param {Roo.form.ComboBox} combo This combo box
23325              */
23326         'add' : true,
23327         /**
23328          * @event edit
23329          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23330              * @param {Roo.form.ComboBox} combo This combo box
23331              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23332              */
23333         'edit' : true
23334         
23335         
23336     });
23337     if(this.transform){
23338         this.allowDomMove = false;
23339         var s = Roo.getDom(this.transform);
23340         if(!this.hiddenName){
23341             this.hiddenName = s.name;
23342         }
23343         if(!this.store){
23344             this.mode = 'local';
23345             var d = [], opts = s.options;
23346             for(var i = 0, len = opts.length;i < len; i++){
23347                 var o = opts[i];
23348                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23349                 if(o.selected) {
23350                     this.value = value;
23351                 }
23352                 d.push([value, o.text]);
23353             }
23354             this.store = new Roo.data.SimpleStore({
23355                 'id': 0,
23356                 fields: ['value', 'text'],
23357                 data : d
23358             });
23359             this.valueField = 'value';
23360             this.displayField = 'text';
23361         }
23362         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23363         if(!this.lazyRender){
23364             this.target = true;
23365             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23366             s.parentNode.removeChild(s); // remove it
23367             this.render(this.el.parentNode);
23368         }else{
23369             s.parentNode.removeChild(s); // remove it
23370         }
23371
23372     }
23373     if (this.store) {
23374         this.store = Roo.factory(this.store, Roo.data);
23375     }
23376     
23377     this.selectedIndex = -1;
23378     if(this.mode == 'local'){
23379         if(config.queryDelay === undefined){
23380             this.queryDelay = 10;
23381         }
23382         if(config.minChars === undefined){
23383             this.minChars = 0;
23384         }
23385     }
23386 };
23387
23388 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23389     /**
23390      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23391      */
23392     /**
23393      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23394      * rendering into an Roo.Editor, defaults to false)
23395      */
23396     /**
23397      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23398      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23399      */
23400     /**
23401      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23402      */
23403     /**
23404      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23405      * the dropdown list (defaults to undefined, with no header element)
23406      */
23407
23408      /**
23409      * @cfg {String/Roo.Template} tpl The template to use to render the output
23410      */
23411      
23412     // private
23413     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23414     /**
23415      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23416      */
23417     listWidth: undefined,
23418     /**
23419      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23420      * mode = 'remote' or 'text' if mode = 'local')
23421      */
23422     displayField: undefined,
23423     /**
23424      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23425      * mode = 'remote' or 'value' if mode = 'local'). 
23426      * Note: use of a valueField requires the user make a selection
23427      * in order for a value to be mapped.
23428      */
23429     valueField: undefined,
23430     
23431     
23432     /**
23433      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23434      * field's data value (defaults to the underlying DOM element's name)
23435      */
23436     hiddenName: undefined,
23437     /**
23438      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23439      */
23440     listClass: '',
23441     /**
23442      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23443      */
23444     selectedClass: 'x-combo-selected',
23445     /**
23446      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23447      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23448      * which displays a downward arrow icon).
23449      */
23450     triggerClass : 'x-form-arrow-trigger',
23451     /**
23452      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23453      */
23454     shadow:'sides',
23455     /**
23456      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23457      * anchor positions (defaults to 'tl-bl')
23458      */
23459     listAlign: 'tl-bl?',
23460     /**
23461      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23462      */
23463     maxHeight: 300,
23464     /**
23465      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23466      * query specified by the allQuery config option (defaults to 'query')
23467      */
23468     triggerAction: 'query',
23469     /**
23470      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23471      * (defaults to 4, does not apply if editable = false)
23472      */
23473     minChars : 4,
23474     /**
23475      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23476      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23477      */
23478     typeAhead: false,
23479     /**
23480      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23481      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23482      */
23483     queryDelay: 500,
23484     /**
23485      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23486      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23487      */
23488     pageSize: 0,
23489     /**
23490      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23491      * when editable = true (defaults to false)
23492      */
23493     selectOnFocus:false,
23494     /**
23495      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23496      */
23497     queryParam: 'query',
23498     /**
23499      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23500      * when mode = 'remote' (defaults to 'Loading...')
23501      */
23502     loadingText: 'Loading...',
23503     /**
23504      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23505      */
23506     resizable: false,
23507     /**
23508      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23509      */
23510     handleHeight : 8,
23511     /**
23512      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23513      * traditional select (defaults to true)
23514      */
23515     editable: true,
23516     /**
23517      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23518      */
23519     allQuery: '',
23520     /**
23521      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23522      */
23523     mode: 'remote',
23524     /**
23525      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23526      * listWidth has a higher value)
23527      */
23528     minListWidth : 70,
23529     /**
23530      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23531      * allow the user to set arbitrary text into the field (defaults to false)
23532      */
23533     forceSelection:false,
23534     /**
23535      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23536      * if typeAhead = true (defaults to 250)
23537      */
23538     typeAheadDelay : 250,
23539     /**
23540      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23541      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23542      */
23543     valueNotFoundText : undefined,
23544     /**
23545      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23546      */
23547     blockFocus : false,
23548     
23549     /**
23550      * @cfg {Boolean} disableClear Disable showing of clear button.
23551      */
23552     disableClear : false,
23553     /**
23554      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23555      */
23556     alwaysQuery : false,
23557     
23558     //private
23559     addicon : false,
23560     editicon: false,
23561     
23562     // element that contains real text value.. (when hidden is used..)
23563      
23564     // private
23565     onRender : function(ct, position){
23566         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23567         if(this.hiddenName){
23568             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23569                     'before', true);
23570             this.hiddenField.value =
23571                 this.hiddenValue !== undefined ? this.hiddenValue :
23572                 this.value !== undefined ? this.value : '';
23573
23574             // prevent input submission
23575             this.el.dom.removeAttribute('name');
23576              
23577              
23578         }
23579         if(Roo.isGecko){
23580             this.el.dom.setAttribute('autocomplete', 'off');
23581         }
23582
23583         var cls = 'x-combo-list';
23584
23585         this.list = new Roo.Layer({
23586             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23587         });
23588
23589         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23590         this.list.setWidth(lw);
23591         this.list.swallowEvent('mousewheel');
23592         this.assetHeight = 0;
23593
23594         if(this.title){
23595             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23596             this.assetHeight += this.header.getHeight();
23597         }
23598
23599         this.innerList = this.list.createChild({cls:cls+'-inner'});
23600         this.innerList.on('mouseover', this.onViewOver, this);
23601         this.innerList.on('mousemove', this.onViewMove, this);
23602         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23603         
23604         if(this.allowBlank && !this.pageSize && !this.disableClear){
23605             this.footer = this.list.createChild({cls:cls+'-ft'});
23606             this.pageTb = new Roo.Toolbar(this.footer);
23607            
23608         }
23609         if(this.pageSize){
23610             this.footer = this.list.createChild({cls:cls+'-ft'});
23611             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23612                     {pageSize: this.pageSize});
23613             
23614         }
23615         
23616         if (this.pageTb && this.allowBlank && !this.disableClear) {
23617             var _this = this;
23618             this.pageTb.add(new Roo.Toolbar.Fill(), {
23619                 cls: 'x-btn-icon x-btn-clear',
23620                 text: '&#160;',
23621                 handler: function()
23622                 {
23623                     _this.collapse();
23624                     _this.clearValue();
23625                     _this.onSelect(false, -1);
23626                 }
23627             });
23628         }
23629         if (this.footer) {
23630             this.assetHeight += this.footer.getHeight();
23631         }
23632         
23633
23634         if(!this.tpl){
23635             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23636         }
23637
23638         this.view = new Roo.View(this.innerList, this.tpl, {
23639             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23640         });
23641
23642         this.view.on('click', this.onViewClick, this);
23643
23644         this.store.on('beforeload', this.onBeforeLoad, this);
23645         this.store.on('load', this.onLoad, this);
23646         this.store.on('loadexception', this.onLoadException, this);
23647
23648         if(this.resizable){
23649             this.resizer = new Roo.Resizable(this.list,  {
23650                pinned:true, handles:'se'
23651             });
23652             this.resizer.on('resize', function(r, w, h){
23653                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23654                 this.listWidth = w;
23655                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23656                 this.restrictHeight();
23657             }, this);
23658             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23659         }
23660         if(!this.editable){
23661             this.editable = true;
23662             this.setEditable(false);
23663         }  
23664         
23665         
23666         if (typeof(this.events.add.listeners) != 'undefined') {
23667             
23668             this.addicon = this.wrap.createChild(
23669                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23670        
23671             this.addicon.on('click', function(e) {
23672                 this.fireEvent('add', this);
23673             }, this);
23674         }
23675         if (typeof(this.events.edit.listeners) != 'undefined') {
23676             
23677             this.editicon = this.wrap.createChild(
23678                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23679             if (this.addicon) {
23680                 this.editicon.setStyle('margin-left', '40px');
23681             }
23682             this.editicon.on('click', function(e) {
23683                 
23684                 // we fire even  if inothing is selected..
23685                 this.fireEvent('edit', this, this.lastData );
23686                 
23687             }, this);
23688         }
23689         
23690         
23691         
23692     },
23693
23694     // private
23695     initEvents : function(){
23696         Roo.form.ComboBox.superclass.initEvents.call(this);
23697
23698         this.keyNav = new Roo.KeyNav(this.el, {
23699             "up" : function(e){
23700                 this.inKeyMode = true;
23701                 this.selectPrev();
23702             },
23703
23704             "down" : function(e){
23705                 if(!this.isExpanded()){
23706                     this.onTriggerClick();
23707                 }else{
23708                     this.inKeyMode = true;
23709                     this.selectNext();
23710                 }
23711             },
23712
23713             "enter" : function(e){
23714                 this.onViewClick();
23715                 //return true;
23716             },
23717
23718             "esc" : function(e){
23719                 this.collapse();
23720             },
23721
23722             "tab" : function(e){
23723                 this.onViewClick(false);
23724                 this.fireEvent("specialkey", this, e);
23725                 return true;
23726             },
23727
23728             scope : this,
23729
23730             doRelay : function(foo, bar, hname){
23731                 if(hname == 'down' || this.scope.isExpanded()){
23732                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23733                 }
23734                 return true;
23735             },
23736
23737             forceKeyDown: true
23738         });
23739         this.queryDelay = Math.max(this.queryDelay || 10,
23740                 this.mode == 'local' ? 10 : 250);
23741         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23742         if(this.typeAhead){
23743             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23744         }
23745         if(this.editable !== false){
23746             this.el.on("keyup", this.onKeyUp, this);
23747         }
23748         if(this.forceSelection){
23749             this.on('blur', this.doForce, this);
23750         }
23751     },
23752
23753     onDestroy : function(){
23754         if(this.view){
23755             this.view.setStore(null);
23756             this.view.el.removeAllListeners();
23757             this.view.el.remove();
23758             this.view.purgeListeners();
23759         }
23760         if(this.list){
23761             this.list.destroy();
23762         }
23763         if(this.store){
23764             this.store.un('beforeload', this.onBeforeLoad, this);
23765             this.store.un('load', this.onLoad, this);
23766             this.store.un('loadexception', this.onLoadException, this);
23767         }
23768         Roo.form.ComboBox.superclass.onDestroy.call(this);
23769     },
23770
23771     // private
23772     fireKey : function(e){
23773         if(e.isNavKeyPress() && !this.list.isVisible()){
23774             this.fireEvent("specialkey", this, e);
23775         }
23776     },
23777
23778     // private
23779     onResize: function(w, h){
23780         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23781         
23782         if(typeof w != 'number'){
23783             // we do not handle it!?!?
23784             return;
23785         }
23786         var tw = this.trigger.getWidth();
23787         tw += this.addicon ? this.addicon.getWidth() : 0;
23788         tw += this.editicon ? this.editicon.getWidth() : 0;
23789         var x = w - tw;
23790         this.el.setWidth( this.adjustWidth('input', x));
23791             
23792         this.trigger.setStyle('left', x+'px');
23793         
23794         if(this.list && this.listWidth === undefined){
23795             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23796             this.list.setWidth(lw);
23797             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23798         }
23799         
23800     
23801         
23802     },
23803
23804     /**
23805      * Allow or prevent the user from directly editing the field text.  If false is passed,
23806      * the user will only be able to select from the items defined in the dropdown list.  This method
23807      * is the runtime equivalent of setting the 'editable' config option at config time.
23808      * @param {Boolean} value True to allow the user to directly edit the field text
23809      */
23810     setEditable : function(value){
23811         if(value == this.editable){
23812             return;
23813         }
23814         this.editable = value;
23815         if(!value){
23816             this.el.dom.setAttribute('readOnly', true);
23817             this.el.on('mousedown', this.onTriggerClick,  this);
23818             this.el.addClass('x-combo-noedit');
23819         }else{
23820             this.el.dom.setAttribute('readOnly', false);
23821             this.el.un('mousedown', this.onTriggerClick,  this);
23822             this.el.removeClass('x-combo-noedit');
23823         }
23824     },
23825
23826     // private
23827     onBeforeLoad : function(){
23828         if(!this.hasFocus){
23829             return;
23830         }
23831         this.innerList.update(this.loadingText ?
23832                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23833         this.restrictHeight();
23834         this.selectedIndex = -1;
23835     },
23836
23837     // private
23838     onLoad : function(){
23839         if(!this.hasFocus){
23840             return;
23841         }
23842         if(this.store.getCount() > 0){
23843             this.expand();
23844             this.restrictHeight();
23845             if(this.lastQuery == this.allQuery){
23846                 if(this.editable){
23847                     this.el.dom.select();
23848                 }
23849                 if(!this.selectByValue(this.value, true)){
23850                     this.select(0, true);
23851                 }
23852             }else{
23853                 this.selectNext();
23854                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23855                     this.taTask.delay(this.typeAheadDelay);
23856                 }
23857             }
23858         }else{
23859             this.onEmptyResults();
23860         }
23861         //this.el.focus();
23862     },
23863     // private
23864     onLoadException : function()
23865     {
23866         this.collapse();
23867         Roo.log(this.store.reader.jsonData);
23868         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23869             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23870         }
23871         
23872         
23873     },
23874     // private
23875     onTypeAhead : function(){
23876         if(this.store.getCount() > 0){
23877             var r = this.store.getAt(0);
23878             var newValue = r.data[this.displayField];
23879             var len = newValue.length;
23880             var selStart = this.getRawValue().length;
23881             if(selStart != len){
23882                 this.setRawValue(newValue);
23883                 this.selectText(selStart, newValue.length);
23884             }
23885         }
23886     },
23887
23888     // private
23889     onSelect : function(record, index){
23890         if(this.fireEvent('beforeselect', this, record, index) !== false){
23891             this.setFromData(index > -1 ? record.data : false);
23892             this.collapse();
23893             this.fireEvent('select', this, record, index);
23894         }
23895     },
23896
23897     /**
23898      * Returns the currently selected field value or empty string if no value is set.
23899      * @return {String} value The selected value
23900      */
23901     getValue : function(){
23902         if(this.valueField){
23903             return typeof this.value != 'undefined' ? this.value : '';
23904         }else{
23905             return Roo.form.ComboBox.superclass.getValue.call(this);
23906         }
23907     },
23908
23909     /**
23910      * Clears any text/value currently set in the field
23911      */
23912     clearValue : function(){
23913         if(this.hiddenField){
23914             this.hiddenField.value = '';
23915         }
23916         this.value = '';
23917         this.setRawValue('');
23918         this.lastSelectionText = '';
23919         this.applyEmptyText();
23920     },
23921
23922     /**
23923      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23924      * will be displayed in the field.  If the value does not match the data value of an existing item,
23925      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23926      * Otherwise the field will be blank (although the value will still be set).
23927      * @param {String} value The value to match
23928      */
23929     setValue : function(v){
23930         var text = v;
23931         if(this.valueField){
23932             var r = this.findRecord(this.valueField, v);
23933             if(r){
23934                 text = r.data[this.displayField];
23935             }else if(this.valueNotFoundText !== undefined){
23936                 text = this.valueNotFoundText;
23937             }
23938         }
23939         this.lastSelectionText = text;
23940         if(this.hiddenField){
23941             this.hiddenField.value = v;
23942         }
23943         Roo.form.ComboBox.superclass.setValue.call(this, text);
23944         this.value = v;
23945     },
23946     /**
23947      * @property {Object} the last set data for the element
23948      */
23949     
23950     lastData : false,
23951     /**
23952      * Sets the value of the field based on a object which is related to the record format for the store.
23953      * @param {Object} value the value to set as. or false on reset?
23954      */
23955     setFromData : function(o){
23956         var dv = ''; // display value
23957         var vv = ''; // value value..
23958         this.lastData = o;
23959         if (this.displayField) {
23960             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23961         } else {
23962             // this is an error condition!!!
23963             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23964         }
23965         
23966         if(this.valueField){
23967             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23968         }
23969         if(this.hiddenField){
23970             this.hiddenField.value = vv;
23971             
23972             this.lastSelectionText = dv;
23973             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23974             this.value = vv;
23975             return;
23976         }
23977         // no hidden field.. - we store the value in 'value', but still display
23978         // display field!!!!
23979         this.lastSelectionText = dv;
23980         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23981         this.value = vv;
23982         
23983         
23984     },
23985     // private
23986     reset : function(){
23987         // overridden so that last data is reset..
23988         this.setValue(this.originalValue);
23989         this.clearInvalid();
23990         this.lastData = false;
23991     },
23992     // private
23993     findRecord : function(prop, value){
23994         var record;
23995         if(this.store.getCount() > 0){
23996             this.store.each(function(r){
23997                 if(r.data[prop] == value){
23998                     record = r;
23999                     return false;
24000                 }
24001                 return true;
24002             });
24003         }
24004         return record;
24005     },
24006     
24007     getName: function()
24008     {
24009         // returns hidden if it's set..
24010         if (!this.rendered) {return ''};
24011         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24012         
24013     },
24014     // private
24015     onViewMove : function(e, t){
24016         this.inKeyMode = false;
24017     },
24018
24019     // private
24020     onViewOver : function(e, t){
24021         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24022             return;
24023         }
24024         var item = this.view.findItemFromChild(t);
24025         if(item){
24026             var index = this.view.indexOf(item);
24027             this.select(index, false);
24028         }
24029     },
24030
24031     // private
24032     onViewClick : function(doFocus)
24033     {
24034         var index = this.view.getSelectedIndexes()[0];
24035         var r = this.store.getAt(index);
24036         if(r){
24037             this.onSelect(r, index);
24038         }
24039         if(doFocus !== false && !this.blockFocus){
24040             this.el.focus();
24041         }
24042     },
24043
24044     // private
24045     restrictHeight : function(){
24046         this.innerList.dom.style.height = '';
24047         var inner = this.innerList.dom;
24048         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24049         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24050         this.list.beginUpdate();
24051         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24052         this.list.alignTo(this.el, this.listAlign);
24053         this.list.endUpdate();
24054     },
24055
24056     // private
24057     onEmptyResults : function(){
24058         this.collapse();
24059     },
24060
24061     /**
24062      * Returns true if the dropdown list is expanded, else false.
24063      */
24064     isExpanded : function(){
24065         return this.list.isVisible();
24066     },
24067
24068     /**
24069      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24070      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24071      * @param {String} value The data value of the item to select
24072      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24073      * selected item if it is not currently in view (defaults to true)
24074      * @return {Boolean} True if the value matched an item in the list, else false
24075      */
24076     selectByValue : function(v, scrollIntoView){
24077         if(v !== undefined && v !== null){
24078             var r = this.findRecord(this.valueField || this.displayField, v);
24079             if(r){
24080                 this.select(this.store.indexOf(r), scrollIntoView);
24081                 return true;
24082             }
24083         }
24084         return false;
24085     },
24086
24087     /**
24088      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24089      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24090      * @param {Number} index The zero-based index of the list item to select
24091      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24092      * selected item if it is not currently in view (defaults to true)
24093      */
24094     select : function(index, scrollIntoView){
24095         this.selectedIndex = index;
24096         this.view.select(index);
24097         if(scrollIntoView !== false){
24098             var el = this.view.getNode(index);
24099             if(el){
24100                 this.innerList.scrollChildIntoView(el, false);
24101             }
24102         }
24103     },
24104
24105     // private
24106     selectNext : function(){
24107         var ct = this.store.getCount();
24108         if(ct > 0){
24109             if(this.selectedIndex == -1){
24110                 this.select(0);
24111             }else if(this.selectedIndex < ct-1){
24112                 this.select(this.selectedIndex+1);
24113             }
24114         }
24115     },
24116
24117     // private
24118     selectPrev : function(){
24119         var ct = this.store.getCount();
24120         if(ct > 0){
24121             if(this.selectedIndex == -1){
24122                 this.select(0);
24123             }else if(this.selectedIndex != 0){
24124                 this.select(this.selectedIndex-1);
24125             }
24126         }
24127     },
24128
24129     // private
24130     onKeyUp : function(e){
24131         if(this.editable !== false && !e.isSpecialKey()){
24132             this.lastKey = e.getKey();
24133             this.dqTask.delay(this.queryDelay);
24134         }
24135     },
24136
24137     // private
24138     validateBlur : function(){
24139         return !this.list || !this.list.isVisible();   
24140     },
24141
24142     // private
24143     initQuery : function(){
24144         this.doQuery(this.getRawValue());
24145     },
24146
24147     // private
24148     doForce : function(){
24149         if(this.el.dom.value.length > 0){
24150             this.el.dom.value =
24151                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24152             this.applyEmptyText();
24153         }
24154     },
24155
24156     /**
24157      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24158      * query allowing the query action to be canceled if needed.
24159      * @param {String} query The SQL query to execute
24160      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24161      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24162      * saved in the current store (defaults to false)
24163      */
24164     doQuery : function(q, forceAll){
24165         if(q === undefined || q === null){
24166             q = '';
24167         }
24168         var qe = {
24169             query: q,
24170             forceAll: forceAll,
24171             combo: this,
24172             cancel:false
24173         };
24174         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24175             return false;
24176         }
24177         q = qe.query;
24178         forceAll = qe.forceAll;
24179         if(forceAll === true || (q.length >= this.minChars)){
24180             if(this.lastQuery != q || this.alwaysQuery){
24181                 this.lastQuery = q;
24182                 if(this.mode == 'local'){
24183                     this.selectedIndex = -1;
24184                     if(forceAll){
24185                         this.store.clearFilter();
24186                     }else{
24187                         this.store.filter(this.displayField, q);
24188                     }
24189                     this.onLoad();
24190                 }else{
24191                     this.store.baseParams[this.queryParam] = q;
24192                     this.store.load({
24193                         params: this.getParams(q)
24194                     });
24195                     this.expand();
24196                 }
24197             }else{
24198                 this.selectedIndex = -1;
24199                 this.onLoad();   
24200             }
24201         }
24202     },
24203
24204     // private
24205     getParams : function(q){
24206         var p = {};
24207         //p[this.queryParam] = q;
24208         if(this.pageSize){
24209             p.start = 0;
24210             p.limit = this.pageSize;
24211         }
24212         return p;
24213     },
24214
24215     /**
24216      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24217      */
24218     collapse : function(){
24219         if(!this.isExpanded()){
24220             return;
24221         }
24222         this.list.hide();
24223         Roo.get(document).un('mousedown', this.collapseIf, this);
24224         Roo.get(document).un('mousewheel', this.collapseIf, this);
24225         if (!this.editable) {
24226             Roo.get(document).un('keydown', this.listKeyPress, this);
24227         }
24228         this.fireEvent('collapse', this);
24229     },
24230
24231     // private
24232     collapseIf : function(e){
24233         if(!e.within(this.wrap) && !e.within(this.list)){
24234             this.collapse();
24235         }
24236     },
24237
24238     /**
24239      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24240      */
24241     expand : function(){
24242         if(this.isExpanded() || !this.hasFocus){
24243             return;
24244         }
24245         this.list.alignTo(this.el, this.listAlign);
24246         this.list.show();
24247         Roo.get(document).on('mousedown', this.collapseIf, this);
24248         Roo.get(document).on('mousewheel', this.collapseIf, this);
24249         if (!this.editable) {
24250             Roo.get(document).on('keydown', this.listKeyPress, this);
24251         }
24252         
24253         this.fireEvent('expand', this);
24254     },
24255
24256     // private
24257     // Implements the default empty TriggerField.onTriggerClick function
24258     onTriggerClick : function(){
24259         if(this.disabled){
24260             return;
24261         }
24262         if(this.isExpanded()){
24263             this.collapse();
24264             if (!this.blockFocus) {
24265                 this.el.focus();
24266             }
24267             
24268         }else {
24269             this.hasFocus = true;
24270             if(this.triggerAction == 'all') {
24271                 this.doQuery(this.allQuery, true);
24272             } else {
24273                 this.doQuery(this.getRawValue());
24274             }
24275             if (!this.blockFocus) {
24276                 this.el.focus();
24277             }
24278         }
24279     },
24280     listKeyPress : function(e)
24281     {
24282         //Roo.log('listkeypress');
24283         // scroll to first matching element based on key pres..
24284         if (e.isSpecialKey()) {
24285             return false;
24286         }
24287         var k = String.fromCharCode(e.getKey()).toUpperCase();
24288         //Roo.log(k);
24289         var match  = false;
24290         var csel = this.view.getSelectedNodes();
24291         var cselitem = false;
24292         if (csel.length) {
24293             var ix = this.view.indexOf(csel[0]);
24294             cselitem  = this.store.getAt(ix);
24295             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24296                 cselitem = false;
24297             }
24298             
24299         }
24300         
24301         this.store.each(function(v) { 
24302             if (cselitem) {
24303                 // start at existing selection.
24304                 if (cselitem.id == v.id) {
24305                     cselitem = false;
24306                 }
24307                 return;
24308             }
24309                 
24310             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24311                 match = this.store.indexOf(v);
24312                 return false;
24313             }
24314         }, this);
24315         
24316         if (match === false) {
24317             return true; // no more action?
24318         }
24319         // scroll to?
24320         this.view.select(match);
24321         var sn = Roo.get(this.view.getSelectedNodes()[0])
24322         sn.scrollIntoView(sn.dom.parentNode, false);
24323     }
24324
24325     /** 
24326     * @cfg {Boolean} grow 
24327     * @hide 
24328     */
24329     /** 
24330     * @cfg {Number} growMin 
24331     * @hide 
24332     */
24333     /** 
24334     * @cfg {Number} growMax 
24335     * @hide 
24336     */
24337     /**
24338      * @hide
24339      * @method autoSize
24340      */
24341 });/*
24342  * Copyright(c) 2010-2012, Roo J Solutions Limited
24343  *
24344  * Licence LGPL
24345  *
24346  */
24347
24348 /**
24349  * @class Roo.form.ComboBoxArray
24350  * @extends Roo.form.TextField
24351  * A facebook style adder... for lists of email / people / countries  etc...
24352  * pick multiple items from a combo box, and shows each one.
24353  *
24354  *  Fred [x]  Brian [x]  [Pick another |v]
24355  *
24356  *
24357  *  For this to work: it needs various extra information
24358  *    - normal combo problay has
24359  *      name, hiddenName
24360  *    + displayField, valueField
24361  *
24362  *    For our purpose...
24363  *
24364  *
24365  *   If we change from 'extends' to wrapping...
24366  *   
24367  *  
24368  *
24369  
24370  
24371  * @constructor
24372  * Create a new ComboBoxArray.
24373  * @param {Object} config Configuration options
24374  */
24375  
24376
24377 Roo.form.ComboBoxArray = function(config)
24378 {
24379     
24380     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24381     
24382     this.items = new Roo.util.MixedCollection(false);
24383     
24384     // construct the child combo...
24385     
24386     
24387     
24388     
24389    
24390     
24391 }
24392
24393  
24394 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24395
24396     /**
24397      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24398      */
24399     
24400     lastData : false,
24401     
24402     // behavies liek a hiddne field
24403     inputType:      'hidden',
24404     /**
24405      * @cfg {Number} width The width of the box that displays the selected element
24406      */ 
24407     width:          300,
24408
24409     
24410     
24411     /**
24412      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24413      */
24414     name : false,
24415     /**
24416      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24417      */
24418     hiddenName : false,
24419     
24420     
24421     // private the array of items that are displayed..
24422     items  : false,
24423     // private - the hidden field el.
24424     hiddenEl : false,
24425     // private - the filed el..
24426     el : false,
24427     
24428     //validateValue : function() { return true; }, // all values are ok!
24429     //onAddClick: function() { },
24430     
24431     onRender : function(ct, position) 
24432     {
24433         
24434         // create the standard hidden element
24435         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24436         
24437         
24438         // give fake names to child combo;
24439         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24440         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24441         
24442         this.combo = Roo.factory(this.combo, Roo.form);
24443         this.combo.onRender(ct, position);
24444         
24445         // assigned so form know we need to do this..
24446         this.store          = this.combo.store;
24447         this.valueField     = this.combo.valueField;
24448         this.displayField   = this.combo.displayField ;
24449         
24450         
24451         this.combo.wrap.addClass('x-cbarray-grp');
24452         
24453         var cbwrap = this.combo.wrap.createChild(
24454             {tag: 'div', cls: 'x-cbarray-cb'},
24455             this.combo.el.dom
24456         );
24457         
24458              
24459         this.hiddenEl = this.combo.wrap.createChild({
24460             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24461         });
24462         this.el = this.combo.wrap.createChild({
24463             tag: 'input',  type:'hidden' , name: this.name, value : ''
24464         });
24465          //   this.el.dom.removeAttribute("name");
24466         
24467         
24468         this.outerWrap = this.combo.wrap;
24469         this.wrap = cbwrap;
24470         
24471         this.outerWrap.setWidth(this.width);
24472         this.outerWrap.dom.removeChild(this.el.dom);
24473         
24474         this.wrap.dom.appendChild(this.el.dom);
24475         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24476         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24477         
24478         this.combo.trigger.setStyle('position','relative');
24479         this.combo.trigger.setStyle('left', '0px');
24480         this.combo.trigger.setStyle('top', '2px');
24481         
24482         this.combo.el.setStyle('vertical-align', 'text-bottom');
24483         
24484         //this.trigger.setStyle('vertical-align', 'top');
24485         
24486         // this should use the code from combo really... on('add' ....)
24487         if (this.adder) {
24488             
24489         
24490             this.adder = this.outerWrap.createChild(
24491                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24492             var _t = this;
24493             this.adder.on('click', function(e) {
24494                 _t.fireEvent('adderclick', this, e);
24495             }, _t);
24496         }
24497         //var _t = this;
24498         //this.adder.on('click', this.onAddClick, _t);
24499         
24500         
24501         this.combo.on('select', function(cb, rec, ix) {
24502             this.addItem(rec.data);
24503             
24504             cb.setValue('');
24505             cb.el.dom.value = '';
24506             //cb.lastData = rec.data;
24507             // add to list
24508             
24509         }, this);
24510         
24511         
24512     },
24513     
24514     
24515     getName: function()
24516     {
24517         // returns hidden if it's set..
24518         if (!this.rendered) {return ''};
24519         return  this.hiddenName ? this.hiddenName : this.name;
24520         
24521     },
24522     
24523     
24524     onResize: function(w, h){
24525         
24526         return;
24527         // not sure if this is needed..
24528         //this.combo.onResize(w,h);
24529         
24530         if(typeof w != 'number'){
24531             // we do not handle it!?!?
24532             return;
24533         }
24534         var tw = this.combo.trigger.getWidth();
24535         tw += this.addicon ? this.addicon.getWidth() : 0;
24536         tw += this.editicon ? this.editicon.getWidth() : 0;
24537         var x = w - tw;
24538         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24539             
24540         this.combo.trigger.setStyle('left', '0px');
24541         
24542         if(this.list && this.listWidth === undefined){
24543             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24544             this.list.setWidth(lw);
24545             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24546         }
24547         
24548     
24549         
24550     },
24551     
24552     addItem: function(rec)
24553     {
24554         var valueField = this.combo.valueField;
24555         var displayField = this.combo.displayField;
24556         if (this.items.indexOfKey(rec[valueField]) > -1) {
24557             //console.log("GOT " + rec.data.id);
24558             return;
24559         }
24560         
24561         var x = new Roo.form.ComboBoxArray.Item({
24562             //id : rec[this.idField],
24563             data : rec,
24564             displayField : displayField ,
24565             tipField : displayField ,
24566             cb : this
24567         });
24568         // use the 
24569         this.items.add(rec[valueField],x);
24570         // add it before the element..
24571         this.updateHiddenEl();
24572         x.render(this.outerWrap, this.wrap.dom);
24573         // add the image handler..
24574     },
24575     
24576     updateHiddenEl : function()
24577     {
24578         this.validate();
24579         if (!this.hiddenEl) {
24580             return;
24581         }
24582         var ar = [];
24583         var idField = this.combo.valueField;
24584         
24585         this.items.each(function(f) {
24586             ar.push(f.data[idField]);
24587            
24588         });
24589         this.hiddenEl.dom.value = ar.join(',');
24590         this.validate();
24591     },
24592     
24593     reset : function()
24594     {
24595         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24596         this.items.each(function(f) {
24597            f.remove(); 
24598         });
24599         this.el.dom.value = '';
24600         if (this.hiddenEl) {
24601             this.hiddenEl.dom.value = '';
24602         }
24603         
24604     },
24605     getValue: function()
24606     {
24607         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24608     },
24609     setValue: function(v) // not a valid action - must use addItems..
24610     {
24611          
24612         this.reset();
24613         
24614         
24615         
24616         if (this.store.isLocal && (typeof(v) == 'string')) {
24617             // then we can use the store to find the values..
24618             // comma seperated at present.. this needs to allow JSON based encoding..
24619             this.hiddenEl.value  = v;
24620             var v_ar = [];
24621             Roo.each(v.split(','), function(k) {
24622                 Roo.log("CHECK " + this.valueField + ',' + k);
24623                 var li = this.store.query(this.valueField, k);
24624                 if (!li.length) {
24625                     return;
24626                 }
24627                 add = {};
24628                 add[this.valueField] = k;
24629                 add[this.displayField] = li.item(0).data[this.displayField];
24630                 
24631                 this.addItem(add);
24632             }, this) 
24633              
24634         }
24635         if (typeof(v) == 'object') {
24636             // then let's assume it's an array of objects..
24637             Roo.each(v, function(l) {
24638                 this.addItem(l);
24639             }, this);
24640              
24641         }
24642         
24643         
24644     },
24645     setFromData: function(v)
24646     {
24647         // this recieves an object, if setValues is called.
24648         this.reset();
24649         this.el.dom.value = v[this.displayField];
24650         this.hiddenEl.dom.value = v[this.valueField];
24651         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24652             return;
24653         }
24654         var kv = v[this.valueField];
24655         var dv = v[this.displayField];
24656         kv = typeof(kv) != 'string' ? '' : kv;
24657         dv = typeof(dv) != 'string' ? '' : dv;
24658         
24659         
24660         var keys = kv.split(',');
24661         var display = dv.split(',');
24662         for (var i = 0 ; i < keys.length; i++) {
24663             
24664             add = {};
24665             add[this.valueField] = keys[i];
24666             add[this.displayField] = display[i];
24667             this.addItem(add);
24668         }
24669       
24670         
24671     },
24672     
24673     
24674     validateValue : function(value){
24675         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24676         
24677     }
24678     
24679 });
24680
24681
24682
24683 /**
24684  * @class Roo.form.ComboBoxArray.Item
24685  * @extends Roo.BoxComponent
24686  * A selected item in the list
24687  *  Fred [x]  Brian [x]  [Pick another |v]
24688  * 
24689  * @constructor
24690  * Create a new item.
24691  * @param {Object} config Configuration options
24692  */
24693  
24694 Roo.form.ComboBoxArray.Item = function(config) {
24695     config.id = Roo.id();
24696     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24697 }
24698
24699 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24700     data : {},
24701     cb: false,
24702     displayField : false,
24703     tipField : false,
24704     
24705     
24706     defaultAutoCreate : {
24707         tag: 'div',
24708         cls: 'x-cbarray-item',
24709         cn : [ 
24710             { tag: 'div' },
24711             {
24712                 tag: 'img',
24713                 width:16,
24714                 height : 16,
24715                 src : Roo.BLANK_IMAGE_URL ,
24716                 align: 'center'
24717             }
24718         ]
24719         
24720     },
24721     
24722  
24723     onRender : function(ct, position)
24724     {
24725         Roo.form.Field.superclass.onRender.call(this, ct, position);
24726         
24727         if(!this.el){
24728             var cfg = this.getAutoCreate();
24729             this.el = ct.createChild(cfg, position);
24730         }
24731         
24732         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24733         
24734         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24735             this.cb.renderer(this.data) :
24736             String.format('{0}',this.data[this.displayField]);
24737         
24738             
24739         this.el.child('div').dom.setAttribute('qtip',
24740                         String.format('{0}',this.data[this.tipField])
24741         );
24742         
24743         this.el.child('img').on('click', this.remove, this);
24744         
24745     },
24746    
24747     remove : function()
24748     {
24749         
24750         this.cb.items.remove(this);
24751         this.el.child('img').un('click', this.remove, this);
24752         this.el.remove();
24753         this.cb.updateHiddenEl();
24754     }
24755     
24756     
24757 });/*
24758  * Based on:
24759  * Ext JS Library 1.1.1
24760  * Copyright(c) 2006-2007, Ext JS, LLC.
24761  *
24762  * Originally Released Under LGPL - original licence link has changed is not relivant.
24763  *
24764  * Fork - LGPL
24765  * <script type="text/javascript">
24766  */
24767 /**
24768  * @class Roo.form.Checkbox
24769  * @extends Roo.form.Field
24770  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24771  * @constructor
24772  * Creates a new Checkbox
24773  * @param {Object} config Configuration options
24774  */
24775 Roo.form.Checkbox = function(config){
24776     Roo.form.Checkbox.superclass.constructor.call(this, config);
24777     this.addEvents({
24778         /**
24779          * @event check
24780          * Fires when the checkbox is checked or unchecked.
24781              * @param {Roo.form.Checkbox} this This checkbox
24782              * @param {Boolean} checked The new checked value
24783              */
24784         check : true
24785     });
24786 };
24787
24788 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24789     /**
24790      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24791      */
24792     focusClass : undefined,
24793     /**
24794      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24795      */
24796     fieldClass: "x-form-field",
24797     /**
24798      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24799      */
24800     checked: false,
24801     /**
24802      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24803      * {tag: "input", type: "checkbox", autocomplete: "off"})
24804      */
24805     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24806     /**
24807      * @cfg {String} boxLabel The text that appears beside the checkbox
24808      */
24809     boxLabel : "",
24810     /**
24811      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24812      */  
24813     inputValue : '1',
24814     /**
24815      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24816      */
24817      valueOff: '0', // value when not checked..
24818
24819     actionMode : 'viewEl', 
24820     //
24821     // private
24822     itemCls : 'x-menu-check-item x-form-item',
24823     groupClass : 'x-menu-group-item',
24824     inputType : 'hidden',
24825     
24826     
24827     inSetChecked: false, // check that we are not calling self...
24828     
24829     inputElement: false, // real input element?
24830     basedOn: false, // ????
24831     
24832     isFormField: true, // not sure where this is needed!!!!
24833
24834     onResize : function(){
24835         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24836         if(!this.boxLabel){
24837             this.el.alignTo(this.wrap, 'c-c');
24838         }
24839     },
24840
24841     initEvents : function(){
24842         Roo.form.Checkbox.superclass.initEvents.call(this);
24843         this.el.on("click", this.onClick,  this);
24844         this.el.on("change", this.onClick,  this);
24845     },
24846
24847
24848     getResizeEl : function(){
24849         return this.wrap;
24850     },
24851
24852     getPositionEl : function(){
24853         return this.wrap;
24854     },
24855
24856     // private
24857     onRender : function(ct, position){
24858         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24859         /*
24860         if(this.inputValue !== undefined){
24861             this.el.dom.value = this.inputValue;
24862         }
24863         */
24864         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24865         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24866         var viewEl = this.wrap.createChild({ 
24867             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24868         this.viewEl = viewEl;   
24869         this.wrap.on('click', this.onClick,  this); 
24870         
24871         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24872         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24873         
24874         
24875         
24876         if(this.boxLabel){
24877             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24878         //    viewEl.on('click', this.onClick,  this); 
24879         }
24880         //if(this.checked){
24881             this.setChecked(this.checked);
24882         //}else{
24883             //this.checked = this.el.dom;
24884         //}
24885
24886     },
24887
24888     // private
24889     initValue : Roo.emptyFn,
24890
24891     /**
24892      * Returns the checked state of the checkbox.
24893      * @return {Boolean} True if checked, else false
24894      */
24895     getValue : function(){
24896         if(this.el){
24897             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24898         }
24899         return this.valueOff;
24900         
24901     },
24902
24903         // private
24904     onClick : function(){ 
24905         this.setChecked(!this.checked);
24906
24907         //if(this.el.dom.checked != this.checked){
24908         //    this.setValue(this.el.dom.checked);
24909        // }
24910     },
24911
24912     /**
24913      * Sets the checked state of the checkbox.
24914      * On is always based on a string comparison between inputValue and the param.
24915      * @param {Boolean/String} value - the value to set 
24916      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24917      */
24918     setValue : function(v,suppressEvent){
24919         
24920         
24921         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24922         //if(this.el && this.el.dom){
24923         //    this.el.dom.checked = this.checked;
24924         //    this.el.dom.defaultChecked = this.checked;
24925         //}
24926         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24927         //this.fireEvent("check", this, this.checked);
24928     },
24929     // private..
24930     setChecked : function(state,suppressEvent)
24931     {
24932         if (this.inSetChecked) {
24933             this.checked = state;
24934             return;
24935         }
24936         
24937     
24938         if(this.wrap){
24939             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24940         }
24941         this.checked = state;
24942         if(suppressEvent !== true){
24943             this.fireEvent('check', this, state);
24944         }
24945         this.inSetChecked = true;
24946         this.el.dom.value = state ? this.inputValue : this.valueOff;
24947         this.inSetChecked = false;
24948         
24949     },
24950     // handle setting of hidden value by some other method!!?!?
24951     setFromHidden: function()
24952     {
24953         if(!this.el){
24954             return;
24955         }
24956         //console.log("SET FROM HIDDEN");
24957         //alert('setFrom hidden');
24958         this.setValue(this.el.dom.value);
24959     },
24960     
24961     onDestroy : function()
24962     {
24963         if(this.viewEl){
24964             Roo.get(this.viewEl).remove();
24965         }
24966          
24967         Roo.form.Checkbox.superclass.onDestroy.call(this);
24968     }
24969
24970 });/*
24971  * Based on:
24972  * Ext JS Library 1.1.1
24973  * Copyright(c) 2006-2007, Ext JS, LLC.
24974  *
24975  * Originally Released Under LGPL - original licence link has changed is not relivant.
24976  *
24977  * Fork - LGPL
24978  * <script type="text/javascript">
24979  */
24980  
24981 /**
24982  * @class Roo.form.Radio
24983  * @extends Roo.form.Checkbox
24984  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24985  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24986  * @constructor
24987  * Creates a new Radio
24988  * @param {Object} config Configuration options
24989  */
24990 Roo.form.Radio = function(){
24991     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24992 };
24993 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24994     inputType: 'radio',
24995
24996     /**
24997      * If this radio is part of a group, it will return the selected value
24998      * @return {String}
24999      */
25000     getGroupValue : function(){
25001         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25002     }
25003 });//<script type="text/javascript">
25004
25005 /*
25006  * Ext JS Library 1.1.1
25007  * Copyright(c) 2006-2007, Ext JS, LLC.
25008  * licensing@extjs.com
25009  * 
25010  * http://www.extjs.com/license
25011  */
25012  
25013  /*
25014   * 
25015   * Known bugs:
25016   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25017   * - IE ? - no idea how much works there.
25018   * 
25019   * 
25020   * 
25021   */
25022  
25023
25024 /**
25025  * @class Ext.form.HtmlEditor
25026  * @extends Ext.form.Field
25027  * Provides a lightweight HTML Editor component.
25028  *
25029  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25030  * 
25031  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25032  * supported by this editor.</b><br/><br/>
25033  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25034  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25035  */
25036 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25037       /**
25038      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25039      */
25040     toolbars : false,
25041     /**
25042      * @cfg {String} createLinkText The default text for the create link prompt
25043      */
25044     createLinkText : 'Please enter the URL for the link:',
25045     /**
25046      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25047      */
25048     defaultLinkValue : 'http:/'+'/',
25049    
25050      /**
25051      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25052      *                        Roo.resizable.
25053      */
25054     resizable : false,
25055      /**
25056      * @cfg {Number} height (in pixels)
25057      */   
25058     height: 300,
25059    /**
25060      * @cfg {Number} width (in pixels)
25061      */   
25062     width: 500,
25063     
25064     /**
25065      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25066      * 
25067      */
25068     stylesheets: false,
25069     
25070     // id of frame..
25071     frameId: false,
25072     
25073     // private properties
25074     validationEvent : false,
25075     deferHeight: true,
25076     initialized : false,
25077     activated : false,
25078     sourceEditMode : false,
25079     onFocus : Roo.emptyFn,
25080     iframePad:3,
25081     hideMode:'offsets',
25082     
25083     defaultAutoCreate : { // modified by initCompnoent..
25084         tag: "textarea",
25085         style:"width:500px;height:300px;",
25086         autocomplete: "off"
25087     },
25088
25089     // private
25090     initComponent : function(){
25091         this.addEvents({
25092             /**
25093              * @event initialize
25094              * Fires when the editor is fully initialized (including the iframe)
25095              * @param {HtmlEditor} this
25096              */
25097             initialize: true,
25098             /**
25099              * @event activate
25100              * Fires when the editor is first receives the focus. Any insertion must wait
25101              * until after this event.
25102              * @param {HtmlEditor} this
25103              */
25104             activate: true,
25105              /**
25106              * @event beforesync
25107              * Fires before the textarea is updated with content from the editor iframe. Return false
25108              * to cancel the sync.
25109              * @param {HtmlEditor} this
25110              * @param {String} html
25111              */
25112             beforesync: true,
25113              /**
25114              * @event beforepush
25115              * Fires before the iframe editor is updated with content from the textarea. Return false
25116              * to cancel the push.
25117              * @param {HtmlEditor} this
25118              * @param {String} html
25119              */
25120             beforepush: true,
25121              /**
25122              * @event sync
25123              * Fires when the textarea is updated with content from the editor iframe.
25124              * @param {HtmlEditor} this
25125              * @param {String} html
25126              */
25127             sync: true,
25128              /**
25129              * @event push
25130              * Fires when the iframe editor is updated with content from the textarea.
25131              * @param {HtmlEditor} this
25132              * @param {String} html
25133              */
25134             push: true,
25135              /**
25136              * @event editmodechange
25137              * Fires when the editor switches edit modes
25138              * @param {HtmlEditor} this
25139              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25140              */
25141             editmodechange: true,
25142             /**
25143              * @event editorevent
25144              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25145              * @param {HtmlEditor} this
25146              */
25147             editorevent: true
25148         });
25149         this.defaultAutoCreate =  {
25150             tag: "textarea",
25151             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25152             autocomplete: "off"
25153         };
25154     },
25155
25156     /**
25157      * Protected method that will not generally be called directly. It
25158      * is called when the editor creates its toolbar. Override this method if you need to
25159      * add custom toolbar buttons.
25160      * @param {HtmlEditor} editor
25161      */
25162     createToolbar : function(editor){
25163         if (!editor.toolbars || !editor.toolbars.length) {
25164             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25165         }
25166         
25167         for (var i =0 ; i < editor.toolbars.length;i++) {
25168             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
25169             editor.toolbars[i].init(editor);
25170         }
25171          
25172         
25173     },
25174
25175     /**
25176      * Protected method that will not generally be called directly. It
25177      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25178      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25179      */
25180     getDocMarkup : function(){
25181         // body styles..
25182         var st = '';
25183         if (this.stylesheets === false) {
25184             
25185             Roo.get(document.head).select('style').each(function(node) {
25186                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25187             });
25188             
25189             Roo.get(document.head).select('link').each(function(node) { 
25190                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25191             });
25192             
25193         } else if (!this.stylesheets.length) {
25194                 // simple..
25195                 st = '<style type="text/css">' +
25196                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25197                    '</style>';
25198         } else {
25199             Roo.each(this.stylesheets, function(s) {
25200                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25201             });
25202             
25203         }
25204         
25205         st +=  '<style type="text/css">' +
25206             'IMG { cursor: pointer } ' +
25207         '</style>';
25208
25209         
25210         return '<html><head>' + st  +
25211             //<style type="text/css">' +
25212             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25213             //'</style>' +
25214             ' </head><body class="roo-htmleditor-body"></body></html>';
25215     },
25216
25217     // private
25218     onRender : function(ct, position)
25219     {
25220         var _t = this;
25221         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25222         this.el.dom.style.border = '0 none';
25223         this.el.dom.setAttribute('tabIndex', -1);
25224         this.el.addClass('x-hidden');
25225         if(Roo.isIE){ // fix IE 1px bogus margin
25226             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25227         }
25228         this.wrap = this.el.wrap({
25229             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25230         });
25231         
25232         if (this.resizable) {
25233             this.resizeEl = new Roo.Resizable(this.wrap, {
25234                 pinned : true,
25235                 wrap: true,
25236                 dynamic : true,
25237                 minHeight : this.height,
25238                 height: this.height,
25239                 handles : this.resizable,
25240                 width: this.width,
25241                 listeners : {
25242                     resize : function(r, w, h) {
25243                         _t.onResize(w,h); // -something
25244                     }
25245                 }
25246             });
25247             
25248         }
25249
25250         this.frameId = Roo.id();
25251         
25252         this.createToolbar(this);
25253         
25254       
25255         
25256         var iframe = this.wrap.createChild({
25257             tag: 'iframe',
25258             id: this.frameId,
25259             name: this.frameId,
25260             frameBorder : 'no',
25261             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25262         }, this.el
25263         );
25264         
25265        // console.log(iframe);
25266         //this.wrap.dom.appendChild(iframe);
25267
25268         this.iframe = iframe.dom;
25269
25270          this.assignDocWin();
25271         
25272         this.doc.designMode = 'on';
25273        
25274         this.doc.open();
25275         this.doc.write(this.getDocMarkup());
25276         this.doc.close();
25277
25278         
25279         var task = { // must defer to wait for browser to be ready
25280             run : function(){
25281                 //console.log("run task?" + this.doc.readyState);
25282                 this.assignDocWin();
25283                 if(this.doc.body || this.doc.readyState == 'complete'){
25284                     try {
25285                         this.doc.designMode="on";
25286                     } catch (e) {
25287                         return;
25288                     }
25289                     Roo.TaskMgr.stop(task);
25290                     this.initEditor.defer(10, this);
25291                 }
25292             },
25293             interval : 10,
25294             duration:10000,
25295             scope: this
25296         };
25297         Roo.TaskMgr.start(task);
25298
25299         if(!this.width){
25300             this.setSize(this.wrap.getSize());
25301         }
25302         if (this.resizeEl) {
25303             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25304             // should trigger onReize..
25305         }
25306     },
25307
25308     // private
25309     onResize : function(w, h)
25310     {
25311         //Roo.log('resize: ' +w + ',' + h );
25312         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25313         if(this.el && this.iframe){
25314             if(typeof w == 'number'){
25315                 var aw = w - this.wrap.getFrameWidth('lr');
25316                 this.el.setWidth(this.adjustWidth('textarea', aw));
25317                 this.iframe.style.width = aw + 'px';
25318             }
25319             if(typeof h == 'number'){
25320                 var tbh = 0;
25321                 for (var i =0; i < this.toolbars.length;i++) {
25322                     // fixme - ask toolbars for heights?
25323                     tbh += this.toolbars[i].tb.el.getHeight();
25324                     if (this.toolbars[i].footer) {
25325                         tbh += this.toolbars[i].footer.el.getHeight();
25326                     }
25327                 }
25328                 
25329                 
25330                 
25331                 
25332                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25333                 ah -= 5; // knock a few pixes off for look..
25334                 this.el.setHeight(this.adjustWidth('textarea', ah));
25335                 this.iframe.style.height = ah + 'px';
25336                 if(this.doc){
25337                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25338                 }
25339             }
25340         }
25341     },
25342
25343     /**
25344      * Toggles the editor between standard and source edit mode.
25345      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25346      */
25347     toggleSourceEdit : function(sourceEditMode){
25348         
25349         this.sourceEditMode = sourceEditMode === true;
25350         
25351         if(this.sourceEditMode){
25352           
25353             this.syncValue();
25354             this.iframe.className = 'x-hidden';
25355             this.el.removeClass('x-hidden');
25356             this.el.dom.removeAttribute('tabIndex');
25357             this.el.focus();
25358         }else{
25359              
25360             this.pushValue();
25361             this.iframe.className = '';
25362             this.el.addClass('x-hidden');
25363             this.el.dom.setAttribute('tabIndex', -1);
25364             this.deferFocus();
25365         }
25366         this.setSize(this.wrap.getSize());
25367         this.fireEvent('editmodechange', this, this.sourceEditMode);
25368     },
25369
25370     // private used internally
25371     createLink : function(){
25372         var url = prompt(this.createLinkText, this.defaultLinkValue);
25373         if(url && url != 'http:/'+'/'){
25374             this.relayCmd('createlink', url);
25375         }
25376     },
25377
25378     // private (for BoxComponent)
25379     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25380
25381     // private (for BoxComponent)
25382     getResizeEl : function(){
25383         return this.wrap;
25384     },
25385
25386     // private (for BoxComponent)
25387     getPositionEl : function(){
25388         return this.wrap;
25389     },
25390
25391     // private
25392     initEvents : function(){
25393         this.originalValue = this.getValue();
25394     },
25395
25396     /**
25397      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25398      * @method
25399      */
25400     markInvalid : Roo.emptyFn,
25401     /**
25402      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25403      * @method
25404      */
25405     clearInvalid : Roo.emptyFn,
25406
25407     setValue : function(v){
25408         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25409         this.pushValue();
25410     },
25411
25412     /**
25413      * Protected method that will not generally be called directly. If you need/want
25414      * custom HTML cleanup, this is the method you should override.
25415      * @param {String} html The HTML to be cleaned
25416      * return {String} The cleaned HTML
25417      */
25418     cleanHtml : function(html){
25419         html = String(html);
25420         if(html.length > 5){
25421             if(Roo.isSafari){ // strip safari nonsense
25422                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25423             }
25424         }
25425         if(html == '&nbsp;'){
25426             html = '';
25427         }
25428         return html;
25429     },
25430
25431     /**
25432      * Protected method that will not generally be called directly. Syncs the contents
25433      * of the editor iframe with the textarea.
25434      */
25435     syncValue : function(){
25436         if(this.initialized){
25437             var bd = (this.doc.body || this.doc.documentElement);
25438             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25439             var html = bd.innerHTML;
25440             if(Roo.isSafari){
25441                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25442                 var m = bs.match(/text-align:(.*?);/i);
25443                 if(m && m[1]){
25444                     html = '<div style="'+m[0]+'">' + html + '</div>';
25445                 }
25446             }
25447             html = this.cleanHtml(html);
25448             // fix up the special chars..
25449             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25450                 return "&#"+b.charCodeAt()+";" 
25451             });
25452             if(this.fireEvent('beforesync', this, html) !== false){
25453                 this.el.dom.value = html;
25454                 this.fireEvent('sync', this, html);
25455             }
25456         }
25457     },
25458
25459     /**
25460      * Protected method that will not generally be called directly. Pushes the value of the textarea
25461      * into the iframe editor.
25462      */
25463     pushValue : function(){
25464         if(this.initialized){
25465             var v = this.el.dom.value;
25466             if(v.length < 1){
25467                 v = '&#160;';
25468             }
25469             
25470             if(this.fireEvent('beforepush', this, v) !== false){
25471                 var d = (this.doc.body || this.doc.documentElement);
25472                 d.innerHTML = v;
25473                 this.cleanUpPaste();
25474                 this.el.dom.value = d.innerHTML;
25475                 this.fireEvent('push', this, v);
25476             }
25477         }
25478     },
25479
25480     // private
25481     deferFocus : function(){
25482         this.focus.defer(10, this);
25483     },
25484
25485     // doc'ed in Field
25486     focus : function(){
25487         if(this.win && !this.sourceEditMode){
25488             this.win.focus();
25489         }else{
25490             this.el.focus();
25491         }
25492     },
25493     
25494     assignDocWin: function()
25495     {
25496         var iframe = this.iframe;
25497         
25498          if(Roo.isIE){
25499             this.doc = iframe.contentWindow.document;
25500             this.win = iframe.contentWindow;
25501         } else {
25502             if (!Roo.get(this.frameId)) {
25503                 return;
25504             }
25505             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25506             this.win = Roo.get(this.frameId).dom.contentWindow;
25507         }
25508     },
25509     
25510     // private
25511     initEditor : function(){
25512         //console.log("INIT EDITOR");
25513         this.assignDocWin();
25514         
25515         
25516         
25517         this.doc.designMode="on";
25518         this.doc.open();
25519         this.doc.write(this.getDocMarkup());
25520         this.doc.close();
25521         
25522         var dbody = (this.doc.body || this.doc.documentElement);
25523         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25524         // this copies styles from the containing element into thsi one..
25525         // not sure why we need all of this..
25526         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25527         ss['background-attachment'] = 'fixed'; // w3c
25528         dbody.bgProperties = 'fixed'; // ie
25529         Roo.DomHelper.applyStyles(dbody, ss);
25530         Roo.EventManager.on(this.doc, {
25531             //'mousedown': this.onEditorEvent,
25532             'mouseup': this.onEditorEvent,
25533             'dblclick': this.onEditorEvent,
25534             'click': this.onEditorEvent,
25535             'keyup': this.onEditorEvent,
25536             buffer:100,
25537             scope: this
25538         });
25539         if(Roo.isGecko){
25540             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25541         }
25542         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25543             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25544         }
25545         this.initialized = true;
25546
25547         this.fireEvent('initialize', this);
25548         this.pushValue();
25549     },
25550
25551     // private
25552     onDestroy : function(){
25553         
25554         
25555         
25556         if(this.rendered){
25557             
25558             for (var i =0; i < this.toolbars.length;i++) {
25559                 // fixme - ask toolbars for heights?
25560                 this.toolbars[i].onDestroy();
25561             }
25562             
25563             this.wrap.dom.innerHTML = '';
25564             this.wrap.remove();
25565         }
25566     },
25567
25568     // private
25569     onFirstFocus : function(){
25570         
25571         this.assignDocWin();
25572         
25573         
25574         this.activated = true;
25575         for (var i =0; i < this.toolbars.length;i++) {
25576             this.toolbars[i].onFirstFocus();
25577         }
25578        
25579         if(Roo.isGecko){ // prevent silly gecko errors
25580             this.win.focus();
25581             var s = this.win.getSelection();
25582             if(!s.focusNode || s.focusNode.nodeType != 3){
25583                 var r = s.getRangeAt(0);
25584                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25585                 r.collapse(true);
25586                 this.deferFocus();
25587             }
25588             try{
25589                 this.execCmd('useCSS', true);
25590                 this.execCmd('styleWithCSS', false);
25591             }catch(e){}
25592         }
25593         this.fireEvent('activate', this);
25594     },
25595
25596     // private
25597     adjustFont: function(btn){
25598         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25599         //if(Roo.isSafari){ // safari
25600         //    adjust *= 2;
25601        // }
25602         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25603         if(Roo.isSafari){ // safari
25604             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25605             v =  (v < 10) ? 10 : v;
25606             v =  (v > 48) ? 48 : v;
25607             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25608             
25609         }
25610         
25611         
25612         v = Math.max(1, v+adjust);
25613         
25614         this.execCmd('FontSize', v  );
25615     },
25616
25617     onEditorEvent : function(e){
25618         this.fireEvent('editorevent', this, e);
25619       //  this.updateToolbar();
25620         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25621     },
25622
25623     insertTag : function(tg)
25624     {
25625         // could be a bit smarter... -> wrap the current selected tRoo..
25626         
25627         this.execCmd("formatblock",   tg);
25628         
25629     },
25630     
25631     insertText : function(txt)
25632     {
25633         
25634         
25635         range = this.createRange();
25636         range.deleteContents();
25637                //alert(Sender.getAttribute('label'));
25638                
25639         range.insertNode(this.doc.createTextNode(txt));
25640     } ,
25641     
25642     // private
25643     relayBtnCmd : function(btn){
25644         this.relayCmd(btn.cmd);
25645     },
25646
25647     /**
25648      * Executes a Midas editor command on the editor document and performs necessary focus and
25649      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25650      * @param {String} cmd The Midas command
25651      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25652      */
25653     relayCmd : function(cmd, value){
25654         this.win.focus();
25655         this.execCmd(cmd, value);
25656         this.fireEvent('editorevent', this);
25657         //this.updateToolbar();
25658         this.deferFocus();
25659     },
25660
25661     /**
25662      * Executes a Midas editor command directly on the editor document.
25663      * For visual commands, you should use {@link #relayCmd} instead.
25664      * <b>This should only be called after the editor is initialized.</b>
25665      * @param {String} cmd The Midas command
25666      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25667      */
25668     execCmd : function(cmd, value){
25669         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25670         this.syncValue();
25671     },
25672  
25673  
25674    
25675     /**
25676      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25677      * to insert tRoo.
25678      * @param {String} text | dom node.. 
25679      */
25680     insertAtCursor : function(text)
25681     {
25682         
25683         
25684         
25685         if(!this.activated){
25686             return;
25687         }
25688         /*
25689         if(Roo.isIE){
25690             this.win.focus();
25691             var r = this.doc.selection.createRange();
25692             if(r){
25693                 r.collapse(true);
25694                 r.pasteHTML(text);
25695                 this.syncValue();
25696                 this.deferFocus();
25697             
25698             }
25699             return;
25700         }
25701         */
25702         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25703             this.win.focus();
25704             
25705             
25706             // from jquery ui (MIT licenced)
25707             var range, node;
25708             var win = this.win;
25709             
25710             if (win.getSelection && win.getSelection().getRangeAt) {
25711                 range = win.getSelection().getRangeAt(0);
25712                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25713                 range.insertNode(node);
25714             } else if (win.document.selection && win.document.selection.createRange) {
25715                 // no firefox support
25716                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25717                 win.document.selection.createRange().pasteHTML(txt);
25718             } else {
25719                 // no firefox support
25720                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25721                 this.execCmd('InsertHTML', txt);
25722             } 
25723             
25724             this.syncValue();
25725             
25726             this.deferFocus();
25727         }
25728     },
25729  // private
25730     mozKeyPress : function(e){
25731         if(e.ctrlKey){
25732             var c = e.getCharCode(), cmd;
25733           
25734             if(c > 0){
25735                 c = String.fromCharCode(c).toLowerCase();
25736                 switch(c){
25737                     case 'b':
25738                         cmd = 'bold';
25739                         break;
25740                     case 'i':
25741                         cmd = 'italic';
25742                         break;
25743                     
25744                     case 'u':
25745                         cmd = 'underline';
25746                         break;
25747                     
25748                     case 'v':
25749                         this.cleanUpPaste.defer(100, this);
25750                         return;
25751                         
25752                 }
25753                 if(cmd){
25754                     this.win.focus();
25755                     this.execCmd(cmd);
25756                     this.deferFocus();
25757                     e.preventDefault();
25758                 }
25759                 
25760             }
25761         }
25762     },
25763
25764     // private
25765     fixKeys : function(){ // load time branching for fastest keydown performance
25766         if(Roo.isIE){
25767             return function(e){
25768                 var k = e.getKey(), r;
25769                 if(k == e.TAB){
25770                     e.stopEvent();
25771                     r = this.doc.selection.createRange();
25772                     if(r){
25773                         r.collapse(true);
25774                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25775                         this.deferFocus();
25776                     }
25777                     return;
25778                 }
25779                 
25780                 if(k == e.ENTER){
25781                     r = this.doc.selection.createRange();
25782                     if(r){
25783                         var target = r.parentElement();
25784                         if(!target || target.tagName.toLowerCase() != 'li'){
25785                             e.stopEvent();
25786                             r.pasteHTML('<br />');
25787                             r.collapse(false);
25788                             r.select();
25789                         }
25790                     }
25791                 }
25792                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25793                     this.cleanUpPaste.defer(100, this);
25794                     return;
25795                 }
25796                 
25797                 
25798             };
25799         }else if(Roo.isOpera){
25800             return function(e){
25801                 var k = e.getKey();
25802                 if(k == e.TAB){
25803                     e.stopEvent();
25804                     this.win.focus();
25805                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25806                     this.deferFocus();
25807                 }
25808                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25809                     this.cleanUpPaste.defer(100, this);
25810                     return;
25811                 }
25812                 
25813             };
25814         }else if(Roo.isSafari){
25815             return function(e){
25816                 var k = e.getKey();
25817                 
25818                 if(k == e.TAB){
25819                     e.stopEvent();
25820                     this.execCmd('InsertText','\t');
25821                     this.deferFocus();
25822                     return;
25823                 }
25824                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25825                     this.cleanUpPaste.defer(100, this);
25826                     return;
25827                 }
25828                 
25829              };
25830         }
25831     }(),
25832     
25833     getAllAncestors: function()
25834     {
25835         var p = this.getSelectedNode();
25836         var a = [];
25837         if (!p) {
25838             a.push(p); // push blank onto stack..
25839             p = this.getParentElement();
25840         }
25841         
25842         
25843         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25844             a.push(p);
25845             p = p.parentNode;
25846         }
25847         a.push(this.doc.body);
25848         return a;
25849     },
25850     lastSel : false,
25851     lastSelNode : false,
25852     
25853     
25854     getSelection : function() 
25855     {
25856         this.assignDocWin();
25857         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25858     },
25859     
25860     getSelectedNode: function() 
25861     {
25862         // this may only work on Gecko!!!
25863         
25864         // should we cache this!!!!
25865         
25866         
25867         
25868          
25869         var range = this.createRange(this.getSelection()).cloneRange();
25870         
25871         if (Roo.isIE) {
25872             var parent = range.parentElement();
25873             while (true) {
25874                 var testRange = range.duplicate();
25875                 testRange.moveToElementText(parent);
25876                 if (testRange.inRange(range)) {
25877                     break;
25878                 }
25879                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25880                     break;
25881                 }
25882                 parent = parent.parentElement;
25883             }
25884             return parent;
25885         }
25886         
25887         // is ancestor a text element.
25888         var ac =  range.commonAncestorContainer;
25889         if (ac.nodeType == 3) {
25890             ac = ac.parentNode;
25891         }
25892         
25893         var ar = ac.childNodes;
25894          
25895         var nodes = [];
25896         var other_nodes = [];
25897         var has_other_nodes = false;
25898         for (var i=0;i<ar.length;i++) {
25899             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25900                 continue;
25901             }
25902             // fullly contained node.
25903             
25904             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25905                 nodes.push(ar[i]);
25906                 continue;
25907             }
25908             
25909             // probably selected..
25910             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25911                 other_nodes.push(ar[i]);
25912                 continue;
25913             }
25914             // outer..
25915             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25916                 continue;
25917             }
25918             
25919             
25920             has_other_nodes = true;
25921         }
25922         if (!nodes.length && other_nodes.length) {
25923             nodes= other_nodes;
25924         }
25925         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25926             return false;
25927         }
25928         
25929         return nodes[0];
25930     },
25931     createRange: function(sel)
25932     {
25933         // this has strange effects when using with 
25934         // top toolbar - not sure if it's a great idea.
25935         //this.editor.contentWindow.focus();
25936         if (typeof sel != "undefined") {
25937             try {
25938                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25939             } catch(e) {
25940                 return this.doc.createRange();
25941             }
25942         } else {
25943             return this.doc.createRange();
25944         }
25945     },
25946     getParentElement: function()
25947     {
25948         
25949         this.assignDocWin();
25950         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25951         
25952         var range = this.createRange(sel);
25953          
25954         try {
25955             var p = range.commonAncestorContainer;
25956             while (p.nodeType == 3) { // text node
25957                 p = p.parentNode;
25958             }
25959             return p;
25960         } catch (e) {
25961             return null;
25962         }
25963     
25964     },
25965     /***
25966      *
25967      * Range intersection.. the hard stuff...
25968      *  '-1' = before
25969      *  '0' = hits..
25970      *  '1' = after.
25971      *         [ -- selected range --- ]
25972      *   [fail]                        [fail]
25973      *
25974      *    basically..
25975      *      if end is before start or  hits it. fail.
25976      *      if start is after end or hits it fail.
25977      *
25978      *   if either hits (but other is outside. - then it's not 
25979      *   
25980      *    
25981      **/
25982     
25983     
25984     // @see http://www.thismuchiknow.co.uk/?p=64.
25985     rangeIntersectsNode : function(range, node)
25986     {
25987         var nodeRange = node.ownerDocument.createRange();
25988         try {
25989             nodeRange.selectNode(node);
25990         } catch (e) {
25991             nodeRange.selectNodeContents(node);
25992         }
25993     
25994         var rangeStartRange = range.cloneRange();
25995         rangeStartRange.collapse(true);
25996     
25997         var rangeEndRange = range.cloneRange();
25998         rangeEndRange.collapse(false);
25999     
26000         var nodeStartRange = nodeRange.cloneRange();
26001         nodeStartRange.collapse(true);
26002     
26003         var nodeEndRange = nodeRange.cloneRange();
26004         nodeEndRange.collapse(false);
26005     
26006         return rangeStartRange.compareBoundaryPoints(
26007                  Range.START_TO_START, nodeEndRange) == -1 &&
26008                rangeEndRange.compareBoundaryPoints(
26009                  Range.START_TO_START, nodeStartRange) == 1;
26010         
26011          
26012     },
26013     rangeCompareNode : function(range, node)
26014     {
26015         var nodeRange = node.ownerDocument.createRange();
26016         try {
26017             nodeRange.selectNode(node);
26018         } catch (e) {
26019             nodeRange.selectNodeContents(node);
26020         }
26021         
26022         
26023         range.collapse(true);
26024     
26025         nodeRange.collapse(true);
26026      
26027         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26028         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26029          
26030         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26031         
26032         var nodeIsBefore   =  ss == 1;
26033         var nodeIsAfter    = ee == -1;
26034         
26035         if (nodeIsBefore && nodeIsAfter)
26036             return 0; // outer
26037         if (!nodeIsBefore && nodeIsAfter)
26038             return 1; //right trailed.
26039         
26040         if (nodeIsBefore && !nodeIsAfter)
26041             return 2;  // left trailed.
26042         // fully contined.
26043         return 3;
26044     },
26045
26046     // private? - in a new class?
26047     cleanUpPaste :  function()
26048     {
26049         // cleans up the whole document..
26050          Roo.log('cleanuppaste');
26051         this.cleanUpChildren(this.doc.body);
26052         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26053         if (clean != this.doc.body.innerHTML) {
26054             this.doc.body.innerHTML = clean;
26055         }
26056         
26057     },
26058     
26059     cleanWordChars : function(input) {
26060         var he = Roo.form.HtmlEditor;
26061     
26062         var output = input;
26063         Roo.each(he.swapCodes, function(sw) { 
26064         
26065             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26066             output = output.replace(swapper, sw[1]);
26067         });
26068         return output;
26069     },
26070     
26071     
26072     cleanUpChildren : function (n)
26073     {
26074         if (!n.childNodes.length) {
26075             return;
26076         }
26077         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26078            this.cleanUpChild(n.childNodes[i]);
26079         }
26080     },
26081     
26082     
26083         
26084     
26085     cleanUpChild : function (node)
26086     {
26087         //console.log(node);
26088         if (node.nodeName == "#text") {
26089             // clean up silly Windows -- stuff?
26090             return; 
26091         }
26092         if (node.nodeName == "#comment") {
26093             node.parentNode.removeChild(node);
26094             // clean up silly Windows -- stuff?
26095             return; 
26096         }
26097         
26098         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26099             // remove node.
26100             node.parentNode.removeChild(node);
26101             return;
26102             
26103         }
26104         
26105         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26106         
26107         // remove <a name=....> as rendering on yahoo mailer is bored with this.
26108         
26109         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26110             remove_keep_children = true;
26111         }
26112         
26113         if (remove_keep_children) {
26114             this.cleanUpChildren(node);
26115             // inserts everything just before this node...
26116             while (node.childNodes.length) {
26117                 var cn = node.childNodes[0];
26118                 node.removeChild(cn);
26119                 node.parentNode.insertBefore(cn, node);
26120             }
26121             node.parentNode.removeChild(node);
26122             return;
26123         }
26124         
26125         if (!node.attributes || !node.attributes.length) {
26126             this.cleanUpChildren(node);
26127             return;
26128         }
26129         
26130         function cleanAttr(n,v)
26131         {
26132             
26133             if (v.match(/^\./) || v.match(/^\//)) {
26134                 return;
26135             }
26136             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26137                 return;
26138             }
26139             if (v.match(/^#/)) {
26140                 return;
26141             }
26142             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
26143             node.removeAttribute(n);
26144             
26145         }
26146         
26147         function cleanStyle(n,v)
26148         {
26149             if (v.match(/expression/)) { //XSS?? should we even bother..
26150                 node.removeAttribute(n);
26151                 return;
26152             }
26153             
26154             
26155             var parts = v.split(/;/);
26156             Roo.each(parts, function(p) {
26157                 p = p.replace(/\s+/g,'');
26158                 if (!p.length) {
26159                     return true;
26160                 }
26161                 var l = p.split(':').shift().replace(/\s+/g,'');
26162                 
26163                 // only allow 'c whitelisted system attributes'
26164                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
26165                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
26166                     node.removeAttribute(n);
26167                     return false;
26168                 }
26169                 return true;
26170             });
26171             
26172             
26173         }
26174         
26175         
26176         for (var i = node.attributes.length-1; i > -1 ; i--) {
26177             var a = node.attributes[i];
26178             //console.log(a);
26179             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26180                 node.removeAttribute(a.name);
26181                 continue;
26182             }
26183             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26184                 cleanAttr(a.name,a.value); // fixme..
26185                 continue;
26186             }
26187             if (a.name == 'style') {
26188                 cleanStyle(a.name,a.value);
26189                 continue;
26190             }
26191             /// clean up MS crap..
26192             // tecnically this should be a list of valid class'es..
26193             
26194             
26195             if (a.name == 'class') {
26196                 if (a.value.match(/^Mso/)) {
26197                     node.className = '';
26198                 }
26199                 
26200                 if (a.value.match(/body/)) {
26201                     node.className = '';
26202                 }
26203                 continue;
26204             }
26205             
26206             // style cleanup!?
26207             // class cleanup?
26208             
26209         }
26210         
26211         
26212         this.cleanUpChildren(node);
26213         
26214         
26215     }
26216     
26217     
26218     // hide stuff that is not compatible
26219     /**
26220      * @event blur
26221      * @hide
26222      */
26223     /**
26224      * @event change
26225      * @hide
26226      */
26227     /**
26228      * @event focus
26229      * @hide
26230      */
26231     /**
26232      * @event specialkey
26233      * @hide
26234      */
26235     /**
26236      * @cfg {String} fieldClass @hide
26237      */
26238     /**
26239      * @cfg {String} focusClass @hide
26240      */
26241     /**
26242      * @cfg {String} autoCreate @hide
26243      */
26244     /**
26245      * @cfg {String} inputType @hide
26246      */
26247     /**
26248      * @cfg {String} invalidClass @hide
26249      */
26250     /**
26251      * @cfg {String} invalidText @hide
26252      */
26253     /**
26254      * @cfg {String} msgFx @hide
26255      */
26256     /**
26257      * @cfg {String} validateOnBlur @hide
26258      */
26259 });
26260
26261 Roo.form.HtmlEditor.white = [
26262         'area', 'br', 'img', 'input', 'hr', 'wbr',
26263         
26264        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26265        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26266        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26267        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26268        'table',   'ul',         'xmp', 
26269        
26270        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26271       'thead',   'tr', 
26272      
26273       'dir', 'menu', 'ol', 'ul', 'dl',
26274        
26275       'embed',  'object'
26276 ];
26277
26278
26279 Roo.form.HtmlEditor.black = [
26280     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26281         'applet', // 
26282         'base',   'basefont', 'bgsound', 'blink',  'body', 
26283         'frame',  'frameset', 'head',    'html',   'ilayer', 
26284         'iframe', 'layer',  'link',     'meta',    'object',   
26285         'script', 'style' ,'title',  'xml' // clean later..
26286 ];
26287 Roo.form.HtmlEditor.clean = [
26288     'script', 'style', 'title', 'xml'
26289 ];
26290 Roo.form.HtmlEditor.remove = [
26291     'font'
26292 ];
26293 // attributes..
26294
26295 Roo.form.HtmlEditor.ablack = [
26296     'on'
26297 ];
26298     
26299 Roo.form.HtmlEditor.aclean = [ 
26300     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26301 ];
26302
26303 // protocols..
26304 Roo.form.HtmlEditor.pwhite= [
26305         'http',  'https',  'mailto'
26306 ];
26307
26308 // white listed style attributes.
26309 Roo.form.HtmlEditor.cwhite= [
26310         'text-align',
26311         'font-size'
26312 ];
26313
26314
26315 Roo.form.HtmlEditor.swapCodes   =[ 
26316     [    8211, "--" ], 
26317     [    8212, "--" ], 
26318     [    8216,  "'" ],  
26319     [    8217, "'" ],  
26320     [    8220, '"' ],  
26321     [    8221, '"' ],  
26322     [    8226, "*" ],  
26323     [    8230, "..." ]
26324 ]; 
26325
26326     // <script type="text/javascript">
26327 /*
26328  * Based on
26329  * Ext JS Library 1.1.1
26330  * Copyright(c) 2006-2007, Ext JS, LLC.
26331  *  
26332  
26333  */
26334
26335 /**
26336  * @class Roo.form.HtmlEditorToolbar1
26337  * Basic Toolbar
26338  * 
26339  * Usage:
26340  *
26341  new Roo.form.HtmlEditor({
26342     ....
26343     toolbars : [
26344         new Roo.form.HtmlEditorToolbar1({
26345             disable : { fonts: 1 , format: 1, ..., ... , ...],
26346             btns : [ .... ]
26347         })
26348     }
26349      
26350  * 
26351  * @cfg {Object} disable List of elements to disable..
26352  * @cfg {Array} btns List of additional buttons.
26353  * 
26354  * 
26355  * NEEDS Extra CSS? 
26356  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26357  */
26358  
26359 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26360 {
26361     
26362     Roo.apply(this, config);
26363     
26364     // default disabled, based on 'good practice'..
26365     this.disable = this.disable || {};
26366     Roo.applyIf(this.disable, {
26367         fontSize : true,
26368         colors : true,
26369         specialElements : true
26370     });
26371     
26372     
26373     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26374     // dont call parent... till later.
26375 }
26376
26377 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26378     
26379     tb: false,
26380     
26381     rendered: false,
26382     
26383     editor : false,
26384     /**
26385      * @cfg {Object} disable  List of toolbar elements to disable
26386          
26387      */
26388     disable : false,
26389       /**
26390      * @cfg {Array} fontFamilies An array of available font families
26391      */
26392     fontFamilies : [
26393         'Arial',
26394         'Courier New',
26395         'Tahoma',
26396         'Times New Roman',
26397         'Verdana'
26398     ],
26399     
26400     specialChars : [
26401            "&#169;",
26402           "&#174;",     
26403           "&#8482;",    
26404           "&#163;" ,    
26405          // "&#8212;",    
26406           "&#8230;",    
26407           "&#247;" ,    
26408         //  "&#225;" ,     ?? a acute?
26409            "&#8364;"    , //Euro
26410        //   "&#8220;"    ,
26411         //  "&#8221;"    ,
26412         //  "&#8226;"    ,
26413           "&#176;"  //   , // degrees
26414
26415          // "&#233;"     , // e ecute
26416          // "&#250;"     , // u ecute?
26417     ],
26418     
26419     specialElements : [
26420         {
26421             text: "Insert Table",
26422             xtype: 'MenuItem',
26423             xns : Roo.Menu,
26424             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26425                 
26426         },
26427         {    
26428             text: "Insert Image",
26429             xtype: 'MenuItem',
26430             xns : Roo.Menu,
26431             ihtml : '<img src="about:blank"/>'
26432             
26433         }
26434         
26435          
26436     ],
26437     
26438     
26439     inputElements : [ 
26440             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26441             "input:submit", "input:button", "select", "textarea", "label" ],
26442     formats : [
26443         ["p"] ,  
26444         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26445         ["pre"],[ "code"], 
26446         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26447     ],
26448      /**
26449      * @cfg {String} defaultFont default font to use.
26450      */
26451     defaultFont: 'tahoma',
26452    
26453     fontSelect : false,
26454     
26455     
26456     formatCombo : false,
26457     
26458     init : function(editor)
26459     {
26460         this.editor = editor;
26461         
26462         
26463         var fid = editor.frameId;
26464         var etb = this;
26465         function btn(id, toggle, handler){
26466             var xid = fid + '-'+ id ;
26467             return {
26468                 id : xid,
26469                 cmd : id,
26470                 cls : 'x-btn-icon x-edit-'+id,
26471                 enableToggle:toggle !== false,
26472                 scope: editor, // was editor...
26473                 handler:handler||editor.relayBtnCmd,
26474                 clickEvent:'mousedown',
26475                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26476                 tabIndex:-1
26477             };
26478         }
26479         
26480         
26481         
26482         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26483         this.tb = tb;
26484          // stop form submits
26485         tb.el.on('click', function(e){
26486             e.preventDefault(); // what does this do?
26487         });
26488
26489         if(!this.disable.font && !Roo.isSafari){
26490             /* why no safari for fonts
26491             editor.fontSelect = tb.el.createChild({
26492                 tag:'select',
26493                 tabIndex: -1,
26494                 cls:'x-font-select',
26495                 html: editor.createFontOptions()
26496             });
26497             editor.fontSelect.on('change', function(){
26498                 var font = editor.fontSelect.dom.value;
26499                 editor.relayCmd('fontname', font);
26500                 editor.deferFocus();
26501             }, editor);
26502             tb.add(
26503                 editor.fontSelect.dom,
26504                 '-'
26505             );
26506             */
26507         };
26508         if(!this.disable.formats){
26509             this.formatCombo = new Roo.form.ComboBox({
26510                 store: new Roo.data.SimpleStore({
26511                     id : 'tag',
26512                     fields: ['tag'],
26513                     data : this.formats // from states.js
26514                 }),
26515                 blockFocus : true,
26516                 //autoCreate : {tag: "div",  size: "20"},
26517                 displayField:'tag',
26518                 typeAhead: false,
26519                 mode: 'local',
26520                 editable : false,
26521                 triggerAction: 'all',
26522                 emptyText:'Add tag',
26523                 selectOnFocus:true,
26524                 width:135,
26525                 listeners : {
26526                     'select': function(c, r, i) {
26527                         editor.insertTag(r.get('tag'));
26528                         editor.focus();
26529                     }
26530                 }
26531
26532             });
26533             tb.addField(this.formatCombo);
26534             
26535         }
26536         
26537         if(!this.disable.format){
26538             tb.add(
26539                 btn('bold'),
26540                 btn('italic'),
26541                 btn('underline')
26542             );
26543         };
26544         if(!this.disable.fontSize){
26545             tb.add(
26546                 '-',
26547                 
26548                 
26549                 btn('increasefontsize', false, editor.adjustFont),
26550                 btn('decreasefontsize', false, editor.adjustFont)
26551             );
26552         };
26553         
26554         
26555         if(!this.disable.colors){
26556             tb.add(
26557                 '-', {
26558                     id:editor.frameId +'-forecolor',
26559                     cls:'x-btn-icon x-edit-forecolor',
26560                     clickEvent:'mousedown',
26561                     tooltip: this.buttonTips['forecolor'] || undefined,
26562                     tabIndex:-1,
26563                     menu : new Roo.menu.ColorMenu({
26564                         allowReselect: true,
26565                         focus: Roo.emptyFn,
26566                         value:'000000',
26567                         plain:true,
26568                         selectHandler: function(cp, color){
26569                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26570                             editor.deferFocus();
26571                         },
26572                         scope: editor,
26573                         clickEvent:'mousedown'
26574                     })
26575                 }, {
26576                     id:editor.frameId +'backcolor',
26577                     cls:'x-btn-icon x-edit-backcolor',
26578                     clickEvent:'mousedown',
26579                     tooltip: this.buttonTips['backcolor'] || undefined,
26580                     tabIndex:-1,
26581                     menu : new Roo.menu.ColorMenu({
26582                         focus: Roo.emptyFn,
26583                         value:'FFFFFF',
26584                         plain:true,
26585                         allowReselect: true,
26586                         selectHandler: function(cp, color){
26587                             if(Roo.isGecko){
26588                                 editor.execCmd('useCSS', false);
26589                                 editor.execCmd('hilitecolor', color);
26590                                 editor.execCmd('useCSS', true);
26591                                 editor.deferFocus();
26592                             }else{
26593                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26594                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26595                                 editor.deferFocus();
26596                             }
26597                         },
26598                         scope:editor,
26599                         clickEvent:'mousedown'
26600                     })
26601                 }
26602             );
26603         };
26604         // now add all the items...
26605         
26606
26607         if(!this.disable.alignments){
26608             tb.add(
26609                 '-',
26610                 btn('justifyleft'),
26611                 btn('justifycenter'),
26612                 btn('justifyright')
26613             );
26614         };
26615
26616         //if(!Roo.isSafari){
26617             if(!this.disable.links){
26618                 tb.add(
26619                     '-',
26620                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26621                 );
26622             };
26623
26624             if(!this.disable.lists){
26625                 tb.add(
26626                     '-',
26627                     btn('insertorderedlist'),
26628                     btn('insertunorderedlist')
26629                 );
26630             }
26631             if(!this.disable.sourceEdit){
26632                 tb.add(
26633                     '-',
26634                     btn('sourceedit', true, function(btn){
26635                         this.toggleSourceEdit(btn.pressed);
26636                     })
26637                 );
26638             }
26639         //}
26640         
26641         var smenu = { };
26642         // special menu.. - needs to be tidied up..
26643         if (!this.disable.special) {
26644             smenu = {
26645                 text: "&#169;",
26646                 cls: 'x-edit-none',
26647                 
26648                 menu : {
26649                     items : []
26650                 }
26651             };
26652             for (var i =0; i < this.specialChars.length; i++) {
26653                 smenu.menu.items.push({
26654                     
26655                     html: this.specialChars[i],
26656                     handler: function(a,b) {
26657                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26658                         //editor.insertAtCursor(a.html);
26659                         
26660                     },
26661                     tabIndex:-1
26662                 });
26663             }
26664             
26665             
26666             tb.add(smenu);
26667             
26668             
26669         }
26670          
26671         if (!this.disable.specialElements) {
26672             var semenu = {
26673                 text: "Other;",
26674                 cls: 'x-edit-none',
26675                 menu : {
26676                     items : []
26677                 }
26678             };
26679             for (var i =0; i < this.specialElements.length; i++) {
26680                 semenu.menu.items.push(
26681                     Roo.apply({ 
26682                         handler: function(a,b) {
26683                             editor.insertAtCursor(this.ihtml);
26684                         }
26685                     }, this.specialElements[i])
26686                 );
26687                     
26688             }
26689             
26690             tb.add(semenu);
26691             
26692             
26693         }
26694          
26695         
26696         if (this.btns) {
26697             for(var i =0; i< this.btns.length;i++) {
26698                 var b = Roo.factory(this.btns[i],Roo.form);
26699                 b.cls =  'x-edit-none';
26700                 b.scope = editor;
26701                 tb.add(b);
26702             }
26703         
26704         }
26705         
26706         
26707         
26708         // disable everything...
26709         
26710         this.tb.items.each(function(item){
26711            if(item.id != editor.frameId+ '-sourceedit'){
26712                 item.disable();
26713             }
26714         });
26715         this.rendered = true;
26716         
26717         // the all the btns;
26718         editor.on('editorevent', this.updateToolbar, this);
26719         // other toolbars need to implement this..
26720         //editor.on('editmodechange', this.updateToolbar, this);
26721     },
26722     
26723     
26724     
26725     /**
26726      * Protected method that will not generally be called directly. It triggers
26727      * a toolbar update by reading the markup state of the current selection in the editor.
26728      */
26729     updateToolbar: function(){
26730
26731         if(!this.editor.activated){
26732             this.editor.onFirstFocus();
26733             return;
26734         }
26735
26736         var btns = this.tb.items.map, 
26737             doc = this.editor.doc,
26738             frameId = this.editor.frameId;
26739
26740         if(!this.disable.font && !Roo.isSafari){
26741             /*
26742             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26743             if(name != this.fontSelect.dom.value){
26744                 this.fontSelect.dom.value = name;
26745             }
26746             */
26747         }
26748         if(!this.disable.format){
26749             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26750             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26751             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26752         }
26753         if(!this.disable.alignments){
26754             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26755             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26756             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26757         }
26758         if(!Roo.isSafari && !this.disable.lists){
26759             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26760             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26761         }
26762         
26763         var ans = this.editor.getAllAncestors();
26764         if (this.formatCombo) {
26765             
26766             
26767             var store = this.formatCombo.store;
26768             this.formatCombo.setValue("");
26769             for (var i =0; i < ans.length;i++) {
26770                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26771                     // select it..
26772                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26773                     break;
26774                 }
26775             }
26776         }
26777         
26778         
26779         
26780         // hides menus... - so this cant be on a menu...
26781         Roo.menu.MenuMgr.hideAll();
26782
26783         //this.editorsyncValue();
26784     },
26785    
26786     
26787     createFontOptions : function(){
26788         var buf = [], fs = this.fontFamilies, ff, lc;
26789         for(var i = 0, len = fs.length; i< len; i++){
26790             ff = fs[i];
26791             lc = ff.toLowerCase();
26792             buf.push(
26793                 '<option value="',lc,'" style="font-family:',ff,';"',
26794                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26795                     ff,
26796                 '</option>'
26797             );
26798         }
26799         return buf.join('');
26800     },
26801     
26802     toggleSourceEdit : function(sourceEditMode){
26803         if(sourceEditMode === undefined){
26804             sourceEditMode = !this.sourceEditMode;
26805         }
26806         this.sourceEditMode = sourceEditMode === true;
26807         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26808         // just toggle the button?
26809         if(btn.pressed !== this.editor.sourceEditMode){
26810             btn.toggle(this.editor.sourceEditMode);
26811             return;
26812         }
26813         
26814         if(this.sourceEditMode){
26815             this.tb.items.each(function(item){
26816                 if(item.cmd != 'sourceedit'){
26817                     item.disable();
26818                 }
26819             });
26820           
26821         }else{
26822             if(this.initialized){
26823                 this.tb.items.each(function(item){
26824                     item.enable();
26825                 });
26826             }
26827             
26828         }
26829         // tell the editor that it's been pressed..
26830         this.editor.toggleSourceEdit(sourceEditMode);
26831        
26832     },
26833      /**
26834      * Object collection of toolbar tooltips for the buttons in the editor. The key
26835      * is the command id associated with that button and the value is a valid QuickTips object.
26836      * For example:
26837 <pre><code>
26838 {
26839     bold : {
26840         title: 'Bold (Ctrl+B)',
26841         text: 'Make the selected text bold.',
26842         cls: 'x-html-editor-tip'
26843     },
26844     italic : {
26845         title: 'Italic (Ctrl+I)',
26846         text: 'Make the selected text italic.',
26847         cls: 'x-html-editor-tip'
26848     },
26849     ...
26850 </code></pre>
26851     * @type Object
26852      */
26853     buttonTips : {
26854         bold : {
26855             title: 'Bold (Ctrl+B)',
26856             text: 'Make the selected text bold.',
26857             cls: 'x-html-editor-tip'
26858         },
26859         italic : {
26860             title: 'Italic (Ctrl+I)',
26861             text: 'Make the selected text italic.',
26862             cls: 'x-html-editor-tip'
26863         },
26864         underline : {
26865             title: 'Underline (Ctrl+U)',
26866             text: 'Underline the selected text.',
26867             cls: 'x-html-editor-tip'
26868         },
26869         increasefontsize : {
26870             title: 'Grow Text',
26871             text: 'Increase the font size.',
26872             cls: 'x-html-editor-tip'
26873         },
26874         decreasefontsize : {
26875             title: 'Shrink Text',
26876             text: 'Decrease the font size.',
26877             cls: 'x-html-editor-tip'
26878         },
26879         backcolor : {
26880             title: 'Text Highlight Color',
26881             text: 'Change the background color of the selected text.',
26882             cls: 'x-html-editor-tip'
26883         },
26884         forecolor : {
26885             title: 'Font Color',
26886             text: 'Change the color of the selected text.',
26887             cls: 'x-html-editor-tip'
26888         },
26889         justifyleft : {
26890             title: 'Align Text Left',
26891             text: 'Align text to the left.',
26892             cls: 'x-html-editor-tip'
26893         },
26894         justifycenter : {
26895             title: 'Center Text',
26896             text: 'Center text in the editor.',
26897             cls: 'x-html-editor-tip'
26898         },
26899         justifyright : {
26900             title: 'Align Text Right',
26901             text: 'Align text to the right.',
26902             cls: 'x-html-editor-tip'
26903         },
26904         insertunorderedlist : {
26905             title: 'Bullet List',
26906             text: 'Start a bulleted list.',
26907             cls: 'x-html-editor-tip'
26908         },
26909         insertorderedlist : {
26910             title: 'Numbered List',
26911             text: 'Start a numbered list.',
26912             cls: 'x-html-editor-tip'
26913         },
26914         createlink : {
26915             title: 'Hyperlink',
26916             text: 'Make the selected text a hyperlink.',
26917             cls: 'x-html-editor-tip'
26918         },
26919         sourceedit : {
26920             title: 'Source Edit',
26921             text: 'Switch to source editing mode.',
26922             cls: 'x-html-editor-tip'
26923         }
26924     },
26925     // private
26926     onDestroy : function(){
26927         if(this.rendered){
26928             
26929             this.tb.items.each(function(item){
26930                 if(item.menu){
26931                     item.menu.removeAll();
26932                     if(item.menu.el){
26933                         item.menu.el.destroy();
26934                     }
26935                 }
26936                 item.destroy();
26937             });
26938              
26939         }
26940     },
26941     onFirstFocus: function() {
26942         this.tb.items.each(function(item){
26943            item.enable();
26944         });
26945     }
26946 });
26947
26948
26949
26950
26951 // <script type="text/javascript">
26952 /*
26953  * Based on
26954  * Ext JS Library 1.1.1
26955  * Copyright(c) 2006-2007, Ext JS, LLC.
26956  *  
26957  
26958  */
26959
26960  
26961 /**
26962  * @class Roo.form.HtmlEditor.ToolbarContext
26963  * Context Toolbar
26964  * 
26965  * Usage:
26966  *
26967  new Roo.form.HtmlEditor({
26968     ....
26969     toolbars : [
26970         { xtype: 'ToolbarStandard', styles : {} }
26971         { xtype: 'ToolbarContext', disable : {} }
26972     ]
26973 })
26974
26975      
26976  * 
26977  * @config : {Object} disable List of elements to disable.. (not done yet.)
26978  * @config : {Object} styles  Map of styles available.
26979  * 
26980  */
26981
26982 Roo.form.HtmlEditor.ToolbarContext = function(config)
26983 {
26984     
26985     Roo.apply(this, config);
26986     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26987     // dont call parent... till later.
26988     this.styles = this.styles || {};
26989 }
26990 Roo.form.HtmlEditor.ToolbarContext.types = {
26991     'IMG' : {
26992         width : {
26993             title: "Width",
26994             width: 40
26995         },
26996         height:  {
26997             title: "Height",
26998             width: 40
26999         },
27000         align: {
27001             title: "Align",
27002             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27003             width : 80
27004             
27005         },
27006         border: {
27007             title: "Border",
27008             width: 40
27009         },
27010         alt: {
27011             title: "Alt",
27012             width: 120
27013         },
27014         src : {
27015             title: "Src",
27016             width: 220
27017         }
27018         
27019     },
27020     'A' : {
27021         name : {
27022             title: "Name",
27023             width: 50
27024         },
27025         href:  {
27026             title: "Href",
27027             width: 220
27028         } // border?
27029         
27030     },
27031     'TABLE' : {
27032         rows : {
27033             title: "Rows",
27034             width: 20
27035         },
27036         cols : {
27037             title: "Cols",
27038             width: 20
27039         },
27040         width : {
27041             title: "Width",
27042             width: 40
27043         },
27044         height : {
27045             title: "Height",
27046             width: 40
27047         },
27048         border : {
27049             title: "Border",
27050             width: 20
27051         }
27052     },
27053     'TD' : {
27054         width : {
27055             title: "Width",
27056             width: 40
27057         },
27058         height : {
27059             title: "Height",
27060             width: 40
27061         },   
27062         align: {
27063             title: "Align",
27064             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27065             width: 80
27066         },
27067         valign: {
27068             title: "Valign",
27069             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27070             width: 80
27071         },
27072         colspan: {
27073             title: "Colspan",
27074             width: 20
27075             
27076         }
27077     },
27078     'INPUT' : {
27079         name : {
27080             title: "name",
27081             width: 120
27082         },
27083         value : {
27084             title: "Value",
27085             width: 120
27086         },
27087         width : {
27088             title: "Width",
27089             width: 40
27090         }
27091     },
27092     'LABEL' : {
27093         'for' : {
27094             title: "For",
27095             width: 120
27096         }
27097     },
27098     'TEXTAREA' : {
27099           name : {
27100             title: "name",
27101             width: 120
27102         },
27103         rows : {
27104             title: "Rows",
27105             width: 20
27106         },
27107         cols : {
27108             title: "Cols",
27109             width: 20
27110         }
27111     },
27112     'SELECT' : {
27113         name : {
27114             title: "name",
27115             width: 120
27116         },
27117         selectoptions : {
27118             title: "Options",
27119             width: 200
27120         }
27121     },
27122     
27123     // should we really allow this??
27124     // should this just be 
27125     'BODY' : {
27126         title : {
27127             title: "title",
27128             width: 200,
27129             disabled : true
27130         }
27131     },
27132     '*' : {
27133         // empty..
27134     }
27135 };
27136
27137
27138
27139 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27140     
27141     tb: false,
27142     
27143     rendered: false,
27144     
27145     editor : false,
27146     /**
27147      * @cfg {Object} disable  List of toolbar elements to disable
27148          
27149      */
27150     disable : false,
27151     /**
27152      * @cfg {Object} styles List of styles 
27153      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27154      *
27155      * These must be defined in the page, so they get rendered correctly..
27156      * .headline { }
27157      * TD.underline { }
27158      * 
27159      */
27160     styles : false,
27161     
27162     
27163     
27164     toolbars : false,
27165     
27166     init : function(editor)
27167     {
27168         this.editor = editor;
27169         
27170         
27171         var fid = editor.frameId;
27172         var etb = this;
27173         function btn(id, toggle, handler){
27174             var xid = fid + '-'+ id ;
27175             return {
27176                 id : xid,
27177                 cmd : id,
27178                 cls : 'x-btn-icon x-edit-'+id,
27179                 enableToggle:toggle !== false,
27180                 scope: editor, // was editor...
27181                 handler:handler||editor.relayBtnCmd,
27182                 clickEvent:'mousedown',
27183                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27184                 tabIndex:-1
27185             };
27186         }
27187         // create a new element.
27188         var wdiv = editor.wrap.createChild({
27189                 tag: 'div'
27190             }, editor.wrap.dom.firstChild.nextSibling, true);
27191         
27192         // can we do this more than once??
27193         
27194          // stop form submits
27195       
27196  
27197         // disable everything...
27198         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27199         this.toolbars = {};
27200            
27201         for (var i in  ty) {
27202           
27203             this.toolbars[i] = this.buildToolbar(ty[i],i);
27204         }
27205         this.tb = this.toolbars.BODY;
27206         this.tb.el.show();
27207         this.buildFooter();
27208         this.footer.show();
27209         editor.on('hide', function( ) { this.footer.hide() }, this);
27210         editor.on('show', function( ) { this.footer.show() }, this);
27211         
27212          
27213         this.rendered = true;
27214         
27215         // the all the btns;
27216         editor.on('editorevent', this.updateToolbar, this);
27217         // other toolbars need to implement this..
27218         //editor.on('editmodechange', this.updateToolbar, this);
27219     },
27220     
27221     
27222     
27223     /**
27224      * Protected method that will not generally be called directly. It triggers
27225      * a toolbar update by reading the markup state of the current selection in the editor.
27226      */
27227     updateToolbar: function(editor,ev,sel){
27228
27229         //Roo.log(ev);
27230         // capture mouse up - this is handy for selecting images..
27231         // perhaps should go somewhere else...
27232         if(!this.editor.activated){
27233              this.editor.onFirstFocus();
27234             return;
27235         }
27236         
27237         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27238         // selectNode - might want to handle IE?
27239         if (ev &&
27240             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27241             ev.target && ev.target.tagName == 'IMG') {
27242             // they have click on an image...
27243             // let's see if we can change the selection...
27244             sel = ev.target;
27245          
27246               var nodeRange = sel.ownerDocument.createRange();
27247             try {
27248                 nodeRange.selectNode(sel);
27249             } catch (e) {
27250                 nodeRange.selectNodeContents(sel);
27251             }
27252             //nodeRange.collapse(true);
27253             var s = editor.win.getSelection();
27254             s.removeAllRanges();
27255             s.addRange(nodeRange);
27256         }  
27257         
27258       
27259         var updateFooter = sel ? false : true;
27260         
27261         
27262         var ans = this.editor.getAllAncestors();
27263         
27264         // pick
27265         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27266         
27267         if (!sel) { 
27268             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27269             sel = sel ? sel : this.editor.doc.body;
27270             sel = sel.tagName.length ? sel : this.editor.doc.body;
27271             
27272         }
27273         // pick a menu that exists..
27274         var tn = sel.tagName.toUpperCase();
27275         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27276         
27277         tn = sel.tagName.toUpperCase();
27278         
27279         var lastSel = this.tb.selectedNode
27280         
27281         this.tb.selectedNode = sel;
27282         
27283         // if current menu does not match..
27284         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27285                 
27286             this.tb.el.hide();
27287             ///console.log("show: " + tn);
27288             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27289             this.tb.el.show();
27290             // update name
27291             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27292             
27293             
27294             // update attributes
27295             if (this.tb.fields) {
27296                 this.tb.fields.each(function(e) {
27297                    e.setValue(sel.getAttribute(e.attrname));
27298                 });
27299             }
27300             
27301             var hasStyles = false;
27302             for(var i in this.styles) {
27303                 hasStyles = true;
27304                 break;
27305             }
27306             
27307             // update styles
27308             if (hasStyles) { 
27309                 var st = this.tb.fields.item(0);
27310                 
27311                 st.store.removeAll();
27312                
27313                 
27314                 var cn = sel.className.split(/\s+/);
27315                 
27316                 var avs = [];
27317                 if (this.styles['*']) {
27318                     
27319                     Roo.each(this.styles['*'], function(v) {
27320                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27321                     });
27322                 }
27323                 if (this.styles[tn]) { 
27324                     Roo.each(this.styles[tn], function(v) {
27325                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27326                     });
27327                 }
27328                 
27329                 st.store.loadData(avs);
27330                 st.collapse();
27331                 st.setValue(cn);
27332             }
27333             // flag our selected Node.
27334             this.tb.selectedNode = sel;
27335            
27336            
27337             Roo.menu.MenuMgr.hideAll();
27338
27339         }
27340         
27341         if (!updateFooter) {
27342             return;
27343         }
27344         // update the footer
27345         //
27346         var html = '';
27347         
27348         this.footerEls = ans.reverse();
27349         Roo.each(this.footerEls, function(a,i) {
27350             if (!a) { return; }
27351             html += html.length ? ' &gt; '  :  '';
27352             
27353             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27354             
27355         });
27356        
27357         // 
27358         var sz = this.footDisp.up('td').getSize();
27359         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27360         this.footDisp.dom.style.marginLeft = '5px';
27361         
27362         this.footDisp.dom.style.overflow = 'hidden';
27363         
27364         this.footDisp.dom.innerHTML = html;
27365             
27366         //this.editorsyncValue();
27367     },
27368    
27369        
27370     // private
27371     onDestroy : function(){
27372         if(this.rendered){
27373             
27374             this.tb.items.each(function(item){
27375                 if(item.menu){
27376                     item.menu.removeAll();
27377                     if(item.menu.el){
27378                         item.menu.el.destroy();
27379                     }
27380                 }
27381                 item.destroy();
27382             });
27383              
27384         }
27385     },
27386     onFirstFocus: function() {
27387         // need to do this for all the toolbars..
27388         this.tb.items.each(function(item){
27389            item.enable();
27390         });
27391     },
27392     buildToolbar: function(tlist, nm)
27393     {
27394         var editor = this.editor;
27395          // create a new element.
27396         var wdiv = editor.wrap.createChild({
27397                 tag: 'div'
27398             }, editor.wrap.dom.firstChild.nextSibling, true);
27399         
27400        
27401         var tb = new Roo.Toolbar(wdiv);
27402         // add the name..
27403         
27404         tb.add(nm+ ":&nbsp;");
27405         
27406         var styles = [];
27407         for(var i in this.styles) {
27408             styles.push(i);
27409         }
27410         
27411         // styles...
27412         if (styles && styles.length) {
27413             
27414             // this needs a multi-select checkbox...
27415             tb.addField( new Roo.form.ComboBox({
27416                 store: new Roo.data.SimpleStore({
27417                     id : 'val',
27418                     fields: ['val', 'selected'],
27419                     data : [] 
27420                 }),
27421                 name : '-roo-edit-className',
27422                 attrname : 'className',
27423                 displayField:'val',
27424                 typeAhead: false,
27425                 mode: 'local',
27426                 editable : false,
27427                 triggerAction: 'all',
27428                 emptyText:'Select Style',
27429                 selectOnFocus:true,
27430                 width: 130,
27431                 listeners : {
27432                     'select': function(c, r, i) {
27433                         // initial support only for on class per el..
27434                         tb.selectedNode.className =  r ? r.get('val') : '';
27435                         editor.syncValue();
27436                     }
27437                 }
27438     
27439             }));
27440         }
27441             
27442         
27443         
27444         for (var i in tlist) {
27445             
27446             var item = tlist[i];
27447             tb.add(item.title + ":&nbsp;");
27448             
27449             
27450             
27451             
27452             if (item.opts) {
27453                 // opts == pulldown..
27454                 tb.addField( new Roo.form.ComboBox({
27455                     store: new Roo.data.SimpleStore({
27456                         id : 'val',
27457                         fields: ['val'],
27458                         data : item.opts  
27459                     }),
27460                     name : '-roo-edit-' + i,
27461                     attrname : i,
27462                     displayField:'val',
27463                     typeAhead: false,
27464                     mode: 'local',
27465                     editable : false,
27466                     triggerAction: 'all',
27467                     emptyText:'Select',
27468                     selectOnFocus:true,
27469                     width: item.width ? item.width  : 130,
27470                     listeners : {
27471                         'select': function(c, r, i) {
27472                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27473                         }
27474                     }
27475
27476                 }));
27477                 continue;
27478                     
27479                  
27480                 
27481                 tb.addField( new Roo.form.TextField({
27482                     name: i,
27483                     width: 100,
27484                     //allowBlank:false,
27485                     value: ''
27486                 }));
27487                 continue;
27488             }
27489             tb.addField( new Roo.form.TextField({
27490                 name: '-roo-edit-' + i,
27491                 attrname : i,
27492                 
27493                 width: item.width,
27494                 //allowBlank:true,
27495                 value: '',
27496                 listeners: {
27497                     'change' : function(f, nv, ov) {
27498                         tb.selectedNode.setAttribute(f.attrname, nv);
27499                     }
27500                 }
27501             }));
27502              
27503         }
27504         tb.el.on('click', function(e){
27505             e.preventDefault(); // what does this do?
27506         });
27507         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27508         tb.el.hide();
27509         tb.name = nm;
27510         // dont need to disable them... as they will get hidden
27511         return tb;
27512          
27513         
27514     },
27515     buildFooter : function()
27516     {
27517         
27518         var fel = this.editor.wrap.createChild();
27519         this.footer = new Roo.Toolbar(fel);
27520         // toolbar has scrolly on left / right?
27521         var footDisp= new Roo.Toolbar.Fill();
27522         var _t = this;
27523         this.footer.add(
27524             {
27525                 text : '&lt;',
27526                 xtype: 'Button',
27527                 handler : function() {
27528                     _t.footDisp.scrollTo('left',0,true)
27529                 }
27530             }
27531         );
27532         this.footer.add( footDisp );
27533         this.footer.add( 
27534             {
27535                 text : '&gt;',
27536                 xtype: 'Button',
27537                 handler : function() {
27538                     // no animation..
27539                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27540                 }
27541             }
27542         );
27543         var fel = Roo.get(footDisp.el);
27544         fel.addClass('x-editor-context');
27545         this.footDispWrap = fel; 
27546         this.footDispWrap.overflow  = 'hidden';
27547         
27548         this.footDisp = fel.createChild();
27549         this.footDispWrap.on('click', this.onContextClick, this)
27550         
27551         
27552     },
27553     onContextClick : function (ev,dom)
27554     {
27555         ev.preventDefault();
27556         var  cn = dom.className;
27557         Roo.log(cn);
27558         if (!cn.match(/x-ed-loc-/)) {
27559             return;
27560         }
27561         var n = cn.split('-').pop();
27562         var ans = this.footerEls;
27563         var sel = ans[n];
27564         
27565          // pick
27566         var range = this.editor.createRange();
27567         
27568         range.selectNodeContents(sel);
27569         //range.selectNode(sel);
27570         
27571         
27572         var selection = this.editor.getSelection();
27573         selection.removeAllRanges();
27574         selection.addRange(range);
27575         
27576         
27577         
27578         this.updateToolbar(null, null, sel);
27579         
27580         
27581     }
27582     
27583     
27584     
27585     
27586     
27587 });
27588
27589
27590
27591
27592
27593 /*
27594  * Based on:
27595  * Ext JS Library 1.1.1
27596  * Copyright(c) 2006-2007, Ext JS, LLC.
27597  *
27598  * Originally Released Under LGPL - original licence link has changed is not relivant.
27599  *
27600  * Fork - LGPL
27601  * <script type="text/javascript">
27602  */
27603  
27604 /**
27605  * @class Roo.form.BasicForm
27606  * @extends Roo.util.Observable
27607  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27608  * @constructor
27609  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27610  * @param {Object} config Configuration options
27611  */
27612 Roo.form.BasicForm = function(el, config){
27613     this.allItems = [];
27614     this.childForms = [];
27615     Roo.apply(this, config);
27616     /*
27617      * The Roo.form.Field items in this form.
27618      * @type MixedCollection
27619      */
27620      
27621      
27622     this.items = new Roo.util.MixedCollection(false, function(o){
27623         return o.id || (o.id = Roo.id());
27624     });
27625     this.addEvents({
27626         /**
27627          * @event beforeaction
27628          * Fires before any action is performed. Return false to cancel the action.
27629          * @param {Form} this
27630          * @param {Action} action The action to be performed
27631          */
27632         beforeaction: true,
27633         /**
27634          * @event actionfailed
27635          * Fires when an action fails.
27636          * @param {Form} this
27637          * @param {Action} action The action that failed
27638          */
27639         actionfailed : true,
27640         /**
27641          * @event actioncomplete
27642          * Fires when an action is completed.
27643          * @param {Form} this
27644          * @param {Action} action The action that completed
27645          */
27646         actioncomplete : true
27647     });
27648     if(el){
27649         this.initEl(el);
27650     }
27651     Roo.form.BasicForm.superclass.constructor.call(this);
27652 };
27653
27654 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27655     /**
27656      * @cfg {String} method
27657      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27658      */
27659     /**
27660      * @cfg {DataReader} reader
27661      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27662      * This is optional as there is built-in support for processing JSON.
27663      */
27664     /**
27665      * @cfg {DataReader} errorReader
27666      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27667      * This is completely optional as there is built-in support for processing JSON.
27668      */
27669     /**
27670      * @cfg {String} url
27671      * The URL to use for form actions if one isn't supplied in the action options.
27672      */
27673     /**
27674      * @cfg {Boolean} fileUpload
27675      * Set to true if this form is a file upload.
27676      */
27677      
27678     /**
27679      * @cfg {Object} baseParams
27680      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27681      */
27682      /**
27683      
27684     /**
27685      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27686      */
27687     timeout: 30,
27688
27689     // private
27690     activeAction : null,
27691
27692     /**
27693      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27694      * or setValues() data instead of when the form was first created.
27695      */
27696     trackResetOnLoad : false,
27697     
27698     
27699     /**
27700      * childForms - used for multi-tab forms
27701      * @type {Array}
27702      */
27703     childForms : false,
27704     
27705     /**
27706      * allItems - full list of fields.
27707      * @type {Array}
27708      */
27709     allItems : false,
27710     
27711     /**
27712      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27713      * element by passing it or its id or mask the form itself by passing in true.
27714      * @type Mixed
27715      */
27716     waitMsgTarget : false,
27717
27718     // private
27719     initEl : function(el){
27720         this.el = Roo.get(el);
27721         this.id = this.el.id || Roo.id();
27722         this.el.on('submit', this.onSubmit, this);
27723         this.el.addClass('x-form');
27724     },
27725
27726     // private
27727     onSubmit : function(e){
27728         e.stopEvent();
27729     },
27730
27731     /**
27732      * Returns true if client-side validation on the form is successful.
27733      * @return Boolean
27734      */
27735     isValid : function(){
27736         var valid = true;
27737         this.items.each(function(f){
27738            if(!f.validate()){
27739                valid = false;
27740            }
27741         });
27742         return valid;
27743     },
27744
27745     /**
27746      * Returns true if any fields in this form have changed since their original load.
27747      * @return Boolean
27748      */
27749     isDirty : function(){
27750         var dirty = false;
27751         this.items.each(function(f){
27752            if(f.isDirty()){
27753                dirty = true;
27754                return false;
27755            }
27756         });
27757         return dirty;
27758     },
27759
27760     /**
27761      * Performs a predefined action (submit or load) or custom actions you define on this form.
27762      * @param {String} actionName The name of the action type
27763      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27764      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27765      * accept other config options):
27766      * <pre>
27767 Property          Type             Description
27768 ----------------  ---------------  ----------------------------------------------------------------------------------
27769 url               String           The url for the action (defaults to the form's url)
27770 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27771 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27772 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27773                                    validate the form on the client (defaults to false)
27774      * </pre>
27775      * @return {BasicForm} this
27776      */
27777     doAction : function(action, options){
27778         if(typeof action == 'string'){
27779             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27780         }
27781         if(this.fireEvent('beforeaction', this, action) !== false){
27782             this.beforeAction(action);
27783             action.run.defer(100, action);
27784         }
27785         return this;
27786     },
27787
27788     /**
27789      * Shortcut to do a submit action.
27790      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27791      * @return {BasicForm} this
27792      */
27793     submit : function(options){
27794         this.doAction('submit', options);
27795         return this;
27796     },
27797
27798     /**
27799      * Shortcut to do a load action.
27800      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27801      * @return {BasicForm} this
27802      */
27803     load : function(options){
27804         this.doAction('load', options);
27805         return this;
27806     },
27807
27808     /**
27809      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27810      * @param {Record} record The record to edit
27811      * @return {BasicForm} this
27812      */
27813     updateRecord : function(record){
27814         record.beginEdit();
27815         var fs = record.fields;
27816         fs.each(function(f){
27817             var field = this.findField(f.name);
27818             if(field){
27819                 record.set(f.name, field.getValue());
27820             }
27821         }, this);
27822         record.endEdit();
27823         return this;
27824     },
27825
27826     /**
27827      * Loads an Roo.data.Record into this form.
27828      * @param {Record} record The record to load
27829      * @return {BasicForm} this
27830      */
27831     loadRecord : function(record){
27832         this.setValues(record.data);
27833         return this;
27834     },
27835
27836     // private
27837     beforeAction : function(action){
27838         var o = action.options;
27839         
27840        
27841         if(this.waitMsgTarget === true){
27842             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27843         }else if(this.waitMsgTarget){
27844             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27845             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27846         }else {
27847             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27848         }
27849          
27850     },
27851
27852     // private
27853     afterAction : function(action, success){
27854         this.activeAction = null;
27855         var o = action.options;
27856         
27857         if(this.waitMsgTarget === true){
27858             this.el.unmask();
27859         }else if(this.waitMsgTarget){
27860             this.waitMsgTarget.unmask();
27861         }else{
27862             Roo.MessageBox.updateProgress(1);
27863             Roo.MessageBox.hide();
27864         }
27865          
27866         if(success){
27867             if(o.reset){
27868                 this.reset();
27869             }
27870             Roo.callback(o.success, o.scope, [this, action]);
27871             this.fireEvent('actioncomplete', this, action);
27872             
27873         }else{
27874             
27875             // failure condition..
27876             // we have a scenario where updates need confirming.
27877             // eg. if a locking scenario exists..
27878             // we look for { errors : { needs_confirm : true }} in the response.
27879             if (
27880                 (typeof(action.result) != 'undefined')  &&
27881                 (typeof(action.result.errors) != 'undefined')  &&
27882                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27883            ){
27884                 var _t = this;
27885                 Roo.MessageBox.confirm(
27886                     "Change requires confirmation",
27887                     action.result.errorMsg,
27888                     function(r) {
27889                         if (r != 'yes') {
27890                             return;
27891                         }
27892                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27893                     }
27894                     
27895                 );
27896                 
27897                 
27898                 
27899                 return;
27900             }
27901             
27902             Roo.callback(o.failure, o.scope, [this, action]);
27903             // show an error message if no failed handler is set..
27904             if (!this.hasListener('actionfailed')) {
27905                 Roo.MessageBox.alert("Error",
27906                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27907                         action.result.errorMsg :
27908                         "Saving Failed, please check your entries or try again"
27909                 );
27910             }
27911             
27912             this.fireEvent('actionfailed', this, action);
27913         }
27914         
27915     },
27916
27917     /**
27918      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27919      * @param {String} id The value to search for
27920      * @return Field
27921      */
27922     findField : function(id){
27923         var field = this.items.get(id);
27924         if(!field){
27925             this.items.each(function(f){
27926                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27927                     field = f;
27928                     return false;
27929                 }
27930             });
27931         }
27932         return field || null;
27933     },
27934
27935     /**
27936      * Add a secondary form to this one, 
27937      * Used to provide tabbed forms. One form is primary, with hidden values 
27938      * which mirror the elements from the other forms.
27939      * 
27940      * @param {Roo.form.Form} form to add.
27941      * 
27942      */
27943     addForm : function(form)
27944     {
27945        
27946         if (this.childForms.indexOf(form) > -1) {
27947             // already added..
27948             return;
27949         }
27950         this.childForms.push(form);
27951         var n = '';
27952         Roo.each(form.allItems, function (fe) {
27953             
27954             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27955             if (this.findField(n)) { // already added..
27956                 return;
27957             }
27958             var add = new Roo.form.Hidden({
27959                 name : n
27960             });
27961             add.render(this.el);
27962             
27963             this.add( add );
27964         }, this);
27965         
27966     },
27967     /**
27968      * Mark fields in this form invalid in bulk.
27969      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27970      * @return {BasicForm} this
27971      */
27972     markInvalid : function(errors){
27973         if(errors instanceof Array){
27974             for(var i = 0, len = errors.length; i < len; i++){
27975                 var fieldError = errors[i];
27976                 var f = this.findField(fieldError.id);
27977                 if(f){
27978                     f.markInvalid(fieldError.msg);
27979                 }
27980             }
27981         }else{
27982             var field, id;
27983             for(id in errors){
27984                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27985                     field.markInvalid(errors[id]);
27986                 }
27987             }
27988         }
27989         Roo.each(this.childForms || [], function (f) {
27990             f.markInvalid(errors);
27991         });
27992         
27993         return this;
27994     },
27995
27996     /**
27997      * Set values for fields in this form in bulk.
27998      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27999      * @return {BasicForm} this
28000      */
28001     setValues : function(values){
28002         if(values instanceof Array){ // array of objects
28003             for(var i = 0, len = values.length; i < len; i++){
28004                 var v = values[i];
28005                 var f = this.findField(v.id);
28006                 if(f){
28007                     f.setValue(v.value);
28008                     if(this.trackResetOnLoad){
28009                         f.originalValue = f.getValue();
28010                     }
28011                 }
28012             }
28013         }else{ // object hash
28014             var field, id;
28015             for(id in values){
28016                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28017                     
28018                     if (field.setFromData && 
28019                         field.valueField && 
28020                         field.displayField &&
28021                         // combos' with local stores can 
28022                         // be queried via setValue()
28023                         // to set their value..
28024                         (field.store && !field.store.isLocal)
28025                         ) {
28026                         // it's a combo
28027                         var sd = { };
28028                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28029                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28030                         field.setFromData(sd);
28031                         
28032                     } else {
28033                         field.setValue(values[id]);
28034                     }
28035                     
28036                     
28037                     if(this.trackResetOnLoad){
28038                         field.originalValue = field.getValue();
28039                     }
28040                 }
28041             }
28042         }
28043          
28044         Roo.each(this.childForms || [], function (f) {
28045             f.setValues(values);
28046         });
28047                 
28048         return this;
28049     },
28050
28051     /**
28052      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28053      * they are returned as an array.
28054      * @param {Boolean} asString
28055      * @return {Object}
28056      */
28057     getValues : function(asString){
28058         if (this.childForms) {
28059             // copy values from the child forms
28060             Roo.each(this.childForms, function (f) {
28061                 this.setValues(f.getValues());
28062             }, this);
28063         }
28064         
28065         
28066         
28067         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28068         if(asString === true){
28069             return fs;
28070         }
28071         return Roo.urlDecode(fs);
28072     },
28073     
28074     /**
28075      * Returns the fields in this form as an object with key/value pairs. 
28076      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28077      * @return {Object}
28078      */
28079     getFieldValues : function(with_hidden)
28080     {
28081         if (this.childForms) {
28082             // copy values from the child forms
28083             // should this call getFieldValues - probably not as we do not currently copy
28084             // hidden fields when we generate..
28085             Roo.each(this.childForms, function (f) {
28086                 this.setValues(f.getValues());
28087             }, this);
28088         }
28089         
28090         var ret = {};
28091         this.items.each(function(f){
28092             if (!f.getName()) {
28093                 return;
28094             }
28095             var v = f.getValue();
28096             // not sure if this supported any more..
28097             if ((typeof(v) == 'object') && f.getRawValue) {
28098                 v = f.getRawValue() ; // dates..
28099             }
28100             // combo boxes where name != hiddenName...
28101             if (f.name != f.getName()) {
28102                 ret[f.name] = f.getRawValue();
28103             }
28104             ret[f.getName()] = v;
28105         });
28106         
28107         return ret;
28108     },
28109
28110     /**
28111      * Clears all invalid messages in this form.
28112      * @return {BasicForm} this
28113      */
28114     clearInvalid : function(){
28115         this.items.each(function(f){
28116            f.clearInvalid();
28117         });
28118         
28119         Roo.each(this.childForms || [], function (f) {
28120             f.clearInvalid();
28121         });
28122         
28123         
28124         return this;
28125     },
28126
28127     /**
28128      * Resets this form.
28129      * @return {BasicForm} this
28130      */
28131     reset : function(){
28132         this.items.each(function(f){
28133             f.reset();
28134         });
28135         
28136         Roo.each(this.childForms || [], function (f) {
28137             f.reset();
28138         });
28139        
28140         
28141         return this;
28142     },
28143
28144     /**
28145      * Add Roo.form components to this form.
28146      * @param {Field} field1
28147      * @param {Field} field2 (optional)
28148      * @param {Field} etc (optional)
28149      * @return {BasicForm} this
28150      */
28151     add : function(){
28152         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28153         return this;
28154     },
28155
28156
28157     /**
28158      * Removes a field from the items collection (does NOT remove its markup).
28159      * @param {Field} field
28160      * @return {BasicForm} this
28161      */
28162     remove : function(field){
28163         this.items.remove(field);
28164         return this;
28165     },
28166
28167     /**
28168      * Looks at the fields in this form, checks them for an id attribute,
28169      * and calls applyTo on the existing dom element with that id.
28170      * @return {BasicForm} this
28171      */
28172     render : function(){
28173         this.items.each(function(f){
28174             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28175                 f.applyTo(f.id);
28176             }
28177         });
28178         return this;
28179     },
28180
28181     /**
28182      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28183      * @param {Object} values
28184      * @return {BasicForm} this
28185      */
28186     applyToFields : function(o){
28187         this.items.each(function(f){
28188            Roo.apply(f, o);
28189         });
28190         return this;
28191     },
28192
28193     /**
28194      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28195      * @param {Object} values
28196      * @return {BasicForm} this
28197      */
28198     applyIfToFields : function(o){
28199         this.items.each(function(f){
28200            Roo.applyIf(f, o);
28201         });
28202         return this;
28203     }
28204 });
28205
28206 // back compat
28207 Roo.BasicForm = Roo.form.BasicForm;/*
28208  * Based on:
28209  * Ext JS Library 1.1.1
28210  * Copyright(c) 2006-2007, Ext JS, LLC.
28211  *
28212  * Originally Released Under LGPL - original licence link has changed is not relivant.
28213  *
28214  * Fork - LGPL
28215  * <script type="text/javascript">
28216  */
28217
28218 /**
28219  * @class Roo.form.Form
28220  * @extends Roo.form.BasicForm
28221  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28222  * @constructor
28223  * @param {Object} config Configuration options
28224  */
28225 Roo.form.Form = function(config){
28226     var xitems =  [];
28227     if (config.items) {
28228         xitems = config.items;
28229         delete config.items;
28230     }
28231    
28232     
28233     Roo.form.Form.superclass.constructor.call(this, null, config);
28234     this.url = this.url || this.action;
28235     if(!this.root){
28236         this.root = new Roo.form.Layout(Roo.applyIf({
28237             id: Roo.id()
28238         }, config));
28239     }
28240     this.active = this.root;
28241     /**
28242      * Array of all the buttons that have been added to this form via {@link addButton}
28243      * @type Array
28244      */
28245     this.buttons = [];
28246     this.allItems = [];
28247     this.addEvents({
28248         /**
28249          * @event clientvalidation
28250          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28251          * @param {Form} this
28252          * @param {Boolean} valid true if the form has passed client-side validation
28253          */
28254         clientvalidation: true,
28255         /**
28256          * @event rendered
28257          * Fires when the form is rendered
28258          * @param {Roo.form.Form} form
28259          */
28260         rendered : true
28261     });
28262     
28263     if (this.progressUrl) {
28264             // push a hidden field onto the list of fields..
28265             this.addxtype( {
28266                     xns: Roo.form, 
28267                     xtype : 'Hidden', 
28268                     name : 'UPLOAD_IDENTIFIER' 
28269             });
28270         }
28271         
28272     
28273     Roo.each(xitems, this.addxtype, this);
28274     
28275     
28276     
28277 };
28278
28279 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28280     /**
28281      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28282      */
28283     /**
28284      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28285      */
28286     /**
28287      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28288      */
28289     buttonAlign:'center',
28290
28291     /**
28292      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28293      */
28294     minButtonWidth:75,
28295
28296     /**
28297      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28298      * This property cascades to child containers if not set.
28299      */
28300     labelAlign:'left',
28301
28302     /**
28303      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28304      * fires a looping event with that state. This is required to bind buttons to the valid
28305      * state using the config value formBind:true on the button.
28306      */
28307     monitorValid : false,
28308
28309     /**
28310      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28311      */
28312     monitorPoll : 200,
28313     
28314     /**
28315      * @cfg {String} progressUrl - Url to return progress data 
28316      */
28317     
28318     progressUrl : false,
28319   
28320     /**
28321      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28322      * fields are added and the column is closed. If no fields are passed the column remains open
28323      * until end() is called.
28324      * @param {Object} config The config to pass to the column
28325      * @param {Field} field1 (optional)
28326      * @param {Field} field2 (optional)
28327      * @param {Field} etc (optional)
28328      * @return Column The column container object
28329      */
28330     column : function(c){
28331         var col = new Roo.form.Column(c);
28332         this.start(col);
28333         if(arguments.length > 1){ // duplicate code required because of Opera
28334             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28335             this.end();
28336         }
28337         return col;
28338     },
28339
28340     /**
28341      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28342      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28343      * until end() is called.
28344      * @param {Object} config The config to pass to the fieldset
28345      * @param {Field} field1 (optional)
28346      * @param {Field} field2 (optional)
28347      * @param {Field} etc (optional)
28348      * @return FieldSet The fieldset container object
28349      */
28350     fieldset : function(c){
28351         var fs = new Roo.form.FieldSet(c);
28352         this.start(fs);
28353         if(arguments.length > 1){ // duplicate code required because of Opera
28354             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28355             this.end();
28356         }
28357         return fs;
28358     },
28359
28360     /**
28361      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28362      * fields are added and the container is closed. If no fields are passed the container remains open
28363      * until end() is called.
28364      * @param {Object} config The config to pass to the Layout
28365      * @param {Field} field1 (optional)
28366      * @param {Field} field2 (optional)
28367      * @param {Field} etc (optional)
28368      * @return Layout The container object
28369      */
28370     container : function(c){
28371         var l = new Roo.form.Layout(c);
28372         this.start(l);
28373         if(arguments.length > 1){ // duplicate code required because of Opera
28374             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28375             this.end();
28376         }
28377         return l;
28378     },
28379
28380     /**
28381      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28382      * @param {Object} container A Roo.form.Layout or subclass of Layout
28383      * @return {Form} this
28384      */
28385     start : function(c){
28386         // cascade label info
28387         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28388         this.active.stack.push(c);
28389         c.ownerCt = this.active;
28390         this.active = c;
28391         return this;
28392     },
28393
28394     /**
28395      * Closes the current open container
28396      * @return {Form} this
28397      */
28398     end : function(){
28399         if(this.active == this.root){
28400             return this;
28401         }
28402         this.active = this.active.ownerCt;
28403         return this;
28404     },
28405
28406     /**
28407      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28408      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28409      * as the label of the field.
28410      * @param {Field} field1
28411      * @param {Field} field2 (optional)
28412      * @param {Field} etc. (optional)
28413      * @return {Form} this
28414      */
28415     add : function(){
28416         this.active.stack.push.apply(this.active.stack, arguments);
28417         this.allItems.push.apply(this.allItems,arguments);
28418         var r = [];
28419         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28420             if(a[i].isFormField){
28421                 r.push(a[i]);
28422             }
28423         }
28424         if(r.length > 0){
28425             Roo.form.Form.superclass.add.apply(this, r);
28426         }
28427         return this;
28428     },
28429     
28430
28431     
28432     
28433     
28434      /**
28435      * Find any element that has been added to a form, using it's ID or name
28436      * This can include framesets, columns etc. along with regular fields..
28437      * @param {String} id - id or name to find.
28438      
28439      * @return {Element} e - or false if nothing found.
28440      */
28441     findbyId : function(id)
28442     {
28443         var ret = false;
28444         if (!id) {
28445             return ret;
28446         }
28447         Roo.each(this.allItems, function(f){
28448             if (f.id == id || f.name == id ){
28449                 ret = f;
28450                 return false;
28451             }
28452         });
28453         return ret;
28454     },
28455
28456     
28457     
28458     /**
28459      * Render this form into the passed container. This should only be called once!
28460      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28461      * @return {Form} this
28462      */
28463     render : function(ct)
28464     {
28465         
28466         
28467         
28468         ct = Roo.get(ct);
28469         var o = this.autoCreate || {
28470             tag: 'form',
28471             method : this.method || 'POST',
28472             id : this.id || Roo.id()
28473         };
28474         this.initEl(ct.createChild(o));
28475
28476         this.root.render(this.el);
28477         
28478        
28479              
28480         this.items.each(function(f){
28481             f.render('x-form-el-'+f.id);
28482         });
28483
28484         if(this.buttons.length > 0){
28485             // tables are required to maintain order and for correct IE layout
28486             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28487                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28488                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28489             }}, null, true);
28490             var tr = tb.getElementsByTagName('tr')[0];
28491             for(var i = 0, len = this.buttons.length; i < len; i++) {
28492                 var b = this.buttons[i];
28493                 var td = document.createElement('td');
28494                 td.className = 'x-form-btn-td';
28495                 b.render(tr.appendChild(td));
28496             }
28497         }
28498         if(this.monitorValid){ // initialize after render
28499             this.startMonitoring();
28500         }
28501         this.fireEvent('rendered', this);
28502         return this;
28503     },
28504
28505     /**
28506      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28507      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28508      * object or a valid Roo.DomHelper element config
28509      * @param {Function} handler The function called when the button is clicked
28510      * @param {Object} scope (optional) The scope of the handler function
28511      * @return {Roo.Button}
28512      */
28513     addButton : function(config, handler, scope){
28514         var bc = {
28515             handler: handler,
28516             scope: scope,
28517             minWidth: this.minButtonWidth,
28518             hideParent:true
28519         };
28520         if(typeof config == "string"){
28521             bc.text = config;
28522         }else{
28523             Roo.apply(bc, config);
28524         }
28525         var btn = new Roo.Button(null, bc);
28526         this.buttons.push(btn);
28527         return btn;
28528     },
28529
28530      /**
28531      * Adds a series of form elements (using the xtype property as the factory method.
28532      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28533      * @param {Object} config 
28534      */
28535     
28536     addxtype : function()
28537     {
28538         var ar = Array.prototype.slice.call(arguments, 0);
28539         var ret = false;
28540         for(var i = 0; i < ar.length; i++) {
28541             if (!ar[i]) {
28542                 continue; // skip -- if this happends something invalid got sent, we 
28543                 // should ignore it, as basically that interface element will not show up
28544                 // and that should be pretty obvious!!
28545             }
28546             
28547             if (Roo.form[ar[i].xtype]) {
28548                 ar[i].form = this;
28549                 var fe = Roo.factory(ar[i], Roo.form);
28550                 if (!ret) {
28551                     ret = fe;
28552                 }
28553                 fe.form = this;
28554                 if (fe.store) {
28555                     fe.store.form = this;
28556                 }
28557                 if (fe.isLayout) {  
28558                          
28559                     this.start(fe);
28560                     this.allItems.push(fe);
28561                     if (fe.items && fe.addxtype) {
28562                         fe.addxtype.apply(fe, fe.items);
28563                         delete fe.items;
28564                     }
28565                      this.end();
28566                     continue;
28567                 }
28568                 
28569                 
28570                  
28571                 this.add(fe);
28572               //  console.log('adding ' + ar[i].xtype);
28573             }
28574             if (ar[i].xtype == 'Button') {  
28575                 //console.log('adding button');
28576                 //console.log(ar[i]);
28577                 this.addButton(ar[i]);
28578                 this.allItems.push(fe);
28579                 continue;
28580             }
28581             
28582             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28583                 alert('end is not supported on xtype any more, use items');
28584             //    this.end();
28585             //    //console.log('adding end');
28586             }
28587             
28588         }
28589         return ret;
28590     },
28591     
28592     /**
28593      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28594      * option "monitorValid"
28595      */
28596     startMonitoring : function(){
28597         if(!this.bound){
28598             this.bound = true;
28599             Roo.TaskMgr.start({
28600                 run : this.bindHandler,
28601                 interval : this.monitorPoll || 200,
28602                 scope: this
28603             });
28604         }
28605     },
28606
28607     /**
28608      * Stops monitoring of the valid state of this form
28609      */
28610     stopMonitoring : function(){
28611         this.bound = false;
28612     },
28613
28614     // private
28615     bindHandler : function(){
28616         if(!this.bound){
28617             return false; // stops binding
28618         }
28619         var valid = true;
28620         this.items.each(function(f){
28621             if(!f.isValid(true)){
28622                 valid = false;
28623                 return false;
28624             }
28625         });
28626         for(var i = 0, len = this.buttons.length; i < len; i++){
28627             var btn = this.buttons[i];
28628             if(btn.formBind === true && btn.disabled === valid){
28629                 btn.setDisabled(!valid);
28630             }
28631         }
28632         this.fireEvent('clientvalidation', this, valid);
28633     }
28634     
28635     
28636     
28637     
28638     
28639     
28640     
28641     
28642 });
28643
28644
28645 // back compat
28646 Roo.Form = Roo.form.Form;
28647 /*
28648  * Based on:
28649  * Ext JS Library 1.1.1
28650  * Copyright(c) 2006-2007, Ext JS, LLC.
28651  *
28652  * Originally Released Under LGPL - original licence link has changed is not relivant.
28653  *
28654  * Fork - LGPL
28655  * <script type="text/javascript">
28656  */
28657  
28658  /**
28659  * @class Roo.form.Action
28660  * Internal Class used to handle form actions
28661  * @constructor
28662  * @param {Roo.form.BasicForm} el The form element or its id
28663  * @param {Object} config Configuration options
28664  */
28665  
28666  
28667 // define the action interface
28668 Roo.form.Action = function(form, options){
28669     this.form = form;
28670     this.options = options || {};
28671 };
28672 /**
28673  * Client Validation Failed
28674  * @const 
28675  */
28676 Roo.form.Action.CLIENT_INVALID = 'client';
28677 /**
28678  * Server Validation Failed
28679  * @const 
28680  */
28681  Roo.form.Action.SERVER_INVALID = 'server';
28682  /**
28683  * Connect to Server Failed
28684  * @const 
28685  */
28686 Roo.form.Action.CONNECT_FAILURE = 'connect';
28687 /**
28688  * Reading Data from Server Failed
28689  * @const 
28690  */
28691 Roo.form.Action.LOAD_FAILURE = 'load';
28692
28693 Roo.form.Action.prototype = {
28694     type : 'default',
28695     failureType : undefined,
28696     response : undefined,
28697     result : undefined,
28698
28699     // interface method
28700     run : function(options){
28701
28702     },
28703
28704     // interface method
28705     success : function(response){
28706
28707     },
28708
28709     // interface method
28710     handleResponse : function(response){
28711
28712     },
28713
28714     // default connection failure
28715     failure : function(response){
28716         
28717         this.response = response;
28718         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28719         this.form.afterAction(this, false);
28720     },
28721
28722     processResponse : function(response){
28723         this.response = response;
28724         if(!response.responseText){
28725             return true;
28726         }
28727         this.result = this.handleResponse(response);
28728         return this.result;
28729     },
28730
28731     // utility functions used internally
28732     getUrl : function(appendParams){
28733         var url = this.options.url || this.form.url || this.form.el.dom.action;
28734         if(appendParams){
28735             var p = this.getParams();
28736             if(p){
28737                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28738             }
28739         }
28740         return url;
28741     },
28742
28743     getMethod : function(){
28744         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28745     },
28746
28747     getParams : function(){
28748         var bp = this.form.baseParams;
28749         var p = this.options.params;
28750         if(p){
28751             if(typeof p == "object"){
28752                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28753             }else if(typeof p == 'string' && bp){
28754                 p += '&' + Roo.urlEncode(bp);
28755             }
28756         }else if(bp){
28757             p = Roo.urlEncode(bp);
28758         }
28759         return p;
28760     },
28761
28762     createCallback : function(){
28763         return {
28764             success: this.success,
28765             failure: this.failure,
28766             scope: this,
28767             timeout: (this.form.timeout*1000),
28768             upload: this.form.fileUpload ? this.success : undefined
28769         };
28770     }
28771 };
28772
28773 Roo.form.Action.Submit = function(form, options){
28774     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28775 };
28776
28777 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28778     type : 'submit',
28779
28780     haveProgress : false,
28781     uploadComplete : false,
28782     
28783     // uploadProgress indicator.
28784     uploadProgress : function()
28785     {
28786         if (!this.form.progressUrl) {
28787             return;
28788         }
28789         
28790         if (!this.haveProgress) {
28791             Roo.MessageBox.progress("Uploading", "Uploading");
28792         }
28793         if (this.uploadComplete) {
28794            Roo.MessageBox.hide();
28795            return;
28796         }
28797         
28798         this.haveProgress = true;
28799    
28800         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28801         
28802         var c = new Roo.data.Connection();
28803         c.request({
28804             url : this.form.progressUrl,
28805             params: {
28806                 id : uid
28807             },
28808             method: 'GET',
28809             success : function(req){
28810                //console.log(data);
28811                 var rdata = false;
28812                 var edata;
28813                 try  {
28814                    rdata = Roo.decode(req.responseText)
28815                 } catch (e) {
28816                     Roo.log("Invalid data from server..");
28817                     Roo.log(edata);
28818                     return;
28819                 }
28820                 if (!rdata || !rdata.success) {
28821                     Roo.log(rdata);
28822                     Roo.MessageBox.alert(Roo.encode(rdata));
28823                     return;
28824                 }
28825                 var data = rdata.data;
28826                 
28827                 if (this.uploadComplete) {
28828                    Roo.MessageBox.hide();
28829                    return;
28830                 }
28831                    
28832                 if (data){
28833                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28834                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28835                     );
28836                 }
28837                 this.uploadProgress.defer(2000,this);
28838             },
28839        
28840             failure: function(data) {
28841                 Roo.log('progress url failed ');
28842                 Roo.log(data);
28843             },
28844             scope : this
28845         });
28846            
28847     },
28848     
28849     
28850     run : function()
28851     {
28852         // run get Values on the form, so it syncs any secondary forms.
28853         this.form.getValues();
28854         
28855         var o = this.options;
28856         var method = this.getMethod();
28857         var isPost = method == 'POST';
28858         if(o.clientValidation === false || this.form.isValid()){
28859             
28860             if (this.form.progressUrl) {
28861                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28862                     (new Date() * 1) + '' + Math.random());
28863                     
28864             } 
28865             
28866             
28867             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28868                 form:this.form.el.dom,
28869                 url:this.getUrl(!isPost),
28870                 method: method,
28871                 params:isPost ? this.getParams() : null,
28872                 isUpload: this.form.fileUpload
28873             }));
28874             
28875             this.uploadProgress();
28876
28877         }else if (o.clientValidation !== false){ // client validation failed
28878             this.failureType = Roo.form.Action.CLIENT_INVALID;
28879             this.form.afterAction(this, false);
28880         }
28881     },
28882
28883     success : function(response)
28884     {
28885         this.uploadComplete= true;
28886         if (this.haveProgress) {
28887             Roo.MessageBox.hide();
28888         }
28889         
28890         
28891         var result = this.processResponse(response);
28892         if(result === true || result.success){
28893             this.form.afterAction(this, true);
28894             return;
28895         }
28896         if(result.errors){
28897             this.form.markInvalid(result.errors);
28898             this.failureType = Roo.form.Action.SERVER_INVALID;
28899         }
28900         this.form.afterAction(this, false);
28901     },
28902     failure : function(response)
28903     {
28904         this.uploadComplete= true;
28905         if (this.haveProgress) {
28906             Roo.MessageBox.hide();
28907         }
28908         
28909         this.response = response;
28910         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28911         this.form.afterAction(this, false);
28912     },
28913     
28914     handleResponse : function(response){
28915         if(this.form.errorReader){
28916             var rs = this.form.errorReader.read(response);
28917             var errors = [];
28918             if(rs.records){
28919                 for(var i = 0, len = rs.records.length; i < len; i++) {
28920                     var r = rs.records[i];
28921                     errors[i] = r.data;
28922                 }
28923             }
28924             if(errors.length < 1){
28925                 errors = null;
28926             }
28927             return {
28928                 success : rs.success,
28929                 errors : errors
28930             };
28931         }
28932         var ret = false;
28933         try {
28934             ret = Roo.decode(response.responseText);
28935         } catch (e) {
28936             ret = {
28937                 success: false,
28938                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28939                 errors : []
28940             };
28941         }
28942         return ret;
28943         
28944     }
28945 });
28946
28947
28948 Roo.form.Action.Load = function(form, options){
28949     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28950     this.reader = this.form.reader;
28951 };
28952
28953 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28954     type : 'load',
28955
28956     run : function(){
28957         
28958         Roo.Ajax.request(Roo.apply(
28959                 this.createCallback(), {
28960                     method:this.getMethod(),
28961                     url:this.getUrl(false),
28962                     params:this.getParams()
28963         }));
28964     },
28965
28966     success : function(response){
28967         
28968         var result = this.processResponse(response);
28969         if(result === true || !result.success || !result.data){
28970             this.failureType = Roo.form.Action.LOAD_FAILURE;
28971             this.form.afterAction(this, false);
28972             return;
28973         }
28974         this.form.clearInvalid();
28975         this.form.setValues(result.data);
28976         this.form.afterAction(this, true);
28977     },
28978
28979     handleResponse : function(response){
28980         if(this.form.reader){
28981             var rs = this.form.reader.read(response);
28982             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28983             return {
28984                 success : rs.success,
28985                 data : data
28986             };
28987         }
28988         return Roo.decode(response.responseText);
28989     }
28990 });
28991
28992 Roo.form.Action.ACTION_TYPES = {
28993     'load' : Roo.form.Action.Load,
28994     'submit' : Roo.form.Action.Submit
28995 };/*
28996  * Based on:
28997  * Ext JS Library 1.1.1
28998  * Copyright(c) 2006-2007, Ext JS, LLC.
28999  *
29000  * Originally Released Under LGPL - original licence link has changed is not relivant.
29001  *
29002  * Fork - LGPL
29003  * <script type="text/javascript">
29004  */
29005  
29006 /**
29007  * @class Roo.form.Layout
29008  * @extends Roo.Component
29009  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29010  * @constructor
29011  * @param {Object} config Configuration options
29012  */
29013 Roo.form.Layout = function(config){
29014     var xitems = [];
29015     if (config.items) {
29016         xitems = config.items;
29017         delete config.items;
29018     }
29019     Roo.form.Layout.superclass.constructor.call(this, config);
29020     this.stack = [];
29021     Roo.each(xitems, this.addxtype, this);
29022      
29023 };
29024
29025 Roo.extend(Roo.form.Layout, Roo.Component, {
29026     /**
29027      * @cfg {String/Object} autoCreate
29028      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29029      */
29030     /**
29031      * @cfg {String/Object/Function} style
29032      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29033      * a function which returns such a specification.
29034      */
29035     /**
29036      * @cfg {String} labelAlign
29037      * Valid values are "left," "top" and "right" (defaults to "left")
29038      */
29039     /**
29040      * @cfg {Number} labelWidth
29041      * Fixed width in pixels of all field labels (defaults to undefined)
29042      */
29043     /**
29044      * @cfg {Boolean} clear
29045      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29046      */
29047     clear : true,
29048     /**
29049      * @cfg {String} labelSeparator
29050      * The separator to use after field labels (defaults to ':')
29051      */
29052     labelSeparator : ':',
29053     /**
29054      * @cfg {Boolean} hideLabels
29055      * True to suppress the display of field labels in this layout (defaults to false)
29056      */
29057     hideLabels : false,
29058
29059     // private
29060     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29061     
29062     isLayout : true,
29063     
29064     // private
29065     onRender : function(ct, position){
29066         if(this.el){ // from markup
29067             this.el = Roo.get(this.el);
29068         }else {  // generate
29069             var cfg = this.getAutoCreate();
29070             this.el = ct.createChild(cfg, position);
29071         }
29072         if(this.style){
29073             this.el.applyStyles(this.style);
29074         }
29075         if(this.labelAlign){
29076             this.el.addClass('x-form-label-'+this.labelAlign);
29077         }
29078         if(this.hideLabels){
29079             this.labelStyle = "display:none";
29080             this.elementStyle = "padding-left:0;";
29081         }else{
29082             if(typeof this.labelWidth == 'number'){
29083                 this.labelStyle = "width:"+this.labelWidth+"px;";
29084                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29085             }
29086             if(this.labelAlign == 'top'){
29087                 this.labelStyle = "width:auto;";
29088                 this.elementStyle = "padding-left:0;";
29089             }
29090         }
29091         var stack = this.stack;
29092         var slen = stack.length;
29093         if(slen > 0){
29094             if(!this.fieldTpl){
29095                 var t = new Roo.Template(
29096                     '<div class="x-form-item {5}">',
29097                         '<label for="{0}" style="{2}">{1}{4}</label>',
29098                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29099                         '</div>',
29100                     '</div><div class="x-form-clear-left"></div>'
29101                 );
29102                 t.disableFormats = true;
29103                 t.compile();
29104                 Roo.form.Layout.prototype.fieldTpl = t;
29105             }
29106             for(var i = 0; i < slen; i++) {
29107                 if(stack[i].isFormField){
29108                     this.renderField(stack[i]);
29109                 }else{
29110                     this.renderComponent(stack[i]);
29111                 }
29112             }
29113         }
29114         if(this.clear){
29115             this.el.createChild({cls:'x-form-clear'});
29116         }
29117     },
29118
29119     // private
29120     renderField : function(f){
29121         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29122                f.id, //0
29123                f.fieldLabel, //1
29124                f.labelStyle||this.labelStyle||'', //2
29125                this.elementStyle||'', //3
29126                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29127                f.itemCls||this.itemCls||''  //5
29128        ], true).getPrevSibling());
29129     },
29130
29131     // private
29132     renderComponent : function(c){
29133         c.render(c.isLayout ? this.el : this.el.createChild());    
29134     },
29135     /**
29136      * Adds a object form elements (using the xtype property as the factory method.)
29137      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29138      * @param {Object} config 
29139      */
29140     addxtype : function(o)
29141     {
29142         // create the lement.
29143         o.form = this.form;
29144         var fe = Roo.factory(o, Roo.form);
29145         this.form.allItems.push(fe);
29146         this.stack.push(fe);
29147         
29148         if (fe.isFormField) {
29149             this.form.items.add(fe);
29150         }
29151          
29152         return fe;
29153     }
29154 });
29155
29156 /**
29157  * @class Roo.form.Column
29158  * @extends Roo.form.Layout
29159  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29160  * @constructor
29161  * @param {Object} config Configuration options
29162  */
29163 Roo.form.Column = function(config){
29164     Roo.form.Column.superclass.constructor.call(this, config);
29165 };
29166
29167 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29168     /**
29169      * @cfg {Number/String} width
29170      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29171      */
29172     /**
29173      * @cfg {String/Object} autoCreate
29174      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29175      */
29176
29177     // private
29178     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29179
29180     // private
29181     onRender : function(ct, position){
29182         Roo.form.Column.superclass.onRender.call(this, ct, position);
29183         if(this.width){
29184             this.el.setWidth(this.width);
29185         }
29186     }
29187 });
29188
29189
29190 /**
29191  * @class Roo.form.Row
29192  * @extends Roo.form.Layout
29193  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29194  * @constructor
29195  * @param {Object} config Configuration options
29196  */
29197
29198  
29199 Roo.form.Row = function(config){
29200     Roo.form.Row.superclass.constructor.call(this, config);
29201 };
29202  
29203 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29204       /**
29205      * @cfg {Number/String} width
29206      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29207      */
29208     /**
29209      * @cfg {Number/String} height
29210      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29211      */
29212     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29213     
29214     padWidth : 20,
29215     // private
29216     onRender : function(ct, position){
29217         //console.log('row render');
29218         if(!this.rowTpl){
29219             var t = new Roo.Template(
29220                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29221                     '<label for="{0}" style="{2}">{1}{4}</label>',
29222                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29223                     '</div>',
29224                 '</div>'
29225             );
29226             t.disableFormats = true;
29227             t.compile();
29228             Roo.form.Layout.prototype.rowTpl = t;
29229         }
29230         this.fieldTpl = this.rowTpl;
29231         
29232         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29233         var labelWidth = 100;
29234         
29235         if ((this.labelAlign != 'top')) {
29236             if (typeof this.labelWidth == 'number') {
29237                 labelWidth = this.labelWidth
29238             }
29239             this.padWidth =  20 + labelWidth;
29240             
29241         }
29242         
29243         Roo.form.Column.superclass.onRender.call(this, ct, position);
29244         if(this.width){
29245             this.el.setWidth(this.width);
29246         }
29247         if(this.height){
29248             this.el.setHeight(this.height);
29249         }
29250     },
29251     
29252     // private
29253     renderField : function(f){
29254         f.fieldEl = this.fieldTpl.append(this.el, [
29255                f.id, f.fieldLabel,
29256                f.labelStyle||this.labelStyle||'',
29257                this.elementStyle||'',
29258                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29259                f.itemCls||this.itemCls||'',
29260                f.width ? f.width + this.padWidth : 160 + this.padWidth
29261        ],true);
29262     }
29263 });
29264  
29265
29266 /**
29267  * @class Roo.form.FieldSet
29268  * @extends Roo.form.Layout
29269  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29270  * @constructor
29271  * @param {Object} config Configuration options
29272  */
29273 Roo.form.FieldSet = function(config){
29274     Roo.form.FieldSet.superclass.constructor.call(this, config);
29275 };
29276
29277 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29278     /**
29279      * @cfg {String} legend
29280      * The text to display as the legend for the FieldSet (defaults to '')
29281      */
29282     /**
29283      * @cfg {String/Object} autoCreate
29284      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29285      */
29286
29287     // private
29288     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29289
29290     // private
29291     onRender : function(ct, position){
29292         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29293         if(this.legend){
29294             this.setLegend(this.legend);
29295         }
29296     },
29297
29298     // private
29299     setLegend : function(text){
29300         if(this.rendered){
29301             this.el.child('legend').update(text);
29302         }
29303     }
29304 });/*
29305  * Based on:
29306  * Ext JS Library 1.1.1
29307  * Copyright(c) 2006-2007, Ext JS, LLC.
29308  *
29309  * Originally Released Under LGPL - original licence link has changed is not relivant.
29310  *
29311  * Fork - LGPL
29312  * <script type="text/javascript">
29313  */
29314 /**
29315  * @class Roo.form.VTypes
29316  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29317  * @singleton
29318  */
29319 Roo.form.VTypes = function(){
29320     // closure these in so they are only created once.
29321     var alpha = /^[a-zA-Z_]+$/;
29322     var alphanum = /^[a-zA-Z0-9_]+$/;
29323     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29324     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29325
29326     // All these messages and functions are configurable
29327     return {
29328         /**
29329          * The function used to validate email addresses
29330          * @param {String} value The email address
29331          */
29332         'email' : function(v){
29333             return email.test(v);
29334         },
29335         /**
29336          * The error text to display when the email validation function returns false
29337          * @type String
29338          */
29339         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29340         /**
29341          * The keystroke filter mask to be applied on email input
29342          * @type RegExp
29343          */
29344         'emailMask' : /[a-z0-9_\.\-@]/i,
29345
29346         /**
29347          * The function used to validate URLs
29348          * @param {String} value The URL
29349          */
29350         'url' : function(v){
29351             return url.test(v);
29352         },
29353         /**
29354          * The error text to display when the url validation function returns false
29355          * @type String
29356          */
29357         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29358         
29359         /**
29360          * The function used to validate alpha values
29361          * @param {String} value The value
29362          */
29363         'alpha' : function(v){
29364             return alpha.test(v);
29365         },
29366         /**
29367          * The error text to display when the alpha validation function returns false
29368          * @type String
29369          */
29370         'alphaText' : 'This field should only contain letters and _',
29371         /**
29372          * The keystroke filter mask to be applied on alpha input
29373          * @type RegExp
29374          */
29375         'alphaMask' : /[a-z_]/i,
29376
29377         /**
29378          * The function used to validate alphanumeric values
29379          * @param {String} value The value
29380          */
29381         'alphanum' : function(v){
29382             return alphanum.test(v);
29383         },
29384         /**
29385          * The error text to display when the alphanumeric validation function returns false
29386          * @type String
29387          */
29388         'alphanumText' : 'This field should only contain letters, numbers and _',
29389         /**
29390          * The keystroke filter mask to be applied on alphanumeric input
29391          * @type RegExp
29392          */
29393         'alphanumMask' : /[a-z0-9_]/i
29394     };
29395 }();//<script type="text/javascript">
29396
29397 /**
29398  * @class Roo.form.FCKeditor
29399  * @extends Roo.form.TextArea
29400  * Wrapper around the FCKEditor http://www.fckeditor.net
29401  * @constructor
29402  * Creates a new FCKeditor
29403  * @param {Object} config Configuration options
29404  */
29405 Roo.form.FCKeditor = function(config){
29406     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29407     this.addEvents({
29408          /**
29409          * @event editorinit
29410          * Fired when the editor is initialized - you can add extra handlers here..
29411          * @param {FCKeditor} this
29412          * @param {Object} the FCK object.
29413          */
29414         editorinit : true
29415     });
29416     
29417     
29418 };
29419 Roo.form.FCKeditor.editors = { };
29420 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29421 {
29422     //defaultAutoCreate : {
29423     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29424     //},
29425     // private
29426     /**
29427      * @cfg {Object} fck options - see fck manual for details.
29428      */
29429     fckconfig : false,
29430     
29431     /**
29432      * @cfg {Object} fck toolbar set (Basic or Default)
29433      */
29434     toolbarSet : 'Basic',
29435     /**
29436      * @cfg {Object} fck BasePath
29437      */ 
29438     basePath : '/fckeditor/',
29439     
29440     
29441     frame : false,
29442     
29443     value : '',
29444     
29445    
29446     onRender : function(ct, position)
29447     {
29448         if(!this.el){
29449             this.defaultAutoCreate = {
29450                 tag: "textarea",
29451                 style:"width:300px;height:60px;",
29452                 autocomplete: "off"
29453             };
29454         }
29455         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29456         /*
29457         if(this.grow){
29458             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29459             if(this.preventScrollbars){
29460                 this.el.setStyle("overflow", "hidden");
29461             }
29462             this.el.setHeight(this.growMin);
29463         }
29464         */
29465         //console.log('onrender' + this.getId() );
29466         Roo.form.FCKeditor.editors[this.getId()] = this;
29467          
29468
29469         this.replaceTextarea() ;
29470         
29471     },
29472     
29473     getEditor : function() {
29474         return this.fckEditor;
29475     },
29476     /**
29477      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29478      * @param {Mixed} value The value to set
29479      */
29480     
29481     
29482     setValue : function(value)
29483     {
29484         //console.log('setValue: ' + value);
29485         
29486         if(typeof(value) == 'undefined') { // not sure why this is happending...
29487             return;
29488         }
29489         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29490         
29491         //if(!this.el || !this.getEditor()) {
29492         //    this.value = value;
29493             //this.setValue.defer(100,this,[value]);    
29494         //    return;
29495         //} 
29496         
29497         if(!this.getEditor()) {
29498             return;
29499         }
29500         
29501         this.getEditor().SetData(value);
29502         
29503         //
29504
29505     },
29506
29507     /**
29508      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29509      * @return {Mixed} value The field value
29510      */
29511     getValue : function()
29512     {
29513         
29514         if (this.frame && this.frame.dom.style.display == 'none') {
29515             return Roo.form.FCKeditor.superclass.getValue.call(this);
29516         }
29517         
29518         if(!this.el || !this.getEditor()) {
29519            
29520            // this.getValue.defer(100,this); 
29521             return this.value;
29522         }
29523        
29524         
29525         var value=this.getEditor().GetData();
29526         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29527         return Roo.form.FCKeditor.superclass.getValue.call(this);
29528         
29529
29530     },
29531
29532     /**
29533      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29534      * @return {Mixed} value The field value
29535      */
29536     getRawValue : function()
29537     {
29538         if (this.frame && this.frame.dom.style.display == 'none') {
29539             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29540         }
29541         
29542         if(!this.el || !this.getEditor()) {
29543             //this.getRawValue.defer(100,this); 
29544             return this.value;
29545             return;
29546         }
29547         
29548         
29549         
29550         var value=this.getEditor().GetData();
29551         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29552         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29553          
29554     },
29555     
29556     setSize : function(w,h) {
29557         
29558         
29559         
29560         //if (this.frame && this.frame.dom.style.display == 'none') {
29561         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29562         //    return;
29563         //}
29564         //if(!this.el || !this.getEditor()) {
29565         //    this.setSize.defer(100,this, [w,h]); 
29566         //    return;
29567         //}
29568         
29569         
29570         
29571         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29572         
29573         this.frame.dom.setAttribute('width', w);
29574         this.frame.dom.setAttribute('height', h);
29575         this.frame.setSize(w,h);
29576         
29577     },
29578     
29579     toggleSourceEdit : function(value) {
29580         
29581       
29582          
29583         this.el.dom.style.display = value ? '' : 'none';
29584         this.frame.dom.style.display = value ?  'none' : '';
29585         
29586     },
29587     
29588     
29589     focus: function(tag)
29590     {
29591         if (this.frame.dom.style.display == 'none') {
29592             return Roo.form.FCKeditor.superclass.focus.call(this);
29593         }
29594         if(!this.el || !this.getEditor()) {
29595             this.focus.defer(100,this, [tag]); 
29596             return;
29597         }
29598         
29599         
29600         
29601         
29602         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29603         this.getEditor().Focus();
29604         if (tgs.length) {
29605             if (!this.getEditor().Selection.GetSelection()) {
29606                 this.focus.defer(100,this, [tag]); 
29607                 return;
29608             }
29609             
29610             
29611             var r = this.getEditor().EditorDocument.createRange();
29612             r.setStart(tgs[0],0);
29613             r.setEnd(tgs[0],0);
29614             this.getEditor().Selection.GetSelection().removeAllRanges();
29615             this.getEditor().Selection.GetSelection().addRange(r);
29616             this.getEditor().Focus();
29617         }
29618         
29619     },
29620     
29621     
29622     
29623     replaceTextarea : function()
29624     {
29625         if ( document.getElementById( this.getId() + '___Frame' ) )
29626             return ;
29627         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29628         //{
29629             // We must check the elements firstly using the Id and then the name.
29630         var oTextarea = document.getElementById( this.getId() );
29631         
29632         var colElementsByName = document.getElementsByName( this.getId() ) ;
29633          
29634         oTextarea.style.display = 'none' ;
29635
29636         if ( oTextarea.tabIndex ) {            
29637             this.TabIndex = oTextarea.tabIndex ;
29638         }
29639         
29640         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29641         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29642         this.frame = Roo.get(this.getId() + '___Frame')
29643     },
29644     
29645     _getConfigHtml : function()
29646     {
29647         var sConfig = '' ;
29648
29649         for ( var o in this.fckconfig ) {
29650             sConfig += sConfig.length > 0  ? '&amp;' : '';
29651             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29652         }
29653
29654         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29655     },
29656     
29657     
29658     _getIFrameHtml : function()
29659     {
29660         var sFile = 'fckeditor.html' ;
29661         /* no idea what this is about..
29662         try
29663         {
29664             if ( (/fcksource=true/i).test( window.top.location.search ) )
29665                 sFile = 'fckeditor.original.html' ;
29666         }
29667         catch (e) { 
29668         */
29669
29670         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29671         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29672         
29673         
29674         var html = '<iframe id="' + this.getId() +
29675             '___Frame" src="' + sLink +
29676             '" width="' + this.width +
29677             '" height="' + this.height + '"' +
29678             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29679             ' frameborder="0" scrolling="no"></iframe>' ;
29680
29681         return html ;
29682     },
29683     
29684     _insertHtmlBefore : function( html, element )
29685     {
29686         if ( element.insertAdjacentHTML )       {
29687             // IE
29688             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29689         } else { // Gecko
29690             var oRange = document.createRange() ;
29691             oRange.setStartBefore( element ) ;
29692             var oFragment = oRange.createContextualFragment( html );
29693             element.parentNode.insertBefore( oFragment, element ) ;
29694         }
29695     }
29696     
29697     
29698   
29699     
29700     
29701     
29702     
29703
29704 });
29705
29706 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29707
29708 function FCKeditor_OnComplete(editorInstance){
29709     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29710     f.fckEditor = editorInstance;
29711     //console.log("loaded");
29712     f.fireEvent('editorinit', f, editorInstance);
29713
29714   
29715
29716  
29717
29718
29719
29720
29721
29722
29723
29724
29725
29726
29727
29728
29729
29730
29731
29732 //<script type="text/javascript">
29733 /**
29734  * @class Roo.form.GridField
29735  * @extends Roo.form.Field
29736  * Embed a grid (or editable grid into a form)
29737  * STATUS ALPHA
29738  * 
29739  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29740  * it needs 
29741  * xgrid.store = Roo.data.Store
29742  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29743  * xgrid.store.reader = Roo.data.JsonReader 
29744  * 
29745  * 
29746  * @constructor
29747  * Creates a new GridField
29748  * @param {Object} config Configuration options
29749  */
29750 Roo.form.GridField = function(config){
29751     Roo.form.GridField.superclass.constructor.call(this, config);
29752      
29753 };
29754
29755 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29756     /**
29757      * @cfg {Number} width  - used to restrict width of grid..
29758      */
29759     width : 100,
29760     /**
29761      * @cfg {Number} height - used to restrict height of grid..
29762      */
29763     height : 50,
29764      /**
29765      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29766          * 
29767          *}
29768      */
29769     xgrid : false, 
29770     /**
29771      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29772      * {tag: "input", type: "checkbox", autocomplete: "off"})
29773      */
29774    // defaultAutoCreate : { tag: 'div' },
29775     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29776     /**
29777      * @cfg {String} addTitle Text to include for adding a title.
29778      */
29779     addTitle : false,
29780     //
29781     onResize : function(){
29782         Roo.form.Field.superclass.onResize.apply(this, arguments);
29783     },
29784
29785     initEvents : function(){
29786         // Roo.form.Checkbox.superclass.initEvents.call(this);
29787         // has no events...
29788        
29789     },
29790
29791
29792     getResizeEl : function(){
29793         return this.wrap;
29794     },
29795
29796     getPositionEl : function(){
29797         return this.wrap;
29798     },
29799
29800     // private
29801     onRender : function(ct, position){
29802         
29803         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29804         var style = this.style;
29805         delete this.style;
29806         
29807         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29808         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29809         this.viewEl = this.wrap.createChild({ tag: 'div' });
29810         if (style) {
29811             this.viewEl.applyStyles(style);
29812         }
29813         if (this.width) {
29814             this.viewEl.setWidth(this.width);
29815         }
29816         if (this.height) {
29817             this.viewEl.setHeight(this.height);
29818         }
29819         //if(this.inputValue !== undefined){
29820         //this.setValue(this.value);
29821         
29822         
29823         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29824         
29825         
29826         this.grid.render();
29827         this.grid.getDataSource().on('remove', this.refreshValue, this);
29828         this.grid.getDataSource().on('update', this.refreshValue, this);
29829         this.grid.on('afteredit', this.refreshValue, this);
29830  
29831     },
29832      
29833     
29834     /**
29835      * Sets the value of the item. 
29836      * @param {String} either an object  or a string..
29837      */
29838     setValue : function(v){
29839         //this.value = v;
29840         v = v || []; // empty set..
29841         // this does not seem smart - it really only affects memoryproxy grids..
29842         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29843             var ds = this.grid.getDataSource();
29844             // assumes a json reader..
29845             var data = {}
29846             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29847             ds.loadData( data);
29848         }
29849         // clear selection so it does not get stale.
29850         if (this.grid.sm) { 
29851             this.grid.sm.clearSelections();
29852         }
29853         
29854         Roo.form.GridField.superclass.setValue.call(this, v);
29855         this.refreshValue();
29856         // should load data in the grid really....
29857     },
29858     
29859     // private
29860     refreshValue: function() {
29861          var val = [];
29862         this.grid.getDataSource().each(function(r) {
29863             val.push(r.data);
29864         });
29865         this.el.dom.value = Roo.encode(val);
29866     }
29867     
29868      
29869     
29870     
29871 });/*
29872  * Based on:
29873  * Ext JS Library 1.1.1
29874  * Copyright(c) 2006-2007, Ext JS, LLC.
29875  *
29876  * Originally Released Under LGPL - original licence link has changed is not relivant.
29877  *
29878  * Fork - LGPL
29879  * <script type="text/javascript">
29880  */
29881 /**
29882  * @class Roo.form.DisplayField
29883  * @extends Roo.form.Field
29884  * A generic Field to display non-editable data.
29885  * @constructor
29886  * Creates a new Display Field item.
29887  * @param {Object} config Configuration options
29888  */
29889 Roo.form.DisplayField = function(config){
29890     Roo.form.DisplayField.superclass.constructor.call(this, config);
29891     
29892 };
29893
29894 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29895     inputType:      'hidden',
29896     allowBlank:     true,
29897     readOnly:         true,
29898     
29899  
29900     /**
29901      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29902      */
29903     focusClass : undefined,
29904     /**
29905      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29906      */
29907     fieldClass: 'x-form-field',
29908     
29909      /**
29910      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29911      */
29912     valueRenderer: undefined,
29913     
29914     width: 100,
29915     /**
29916      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29917      * {tag: "input", type: "checkbox", autocomplete: "off"})
29918      */
29919      
29920  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29921
29922     onResize : function(){
29923         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29924         
29925     },
29926
29927     initEvents : function(){
29928         // Roo.form.Checkbox.superclass.initEvents.call(this);
29929         // has no events...
29930        
29931     },
29932
29933
29934     getResizeEl : function(){
29935         return this.wrap;
29936     },
29937
29938     getPositionEl : function(){
29939         return this.wrap;
29940     },
29941
29942     // private
29943     onRender : function(ct, position){
29944         
29945         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29946         //if(this.inputValue !== undefined){
29947         this.wrap = this.el.wrap();
29948         
29949         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29950         
29951         if (this.bodyStyle) {
29952             this.viewEl.applyStyles(this.bodyStyle);
29953         }
29954         //this.viewEl.setStyle('padding', '2px');
29955         
29956         this.setValue(this.value);
29957         
29958     },
29959 /*
29960     // private
29961     initValue : Roo.emptyFn,
29962
29963   */
29964
29965         // private
29966     onClick : function(){
29967         
29968     },
29969
29970     /**
29971      * Sets the checked state of the checkbox.
29972      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29973      */
29974     setValue : function(v){
29975         this.value = v;
29976         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29977         // this might be called before we have a dom element..
29978         if (!this.viewEl) {
29979             return;
29980         }
29981         this.viewEl.dom.innerHTML = html;
29982         Roo.form.DisplayField.superclass.setValue.call(this, v);
29983
29984     }
29985 });/*
29986  * 
29987  * Licence- LGPL
29988  * 
29989  */
29990
29991 /**
29992  * @class Roo.form.DayPicker
29993  * @extends Roo.form.Field
29994  * A Day picker show [M] [T] [W] ....
29995  * @constructor
29996  * Creates a new Day Picker
29997  * @param {Object} config Configuration options
29998  */
29999 Roo.form.DayPicker= function(config){
30000     Roo.form.DayPicker.superclass.constructor.call(this, config);
30001      
30002 };
30003
30004 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30005     /**
30006      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30007      */
30008     focusClass : undefined,
30009     /**
30010      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30011      */
30012     fieldClass: "x-form-field",
30013    
30014     /**
30015      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30016      * {tag: "input", type: "checkbox", autocomplete: "off"})
30017      */
30018     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30019     
30020    
30021     actionMode : 'viewEl', 
30022     //
30023     // private
30024  
30025     inputType : 'hidden',
30026     
30027      
30028     inputElement: false, // real input element?
30029     basedOn: false, // ????
30030     
30031     isFormField: true, // not sure where this is needed!!!!
30032
30033     onResize : function(){
30034         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30035         if(!this.boxLabel){
30036             this.el.alignTo(this.wrap, 'c-c');
30037         }
30038     },
30039
30040     initEvents : function(){
30041         Roo.form.Checkbox.superclass.initEvents.call(this);
30042         this.el.on("click", this.onClick,  this);
30043         this.el.on("change", this.onClick,  this);
30044     },
30045
30046
30047     getResizeEl : function(){
30048         return this.wrap;
30049     },
30050
30051     getPositionEl : function(){
30052         return this.wrap;
30053     },
30054
30055     
30056     // private
30057     onRender : function(ct, position){
30058         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30059        
30060         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30061         
30062         var r1 = '<table><tr>';
30063         var r2 = '<tr class="x-form-daypick-icons">';
30064         for (var i=0; i < 7; i++) {
30065             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30066             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30067         }
30068         
30069         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30070         viewEl.select('img').on('click', this.onClick, this);
30071         this.viewEl = viewEl;   
30072         
30073         
30074         // this will not work on Chrome!!!
30075         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30076         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30077         
30078         
30079           
30080
30081     },
30082
30083     // private
30084     initValue : Roo.emptyFn,
30085
30086     /**
30087      * Returns the checked state of the checkbox.
30088      * @return {Boolean} True if checked, else false
30089      */
30090     getValue : function(){
30091         return this.el.dom.value;
30092         
30093     },
30094
30095         // private
30096     onClick : function(e){ 
30097         //this.setChecked(!this.checked);
30098         Roo.get(e.target).toggleClass('x-menu-item-checked');
30099         this.refreshValue();
30100         //if(this.el.dom.checked != this.checked){
30101         //    this.setValue(this.el.dom.checked);
30102        // }
30103     },
30104     
30105     // private
30106     refreshValue : function()
30107     {
30108         var val = '';
30109         this.viewEl.select('img',true).each(function(e,i,n)  {
30110             val += e.is(".x-menu-item-checked") ? String(n) : '';
30111         });
30112         this.setValue(val, true);
30113     },
30114
30115     /**
30116      * Sets the checked state of the checkbox.
30117      * On is always based on a string comparison between inputValue and the param.
30118      * @param {Boolean/String} value - the value to set 
30119      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30120      */
30121     setValue : function(v,suppressEvent){
30122         if (!this.el.dom) {
30123             return;
30124         }
30125         var old = this.el.dom.value ;
30126         this.el.dom.value = v;
30127         if (suppressEvent) {
30128             return ;
30129         }
30130          
30131         // update display..
30132         this.viewEl.select('img',true).each(function(e,i,n)  {
30133             
30134             var on = e.is(".x-menu-item-checked");
30135             var newv = v.indexOf(String(n)) > -1;
30136             if (on != newv) {
30137                 e.toggleClass('x-menu-item-checked');
30138             }
30139             
30140         });
30141         
30142         
30143         this.fireEvent('change', this, v, old);
30144         
30145         
30146     },
30147    
30148     // handle setting of hidden value by some other method!!?!?
30149     setFromHidden: function()
30150     {
30151         if(!this.el){
30152             return;
30153         }
30154         //console.log("SET FROM HIDDEN");
30155         //alert('setFrom hidden');
30156         this.setValue(this.el.dom.value);
30157     },
30158     
30159     onDestroy : function()
30160     {
30161         if(this.viewEl){
30162             Roo.get(this.viewEl).remove();
30163         }
30164          
30165         Roo.form.DayPicker.superclass.onDestroy.call(this);
30166     }
30167
30168 });/*
30169  * RooJS Library 1.1.1
30170  * Copyright(c) 2008-2011  Alan Knowles
30171  *
30172  * License - LGPL
30173  */
30174  
30175
30176 /**
30177  * @class Roo.form.ComboCheck
30178  * @extends Roo.form.ComboBox
30179  * A combobox for multiple select items.
30180  *
30181  * FIXME - could do with a reset button..
30182  * 
30183  * @constructor
30184  * Create a new ComboCheck
30185  * @param {Object} config Configuration options
30186  */
30187 Roo.form.ComboCheck = function(config){
30188     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30189     // should verify some data...
30190     // like
30191     // hiddenName = required..
30192     // displayField = required
30193     // valudField == required
30194     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30195     var _t = this;
30196     Roo.each(req, function(e) {
30197         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30198             throw "Roo.form.ComboCheck : missing value for: " + e;
30199         }
30200     });
30201     
30202     
30203 };
30204
30205 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30206      
30207      
30208     editable : false,
30209      
30210     selectedClass: 'x-menu-item-checked', 
30211     
30212     // private
30213     onRender : function(ct, position){
30214         var _t = this;
30215         
30216         
30217         
30218         if(!this.tpl){
30219             var cls = 'x-combo-list';
30220
30221             
30222             this.tpl =  new Roo.Template({
30223                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30224                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30225                    '<span>{' + this.displayField + '}</span>' +
30226                     '</div>' 
30227                 
30228             });
30229         }
30230  
30231         
30232         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30233         this.view.singleSelect = false;
30234         this.view.multiSelect = true;
30235         this.view.toggleSelect = true;
30236         this.pageTb.add(new Roo.Toolbar.Fill(), {
30237             
30238             text: 'Done',
30239             handler: function()
30240             {
30241                 _t.collapse();
30242             }
30243         });
30244     },
30245     
30246     onViewOver : function(e, t){
30247         // do nothing...
30248         return;
30249         
30250     },
30251     
30252     onViewClick : function(doFocus,index){
30253         return;
30254         
30255     },
30256     select: function () {
30257         //Roo.log("SELECT CALLED");
30258     },
30259      
30260     selectByValue : function(xv, scrollIntoView){
30261         var ar = this.getValueArray();
30262         var sels = [];
30263         
30264         Roo.each(ar, function(v) {
30265             if(v === undefined || v === null){
30266                 return;
30267             }
30268             var r = this.findRecord(this.valueField, v);
30269             if(r){
30270                 sels.push(this.store.indexOf(r))
30271                 
30272             }
30273         },this);
30274         this.view.select(sels);
30275         return false;
30276     },
30277     
30278     
30279     
30280     onSelect : function(record, index){
30281        // Roo.log("onselect Called");
30282        // this is only called by the clear button now..
30283         this.view.clearSelections();
30284         this.setValue('[]');
30285         if (this.value != this.valueBefore) {
30286             this.fireEvent('change', this, this.value, this.valueBefore);
30287         }
30288     },
30289     getValueArray : function()
30290     {
30291         var ar = [] ;
30292         
30293         try {
30294             //Roo.log(this.value);
30295             if (typeof(this.value) == 'undefined') {
30296                 return [];
30297             }
30298             var ar = Roo.decode(this.value);
30299             return  ar instanceof Array ? ar : []; //?? valid?
30300             
30301         } catch(e) {
30302             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30303             return [];
30304         }
30305          
30306     },
30307     expand : function ()
30308     {
30309         Roo.form.ComboCheck.superclass.expand.call(this);
30310         this.valueBefore = this.value;
30311         
30312
30313     },
30314     
30315     collapse : function(){
30316         Roo.form.ComboCheck.superclass.collapse.call(this);
30317         var sl = this.view.getSelectedIndexes();
30318         var st = this.store;
30319         var nv = [];
30320         var tv = [];
30321         var r;
30322         Roo.each(sl, function(i) {
30323             r = st.getAt(i);
30324             nv.push(r.get(this.valueField));
30325         },this);
30326         this.setValue(Roo.encode(nv));
30327         if (this.value != this.valueBefore) {
30328
30329             this.fireEvent('change', this, this.value, this.valueBefore);
30330         }
30331         
30332     },
30333     
30334     setValue : function(v){
30335         // Roo.log(v);
30336         this.value = v;
30337         
30338         var vals = this.getValueArray();
30339         var tv = [];
30340         Roo.each(vals, function(k) {
30341             var r = this.findRecord(this.valueField, k);
30342             if(r){
30343                 tv.push(r.data[this.displayField]);
30344             }else if(this.valueNotFoundText !== undefined){
30345                 tv.push( this.valueNotFoundText );
30346             }
30347         },this);
30348        // Roo.log(tv);
30349         
30350         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30351         this.hiddenField.value = v;
30352         this.value = v;
30353     }
30354     
30355 });//<script type="text/javasscript">
30356  
30357
30358 /**
30359  * @class Roo.DDView
30360  * A DnD enabled version of Roo.View.
30361  * @param {Element/String} container The Element in which to create the View.
30362  * @param {String} tpl The template string used to create the markup for each element of the View
30363  * @param {Object} config The configuration properties. These include all the config options of
30364  * {@link Roo.View} plus some specific to this class.<br>
30365  * <p>
30366  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30367  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30368  * <p>
30369  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30370 .x-view-drag-insert-above {
30371         border-top:1px dotted #3366cc;
30372 }
30373 .x-view-drag-insert-below {
30374         border-bottom:1px dotted #3366cc;
30375 }
30376 </code></pre>
30377  * 
30378  */
30379  
30380 Roo.DDView = function(container, tpl, config) {
30381     Roo.DDView.superclass.constructor.apply(this, arguments);
30382     this.getEl().setStyle("outline", "0px none");
30383     this.getEl().unselectable();
30384     if (this.dragGroup) {
30385                 this.setDraggable(this.dragGroup.split(","));
30386     }
30387     if (this.dropGroup) {
30388                 this.setDroppable(this.dropGroup.split(","));
30389     }
30390     if (this.deletable) {
30391         this.setDeletable();
30392     }
30393     this.isDirtyFlag = false;
30394         this.addEvents({
30395                 "drop" : true
30396         });
30397 };
30398
30399 Roo.extend(Roo.DDView, Roo.View, {
30400 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30401 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30402 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30403 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30404
30405         isFormField: true,
30406
30407         reset: Roo.emptyFn,
30408         
30409         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30410
30411         validate: function() {
30412                 return true;
30413         },
30414         
30415         destroy: function() {
30416                 this.purgeListeners();
30417                 this.getEl.removeAllListeners();
30418                 this.getEl().remove();
30419                 if (this.dragZone) {
30420                         if (this.dragZone.destroy) {
30421                                 this.dragZone.destroy();
30422                         }
30423                 }
30424                 if (this.dropZone) {
30425                         if (this.dropZone.destroy) {
30426                                 this.dropZone.destroy();
30427                         }
30428                 }
30429         },
30430
30431 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30432         getName: function() {
30433                 return this.name;
30434         },
30435
30436 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30437         setValue: function(v) {
30438                 if (!this.store) {
30439                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30440                 }
30441                 var data = {};
30442                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30443                 this.store.proxy = new Roo.data.MemoryProxy(data);
30444                 this.store.load();
30445         },
30446
30447 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30448         getValue: function() {
30449                 var result = '(';
30450                 this.store.each(function(rec) {
30451                         result += rec.id + ',';
30452                 });
30453                 return result.substr(0, result.length - 1) + ')';
30454         },
30455         
30456         getIds: function() {
30457                 var i = 0, result = new Array(this.store.getCount());
30458                 this.store.each(function(rec) {
30459                         result[i++] = rec.id;
30460                 });
30461                 return result;
30462         },
30463         
30464         isDirty: function() {
30465                 return this.isDirtyFlag;
30466         },
30467
30468 /**
30469  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30470  *      whole Element becomes the target, and this causes the drop gesture to append.
30471  */
30472     getTargetFromEvent : function(e) {
30473                 var target = e.getTarget();
30474                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30475                 target = target.parentNode;
30476                 }
30477                 if (!target) {
30478                         target = this.el.dom.lastChild || this.el.dom;
30479                 }
30480                 return target;
30481     },
30482
30483 /**
30484  *      Create the drag data which consists of an object which has the property "ddel" as
30485  *      the drag proxy element. 
30486  */
30487     getDragData : function(e) {
30488         var target = this.findItemFromChild(e.getTarget());
30489                 if(target) {
30490                         this.handleSelection(e);
30491                         var selNodes = this.getSelectedNodes();
30492             var dragData = {
30493                 source: this,
30494                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30495                 nodes: selNodes,
30496                 records: []
30497                         };
30498                         var selectedIndices = this.getSelectedIndexes();
30499                         for (var i = 0; i < selectedIndices.length; i++) {
30500                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30501                         }
30502                         if (selNodes.length == 1) {
30503                                 dragData.ddel = target.cloneNode(true); // the div element
30504                         } else {
30505                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30506                                 div.className = 'multi-proxy';
30507                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30508                                         div.appendChild(selNodes[i].cloneNode(true));
30509                                 }
30510                                 dragData.ddel = div;
30511                         }
30512             //console.log(dragData)
30513             //console.log(dragData.ddel.innerHTML)
30514                         return dragData;
30515                 }
30516         //console.log('nodragData')
30517                 return false;
30518     },
30519     
30520 /**     Specify to which ddGroup items in this DDView may be dragged. */
30521     setDraggable: function(ddGroup) {
30522         if (ddGroup instanceof Array) {
30523                 Roo.each(ddGroup, this.setDraggable, this);
30524                 return;
30525         }
30526         if (this.dragZone) {
30527                 this.dragZone.addToGroup(ddGroup);
30528         } else {
30529                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30530                                 containerScroll: true,
30531                                 ddGroup: ddGroup 
30532
30533                         });
30534 //                      Draggability implies selection. DragZone's mousedown selects the element.
30535                         if (!this.multiSelect) { this.singleSelect = true; }
30536
30537 //                      Wire the DragZone's handlers up to methods in *this*
30538                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30539                 }
30540     },
30541
30542 /**     Specify from which ddGroup this DDView accepts drops. */
30543     setDroppable: function(ddGroup) {
30544         if (ddGroup instanceof Array) {
30545                 Roo.each(ddGroup, this.setDroppable, this);
30546                 return;
30547         }
30548         if (this.dropZone) {
30549                 this.dropZone.addToGroup(ddGroup);
30550         } else {
30551                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30552                                 containerScroll: true,
30553                                 ddGroup: ddGroup
30554                         });
30555
30556 //                      Wire the DropZone's handlers up to methods in *this*
30557                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30558                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30559                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30560                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30561                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30562                 }
30563     },
30564
30565 /**     Decide whether to drop above or below a View node. */
30566     getDropPoint : function(e, n, dd){
30567         if (n == this.el.dom) { return "above"; }
30568                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30569                 var c = t + (b - t) / 2;
30570                 var y = Roo.lib.Event.getPageY(e);
30571                 if(y <= c) {
30572                         return "above";
30573                 }else{
30574                         return "below";
30575                 }
30576     },
30577
30578     onNodeEnter : function(n, dd, e, data){
30579                 return false;
30580     },
30581     
30582     onNodeOver : function(n, dd, e, data){
30583                 var pt = this.getDropPoint(e, n, dd);
30584                 // set the insert point style on the target node
30585                 var dragElClass = this.dropNotAllowed;
30586                 if (pt) {
30587                         var targetElClass;
30588                         if (pt == "above"){
30589                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30590                                 targetElClass = "x-view-drag-insert-above";
30591                         } else {
30592                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30593                                 targetElClass = "x-view-drag-insert-below";
30594                         }
30595                         if (this.lastInsertClass != targetElClass){
30596                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30597                                 this.lastInsertClass = targetElClass;
30598                         }
30599                 }
30600                 return dragElClass;
30601         },
30602
30603     onNodeOut : function(n, dd, e, data){
30604                 this.removeDropIndicators(n);
30605     },
30606
30607     onNodeDrop : function(n, dd, e, data){
30608         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30609                 return false;
30610         }
30611         var pt = this.getDropPoint(e, n, dd);
30612                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30613                 if (pt == "below") { insertAt++; }
30614                 for (var i = 0; i < data.records.length; i++) {
30615                         var r = data.records[i];
30616                         var dup = this.store.getById(r.id);
30617                         if (dup && (dd != this.dragZone)) {
30618                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30619                         } else {
30620                                 if (data.copy) {
30621                                         this.store.insert(insertAt++, r.copy());
30622                                 } else {
30623                                         data.source.isDirtyFlag = true;
30624                                         r.store.remove(r);
30625                                         this.store.insert(insertAt++, r);
30626                                 }
30627                                 this.isDirtyFlag = true;
30628                         }
30629                 }
30630                 this.dragZone.cachedTarget = null;
30631                 return true;
30632     },
30633
30634     removeDropIndicators : function(n){
30635                 if(n){
30636                         Roo.fly(n).removeClass([
30637                                 "x-view-drag-insert-above",
30638                                 "x-view-drag-insert-below"]);
30639                         this.lastInsertClass = "_noclass";
30640                 }
30641     },
30642
30643 /**
30644  *      Utility method. Add a delete option to the DDView's context menu.
30645  *      @param {String} imageUrl The URL of the "delete" icon image.
30646  */
30647         setDeletable: function(imageUrl) {
30648                 if (!this.singleSelect && !this.multiSelect) {
30649                         this.singleSelect = true;
30650                 }
30651                 var c = this.getContextMenu();
30652                 this.contextMenu.on("itemclick", function(item) {
30653                         switch (item.id) {
30654                                 case "delete":
30655                                         this.remove(this.getSelectedIndexes());
30656                                         break;
30657                         }
30658                 }, this);
30659                 this.contextMenu.add({
30660                         icon: imageUrl,
30661                         id: "delete",
30662                         text: 'Delete'
30663                 });
30664         },
30665         
30666 /**     Return the context menu for this DDView. */
30667         getContextMenu: function() {
30668                 if (!this.contextMenu) {
30669 //                      Create the View's context menu
30670                         this.contextMenu = new Roo.menu.Menu({
30671                                 id: this.id + "-contextmenu"
30672                         });
30673                         this.el.on("contextmenu", this.showContextMenu, this);
30674                 }
30675                 return this.contextMenu;
30676         },
30677         
30678         disableContextMenu: function() {
30679                 if (this.contextMenu) {
30680                         this.el.un("contextmenu", this.showContextMenu, this);
30681                 }
30682         },
30683
30684         showContextMenu: function(e, item) {
30685         item = this.findItemFromChild(e.getTarget());
30686                 if (item) {
30687                         e.stopEvent();
30688                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30689                         this.contextMenu.showAt(e.getXY());
30690             }
30691     },
30692
30693 /**
30694  *      Remove {@link Roo.data.Record}s at the specified indices.
30695  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30696  */
30697     remove: function(selectedIndices) {
30698                 selectedIndices = [].concat(selectedIndices);
30699                 for (var i = 0; i < selectedIndices.length; i++) {
30700                         var rec = this.store.getAt(selectedIndices[i]);
30701                         this.store.remove(rec);
30702                 }
30703     },
30704
30705 /**
30706  *      Double click fires the event, but also, if this is draggable, and there is only one other
30707  *      related DropZone, it transfers the selected node.
30708  */
30709     onDblClick : function(e){
30710         var item = this.findItemFromChild(e.getTarget());
30711         if(item){
30712             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30713                 return false;
30714             }
30715             if (this.dragGroup) {
30716                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30717                     while (targets.indexOf(this.dropZone) > -1) {
30718                             targets.remove(this.dropZone);
30719                                 }
30720                     if (targets.length == 1) {
30721                                         this.dragZone.cachedTarget = null;
30722                         var el = Roo.get(targets[0].getEl());
30723                         var box = el.getBox(true);
30724                         targets[0].onNodeDrop(el.dom, {
30725                                 target: el.dom,
30726                                 xy: [box.x, box.y + box.height - 1]
30727                         }, null, this.getDragData(e));
30728                     }
30729                 }
30730         }
30731     },
30732     
30733     handleSelection: function(e) {
30734                 this.dragZone.cachedTarget = null;
30735         var item = this.findItemFromChild(e.getTarget());
30736         if (!item) {
30737                 this.clearSelections(true);
30738                 return;
30739         }
30740                 if (item && (this.multiSelect || this.singleSelect)){
30741                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30742                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30743                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30744                                 this.unselect(item);
30745                         } else {
30746                                 this.select(item, this.multiSelect && e.ctrlKey);
30747                                 this.lastSelection = item;
30748                         }
30749                 }
30750     },
30751
30752     onItemClick : function(item, index, e){
30753                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30754                         return false;
30755                 }
30756                 return true;
30757     },
30758
30759     unselect : function(nodeInfo, suppressEvent){
30760                 var node = this.getNode(nodeInfo);
30761                 if(node && this.isSelected(node)){
30762                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30763                                 Roo.fly(node).removeClass(this.selectedClass);
30764                                 this.selections.remove(node);
30765                                 if(!suppressEvent){
30766                                         this.fireEvent("selectionchange", this, this.selections);
30767                                 }
30768                         }
30769                 }
30770     }
30771 });
30772 /*
30773  * Based on:
30774  * Ext JS Library 1.1.1
30775  * Copyright(c) 2006-2007, Ext JS, LLC.
30776  *
30777  * Originally Released Under LGPL - original licence link has changed is not relivant.
30778  *
30779  * Fork - LGPL
30780  * <script type="text/javascript">
30781  */
30782  
30783 /**
30784  * @class Roo.LayoutManager
30785  * @extends Roo.util.Observable
30786  * Base class for layout managers.
30787  */
30788 Roo.LayoutManager = function(container, config){
30789     Roo.LayoutManager.superclass.constructor.call(this);
30790     this.el = Roo.get(container);
30791     // ie scrollbar fix
30792     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30793         document.body.scroll = "no";
30794     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30795         this.el.position('relative');
30796     }
30797     this.id = this.el.id;
30798     this.el.addClass("x-layout-container");
30799     /** false to disable window resize monitoring @type Boolean */
30800     this.monitorWindowResize = true;
30801     this.regions = {};
30802     this.addEvents({
30803         /**
30804          * @event layout
30805          * Fires when a layout is performed. 
30806          * @param {Roo.LayoutManager} this
30807          */
30808         "layout" : true,
30809         /**
30810          * @event regionresized
30811          * Fires when the user resizes a region. 
30812          * @param {Roo.LayoutRegion} region The resized region
30813          * @param {Number} newSize The new size (width for east/west, height for north/south)
30814          */
30815         "regionresized" : true,
30816         /**
30817          * @event regioncollapsed
30818          * Fires when a region is collapsed. 
30819          * @param {Roo.LayoutRegion} region The collapsed region
30820          */
30821         "regioncollapsed" : true,
30822         /**
30823          * @event regionexpanded
30824          * Fires when a region is expanded.  
30825          * @param {Roo.LayoutRegion} region The expanded region
30826          */
30827         "regionexpanded" : true
30828     });
30829     this.updating = false;
30830     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30831 };
30832
30833 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30834     /**
30835      * Returns true if this layout is currently being updated
30836      * @return {Boolean}
30837      */
30838     isUpdating : function(){
30839         return this.updating; 
30840     },
30841     
30842     /**
30843      * Suspend the LayoutManager from doing auto-layouts while
30844      * making multiple add or remove calls
30845      */
30846     beginUpdate : function(){
30847         this.updating = true;    
30848     },
30849     
30850     /**
30851      * Restore auto-layouts and optionally disable the manager from performing a layout
30852      * @param {Boolean} noLayout true to disable a layout update 
30853      */
30854     endUpdate : function(noLayout){
30855         this.updating = false;
30856         if(!noLayout){
30857             this.layout();
30858         }    
30859     },
30860     
30861     layout: function(){
30862         
30863     },
30864     
30865     onRegionResized : function(region, newSize){
30866         this.fireEvent("regionresized", region, newSize);
30867         this.layout();
30868     },
30869     
30870     onRegionCollapsed : function(region){
30871         this.fireEvent("regioncollapsed", region);
30872     },
30873     
30874     onRegionExpanded : function(region){
30875         this.fireEvent("regionexpanded", region);
30876     },
30877         
30878     /**
30879      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30880      * performs box-model adjustments.
30881      * @return {Object} The size as an object {width: (the width), height: (the height)}
30882      */
30883     getViewSize : function(){
30884         var size;
30885         if(this.el.dom != document.body){
30886             size = this.el.getSize();
30887         }else{
30888             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30889         }
30890         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30891         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30892         return size;
30893     },
30894     
30895     /**
30896      * Returns the Element this layout is bound to.
30897      * @return {Roo.Element}
30898      */
30899     getEl : function(){
30900         return this.el;
30901     },
30902     
30903     /**
30904      * Returns the specified region.
30905      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30906      * @return {Roo.LayoutRegion}
30907      */
30908     getRegion : function(target){
30909         return this.regions[target.toLowerCase()];
30910     },
30911     
30912     onWindowResize : function(){
30913         if(this.monitorWindowResize){
30914             this.layout();
30915         }
30916     }
30917 });/*
30918  * Based on:
30919  * Ext JS Library 1.1.1
30920  * Copyright(c) 2006-2007, Ext JS, LLC.
30921  *
30922  * Originally Released Under LGPL - original licence link has changed is not relivant.
30923  *
30924  * Fork - LGPL
30925  * <script type="text/javascript">
30926  */
30927 /**
30928  * @class Roo.BorderLayout
30929  * @extends Roo.LayoutManager
30930  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30931  * please see: <br><br>
30932  * <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>
30933  * <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>
30934  * Example:
30935  <pre><code>
30936  var layout = new Roo.BorderLayout(document.body, {
30937     north: {
30938         initialSize: 25,
30939         titlebar: false
30940     },
30941     west: {
30942         split:true,
30943         initialSize: 200,
30944         minSize: 175,
30945         maxSize: 400,
30946         titlebar: true,
30947         collapsible: true
30948     },
30949     east: {
30950         split:true,
30951         initialSize: 202,
30952         minSize: 175,
30953         maxSize: 400,
30954         titlebar: true,
30955         collapsible: true
30956     },
30957     south: {
30958         split:true,
30959         initialSize: 100,
30960         minSize: 100,
30961         maxSize: 200,
30962         titlebar: true,
30963         collapsible: true
30964     },
30965     center: {
30966         titlebar: true,
30967         autoScroll:true,
30968         resizeTabs: true,
30969         minTabWidth: 50,
30970         preferredTabWidth: 150
30971     }
30972 });
30973
30974 // shorthand
30975 var CP = Roo.ContentPanel;
30976
30977 layout.beginUpdate();
30978 layout.add("north", new CP("north", "North"));
30979 layout.add("south", new CP("south", {title: "South", closable: true}));
30980 layout.add("west", new CP("west", {title: "West"}));
30981 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30982 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30983 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30984 layout.getRegion("center").showPanel("center1");
30985 layout.endUpdate();
30986 </code></pre>
30987
30988 <b>The container the layout is rendered into can be either the body element or any other element.
30989 If it is not the body element, the container needs to either be an absolute positioned element,
30990 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30991 the container size if it is not the body element.</b>
30992
30993 * @constructor
30994 * Create a new BorderLayout
30995 * @param {String/HTMLElement/Element} container The container this layout is bound to
30996 * @param {Object} config Configuration options
30997  */
30998 Roo.BorderLayout = function(container, config){
30999     config = config || {};
31000     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31001     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31002     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31003         var target = this.factory.validRegions[i];
31004         if(config[target]){
31005             this.addRegion(target, config[target]);
31006         }
31007     }
31008 };
31009
31010 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31011     /**
31012      * Creates and adds a new region if it doesn't already exist.
31013      * @param {String} target The target region key (north, south, east, west or center).
31014      * @param {Object} config The regions config object
31015      * @return {BorderLayoutRegion} The new region
31016      */
31017     addRegion : function(target, config){
31018         if(!this.regions[target]){
31019             var r = this.factory.create(target, this, config);
31020             this.bindRegion(target, r);
31021         }
31022         return this.regions[target];
31023     },
31024
31025     // private (kinda)
31026     bindRegion : function(name, r){
31027         this.regions[name] = r;
31028         r.on("visibilitychange", this.layout, this);
31029         r.on("paneladded", this.layout, this);
31030         r.on("panelremoved", this.layout, this);
31031         r.on("invalidated", this.layout, this);
31032         r.on("resized", this.onRegionResized, this);
31033         r.on("collapsed", this.onRegionCollapsed, this);
31034         r.on("expanded", this.onRegionExpanded, this);
31035     },
31036
31037     /**
31038      * Performs a layout update.
31039      */
31040     layout : function(){
31041         if(this.updating) return;
31042         var size = this.getViewSize();
31043         var w = size.width;
31044         var h = size.height;
31045         var centerW = w;
31046         var centerH = h;
31047         var centerY = 0;
31048         var centerX = 0;
31049         //var x = 0, y = 0;
31050
31051         var rs = this.regions;
31052         var north = rs["north"];
31053         var south = rs["south"]; 
31054         var west = rs["west"];
31055         var east = rs["east"];
31056         var center = rs["center"];
31057         //if(this.hideOnLayout){ // not supported anymore
31058             //c.el.setStyle("display", "none");
31059         //}
31060         if(north && north.isVisible()){
31061             var b = north.getBox();
31062             var m = north.getMargins();
31063             b.width = w - (m.left+m.right);
31064             b.x = m.left;
31065             b.y = m.top;
31066             centerY = b.height + b.y + m.bottom;
31067             centerH -= centerY;
31068             north.updateBox(this.safeBox(b));
31069         }
31070         if(south && south.isVisible()){
31071             var b = south.getBox();
31072             var m = south.getMargins();
31073             b.width = w - (m.left+m.right);
31074             b.x = m.left;
31075             var totalHeight = (b.height + m.top + m.bottom);
31076             b.y = h - totalHeight + m.top;
31077             centerH -= totalHeight;
31078             south.updateBox(this.safeBox(b));
31079         }
31080         if(west && west.isVisible()){
31081             var b = west.getBox();
31082             var m = west.getMargins();
31083             b.height = centerH - (m.top+m.bottom);
31084             b.x = m.left;
31085             b.y = centerY + m.top;
31086             var totalWidth = (b.width + m.left + m.right);
31087             centerX += totalWidth;
31088             centerW -= totalWidth;
31089             west.updateBox(this.safeBox(b));
31090         }
31091         if(east && east.isVisible()){
31092             var b = east.getBox();
31093             var m = east.getMargins();
31094             b.height = centerH - (m.top+m.bottom);
31095             var totalWidth = (b.width + m.left + m.right);
31096             b.x = w - totalWidth + m.left;
31097             b.y = centerY + m.top;
31098             centerW -= totalWidth;
31099             east.updateBox(this.safeBox(b));
31100         }
31101         if(center){
31102             var m = center.getMargins();
31103             var centerBox = {
31104                 x: centerX + m.left,
31105                 y: centerY + m.top,
31106                 width: centerW - (m.left+m.right),
31107                 height: centerH - (m.top+m.bottom)
31108             };
31109             //if(this.hideOnLayout){
31110                 //center.el.setStyle("display", "block");
31111             //}
31112             center.updateBox(this.safeBox(centerBox));
31113         }
31114         this.el.repaint();
31115         this.fireEvent("layout", this);
31116     },
31117
31118     // private
31119     safeBox : function(box){
31120         box.width = Math.max(0, box.width);
31121         box.height = Math.max(0, box.height);
31122         return box;
31123     },
31124
31125     /**
31126      * Adds a ContentPanel (or subclass) to this layout.
31127      * @param {String} target The target region key (north, south, east, west or center).
31128      * @param {Roo.ContentPanel} panel The panel to add
31129      * @return {Roo.ContentPanel} The added panel
31130      */
31131     add : function(target, panel){
31132          
31133         target = target.toLowerCase();
31134         return this.regions[target].add(panel);
31135     },
31136
31137     /**
31138      * Remove a ContentPanel (or subclass) to this layout.
31139      * @param {String} target The target region key (north, south, east, west or center).
31140      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31141      * @return {Roo.ContentPanel} The removed panel
31142      */
31143     remove : function(target, panel){
31144         target = target.toLowerCase();
31145         return this.regions[target].remove(panel);
31146     },
31147
31148     /**
31149      * Searches all regions for a panel with the specified id
31150      * @param {String} panelId
31151      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31152      */
31153     findPanel : function(panelId){
31154         var rs = this.regions;
31155         for(var target in rs){
31156             if(typeof rs[target] != "function"){
31157                 var p = rs[target].getPanel(panelId);
31158                 if(p){
31159                     return p;
31160                 }
31161             }
31162         }
31163         return null;
31164     },
31165
31166     /**
31167      * Searches all regions for a panel with the specified id and activates (shows) it.
31168      * @param {String/ContentPanel} panelId The panels id or the panel itself
31169      * @return {Roo.ContentPanel} The shown panel or null
31170      */
31171     showPanel : function(panelId) {
31172       var rs = this.regions;
31173       for(var target in rs){
31174          var r = rs[target];
31175          if(typeof r != "function"){
31176             if(r.hasPanel(panelId)){
31177                return r.showPanel(panelId);
31178             }
31179          }
31180       }
31181       return null;
31182    },
31183
31184    /**
31185      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31186      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31187      */
31188     restoreState : function(provider){
31189         if(!provider){
31190             provider = Roo.state.Manager;
31191         }
31192         var sm = new Roo.LayoutStateManager();
31193         sm.init(this, provider);
31194     },
31195
31196     /**
31197      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31198      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31199      * a valid ContentPanel config object.  Example:
31200      * <pre><code>
31201 // Create the main layout
31202 var layout = new Roo.BorderLayout('main-ct', {
31203     west: {
31204         split:true,
31205         minSize: 175,
31206         titlebar: true
31207     },
31208     center: {
31209         title:'Components'
31210     }
31211 }, 'main-ct');
31212
31213 // Create and add multiple ContentPanels at once via configs
31214 layout.batchAdd({
31215    west: {
31216        id: 'source-files',
31217        autoCreate:true,
31218        title:'Ext Source Files',
31219        autoScroll:true,
31220        fitToFrame:true
31221    },
31222    center : {
31223        el: cview,
31224        autoScroll:true,
31225        fitToFrame:true,
31226        toolbar: tb,
31227        resizeEl:'cbody'
31228    }
31229 });
31230 </code></pre>
31231      * @param {Object} regions An object containing ContentPanel configs by region name
31232      */
31233     batchAdd : function(regions){
31234         this.beginUpdate();
31235         for(var rname in regions){
31236             var lr = this.regions[rname];
31237             if(lr){
31238                 this.addTypedPanels(lr, regions[rname]);
31239             }
31240         }
31241         this.endUpdate();
31242     },
31243
31244     // private
31245     addTypedPanels : function(lr, ps){
31246         if(typeof ps == 'string'){
31247             lr.add(new Roo.ContentPanel(ps));
31248         }
31249         else if(ps instanceof Array){
31250             for(var i =0, len = ps.length; i < len; i++){
31251                 this.addTypedPanels(lr, ps[i]);
31252             }
31253         }
31254         else if(!ps.events){ // raw config?
31255             var el = ps.el;
31256             delete ps.el; // prevent conflict
31257             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
31258         }
31259         else {  // panel object assumed!
31260             lr.add(ps);
31261         }
31262     },
31263     /**
31264      * Adds a xtype elements to the layout.
31265      * <pre><code>
31266
31267 layout.addxtype({
31268        xtype : 'ContentPanel',
31269        region: 'west',
31270        items: [ .... ]
31271    }
31272 );
31273
31274 layout.addxtype({
31275         xtype : 'NestedLayoutPanel',
31276         region: 'west',
31277         layout: {
31278            center: { },
31279            west: { }   
31280         },
31281         items : [ ... list of content panels or nested layout panels.. ]
31282    }
31283 );
31284 </code></pre>
31285      * @param {Object} cfg Xtype definition of item to add.
31286      */
31287     addxtype : function(cfg)
31288     {
31289         // basically accepts a pannel...
31290         // can accept a layout region..!?!?
31291         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31292         
31293         if (!cfg.xtype.match(/Panel$/)) {
31294             return false;
31295         }
31296         var ret = false;
31297         
31298         if (typeof(cfg.region) == 'undefined') {
31299             Roo.log("Failed to add Panel, region was not set");
31300             Roo.log(cfg);
31301             return false;
31302         }
31303         var region = cfg.region;
31304         delete cfg.region;
31305         
31306           
31307         var xitems = [];
31308         if (cfg.items) {
31309             xitems = cfg.items;
31310             delete cfg.items;
31311         }
31312         var nb = false;
31313         
31314         switch(cfg.xtype) 
31315         {
31316             case 'ContentPanel':  // ContentPanel (el, cfg)
31317             case 'ScrollPanel':  // ContentPanel (el, cfg)
31318                 if(cfg.autoCreate) {
31319                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31320                 } else {
31321                     var el = this.el.createChild();
31322                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31323                 }
31324                 
31325                 this.add(region, ret);
31326                 break;
31327             
31328             
31329             case 'TreePanel': // our new panel!
31330                 cfg.el = this.el.createChild();
31331                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31332                 this.add(region, ret);
31333                 break;
31334             
31335             case 'NestedLayoutPanel': 
31336                 // create a new Layout (which is  a Border Layout...
31337                 var el = this.el.createChild();
31338                 var clayout = cfg.layout;
31339                 delete cfg.layout;
31340                 clayout.items   = clayout.items  || [];
31341                 // replace this exitems with the clayout ones..
31342                 xitems = clayout.items;
31343                  
31344                 
31345                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31346                     cfg.background = false;
31347                 }
31348                 var layout = new Roo.BorderLayout(el, clayout);
31349                 
31350                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31351                 //console.log('adding nested layout panel '  + cfg.toSource());
31352                 this.add(region, ret);
31353                 nb = {}; /// find first...
31354                 break;
31355                 
31356             case 'GridPanel': 
31357             
31358                 // needs grid and region
31359                 
31360                 //var el = this.getRegion(region).el.createChild();
31361                 var el = this.el.createChild();
31362                 // create the grid first...
31363                 
31364                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31365                 delete cfg.grid;
31366                 if (region == 'center' && this.active ) {
31367                     cfg.background = false;
31368                 }
31369                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31370                 
31371                 this.add(region, ret);
31372                 if (cfg.background) {
31373                     ret.on('activate', function(gp) {
31374                         if (!gp.grid.rendered) {
31375                             gp.grid.render();
31376                         }
31377                     });
31378                 } else {
31379                     grid.render();
31380                 }
31381                 break;
31382            
31383                
31384                 
31385                 
31386             default: 
31387                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31388                 return null;
31389              // GridPanel (grid, cfg)
31390             
31391         }
31392         this.beginUpdate();
31393         // add children..
31394         var region = '';
31395         var abn = {};
31396         Roo.each(xitems, function(i)  {
31397             region = nb && i.region ? i.region : false;
31398             
31399             var add = ret.addxtype(i);
31400            
31401             if (region) {
31402                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31403                 if (!i.background) {
31404                     abn[region] = nb[region] ;
31405                 }
31406             }
31407             
31408         });
31409         this.endUpdate();
31410
31411         // make the last non-background panel active..
31412         //if (nb) { Roo.log(abn); }
31413         if (nb) {
31414             
31415             for(var r in abn) {
31416                 region = this.getRegion(r);
31417                 if (region) {
31418                     // tried using nb[r], but it does not work..
31419                      
31420                     region.showPanel(abn[r]);
31421                    
31422                 }
31423             }
31424         }
31425         return ret;
31426         
31427     }
31428 });
31429
31430 /**
31431  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31432  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31433  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31434  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31435  * <pre><code>
31436 // shorthand
31437 var CP = Roo.ContentPanel;
31438
31439 var layout = Roo.BorderLayout.create({
31440     north: {
31441         initialSize: 25,
31442         titlebar: false,
31443         panels: [new CP("north", "North")]
31444     },
31445     west: {
31446         split:true,
31447         initialSize: 200,
31448         minSize: 175,
31449         maxSize: 400,
31450         titlebar: true,
31451         collapsible: true,
31452         panels: [new CP("west", {title: "West"})]
31453     },
31454     east: {
31455         split:true,
31456         initialSize: 202,
31457         minSize: 175,
31458         maxSize: 400,
31459         titlebar: true,
31460         collapsible: true,
31461         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31462     },
31463     south: {
31464         split:true,
31465         initialSize: 100,
31466         minSize: 100,
31467         maxSize: 200,
31468         titlebar: true,
31469         collapsible: true,
31470         panels: [new CP("south", {title: "South", closable: true})]
31471     },
31472     center: {
31473         titlebar: true,
31474         autoScroll:true,
31475         resizeTabs: true,
31476         minTabWidth: 50,
31477         preferredTabWidth: 150,
31478         panels: [
31479             new CP("center1", {title: "Close Me", closable: true}),
31480             new CP("center2", {title: "Center Panel", closable: false})
31481         ]
31482     }
31483 }, document.body);
31484
31485 layout.getRegion("center").showPanel("center1");
31486 </code></pre>
31487  * @param config
31488  * @param targetEl
31489  */
31490 Roo.BorderLayout.create = function(config, targetEl){
31491     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31492     layout.beginUpdate();
31493     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31494     for(var j = 0, jlen = regions.length; j < jlen; j++){
31495         var lr = regions[j];
31496         if(layout.regions[lr] && config[lr].panels){
31497             var r = layout.regions[lr];
31498             var ps = config[lr].panels;
31499             layout.addTypedPanels(r, ps);
31500         }
31501     }
31502     layout.endUpdate();
31503     return layout;
31504 };
31505
31506 // private
31507 Roo.BorderLayout.RegionFactory = {
31508     // private
31509     validRegions : ["north","south","east","west","center"],
31510
31511     // private
31512     create : function(target, mgr, config){
31513         target = target.toLowerCase();
31514         if(config.lightweight || config.basic){
31515             return new Roo.BasicLayoutRegion(mgr, config, target);
31516         }
31517         switch(target){
31518             case "north":
31519                 return new Roo.NorthLayoutRegion(mgr, config);
31520             case "south":
31521                 return new Roo.SouthLayoutRegion(mgr, config);
31522             case "east":
31523                 return new Roo.EastLayoutRegion(mgr, config);
31524             case "west":
31525                 return new Roo.WestLayoutRegion(mgr, config);
31526             case "center":
31527                 return new Roo.CenterLayoutRegion(mgr, config);
31528         }
31529         throw 'Layout region "'+target+'" not supported.';
31530     }
31531 };/*
31532  * Based on:
31533  * Ext JS Library 1.1.1
31534  * Copyright(c) 2006-2007, Ext JS, LLC.
31535  *
31536  * Originally Released Under LGPL - original licence link has changed is not relivant.
31537  *
31538  * Fork - LGPL
31539  * <script type="text/javascript">
31540  */
31541  
31542 /**
31543  * @class Roo.BasicLayoutRegion
31544  * @extends Roo.util.Observable
31545  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31546  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31547  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31548  */
31549 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31550     this.mgr = mgr;
31551     this.position  = pos;
31552     this.events = {
31553         /**
31554          * @scope Roo.BasicLayoutRegion
31555          */
31556         
31557         /**
31558          * @event beforeremove
31559          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31560          * @param {Roo.LayoutRegion} this
31561          * @param {Roo.ContentPanel} panel The panel
31562          * @param {Object} e The cancel event object
31563          */
31564         "beforeremove" : true,
31565         /**
31566          * @event invalidated
31567          * Fires when the layout for this region is changed.
31568          * @param {Roo.LayoutRegion} this
31569          */
31570         "invalidated" : true,
31571         /**
31572          * @event visibilitychange
31573          * Fires when this region is shown or hidden 
31574          * @param {Roo.LayoutRegion} this
31575          * @param {Boolean} visibility true or false
31576          */
31577         "visibilitychange" : true,
31578         /**
31579          * @event paneladded
31580          * Fires when a panel is added. 
31581          * @param {Roo.LayoutRegion} this
31582          * @param {Roo.ContentPanel} panel The panel
31583          */
31584         "paneladded" : true,
31585         /**
31586          * @event panelremoved
31587          * Fires when a panel is removed. 
31588          * @param {Roo.LayoutRegion} this
31589          * @param {Roo.ContentPanel} panel The panel
31590          */
31591         "panelremoved" : true,
31592         /**
31593          * @event collapsed
31594          * Fires when this region is collapsed.
31595          * @param {Roo.LayoutRegion} this
31596          */
31597         "collapsed" : true,
31598         /**
31599          * @event expanded
31600          * Fires when this region is expanded.
31601          * @param {Roo.LayoutRegion} this
31602          */
31603         "expanded" : true,
31604         /**
31605          * @event slideshow
31606          * Fires when this region is slid into view.
31607          * @param {Roo.LayoutRegion} this
31608          */
31609         "slideshow" : true,
31610         /**
31611          * @event slidehide
31612          * Fires when this region slides out of view. 
31613          * @param {Roo.LayoutRegion} this
31614          */
31615         "slidehide" : true,
31616         /**
31617          * @event panelactivated
31618          * Fires when a panel is activated. 
31619          * @param {Roo.LayoutRegion} this
31620          * @param {Roo.ContentPanel} panel The activated panel
31621          */
31622         "panelactivated" : true,
31623         /**
31624          * @event resized
31625          * Fires when the user resizes this region. 
31626          * @param {Roo.LayoutRegion} this
31627          * @param {Number} newSize The new size (width for east/west, height for north/south)
31628          */
31629         "resized" : true
31630     };
31631     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31632     this.panels = new Roo.util.MixedCollection();
31633     this.panels.getKey = this.getPanelId.createDelegate(this);
31634     this.box = null;
31635     this.activePanel = null;
31636     // ensure listeners are added...
31637     
31638     if (config.listeners || config.events) {
31639         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31640             listeners : config.listeners || {},
31641             events : config.events || {}
31642         });
31643     }
31644     
31645     if(skipConfig !== true){
31646         this.applyConfig(config);
31647     }
31648 };
31649
31650 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31651     getPanelId : function(p){
31652         return p.getId();
31653     },
31654     
31655     applyConfig : function(config){
31656         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31657         this.config = config;
31658         
31659     },
31660     
31661     /**
31662      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31663      * the width, for horizontal (north, south) the height.
31664      * @param {Number} newSize The new width or height
31665      */
31666     resizeTo : function(newSize){
31667         var el = this.el ? this.el :
31668                  (this.activePanel ? this.activePanel.getEl() : null);
31669         if(el){
31670             switch(this.position){
31671                 case "east":
31672                 case "west":
31673                     el.setWidth(newSize);
31674                     this.fireEvent("resized", this, newSize);
31675                 break;
31676                 case "north":
31677                 case "south":
31678                     el.setHeight(newSize);
31679                     this.fireEvent("resized", this, newSize);
31680                 break;                
31681             }
31682         }
31683     },
31684     
31685     getBox : function(){
31686         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31687     },
31688     
31689     getMargins : function(){
31690         return this.margins;
31691     },
31692     
31693     updateBox : function(box){
31694         this.box = box;
31695         var el = this.activePanel.getEl();
31696         el.dom.style.left = box.x + "px";
31697         el.dom.style.top = box.y + "px";
31698         this.activePanel.setSize(box.width, box.height);
31699     },
31700     
31701     /**
31702      * Returns the container element for this region.
31703      * @return {Roo.Element}
31704      */
31705     getEl : function(){
31706         return this.activePanel;
31707     },
31708     
31709     /**
31710      * Returns true if this region is currently visible.
31711      * @return {Boolean}
31712      */
31713     isVisible : function(){
31714         return this.activePanel ? true : false;
31715     },
31716     
31717     setActivePanel : function(panel){
31718         panel = this.getPanel(panel);
31719         if(this.activePanel && this.activePanel != panel){
31720             this.activePanel.setActiveState(false);
31721             this.activePanel.getEl().setLeftTop(-10000,-10000);
31722         }
31723         this.activePanel = panel;
31724         panel.setActiveState(true);
31725         if(this.box){
31726             panel.setSize(this.box.width, this.box.height);
31727         }
31728         this.fireEvent("panelactivated", this, panel);
31729         this.fireEvent("invalidated");
31730     },
31731     
31732     /**
31733      * Show the specified panel.
31734      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31735      * @return {Roo.ContentPanel} The shown panel or null
31736      */
31737     showPanel : function(panel){
31738         if(panel = this.getPanel(panel)){
31739             this.setActivePanel(panel);
31740         }
31741         return panel;
31742     },
31743     
31744     /**
31745      * Get the active panel for this region.
31746      * @return {Roo.ContentPanel} The active panel or null
31747      */
31748     getActivePanel : function(){
31749         return this.activePanel;
31750     },
31751     
31752     /**
31753      * Add the passed ContentPanel(s)
31754      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31755      * @return {Roo.ContentPanel} The panel added (if only one was added)
31756      */
31757     add : function(panel){
31758         if(arguments.length > 1){
31759             for(var i = 0, len = arguments.length; i < len; i++) {
31760                 this.add(arguments[i]);
31761             }
31762             return null;
31763         }
31764         if(this.hasPanel(panel)){
31765             this.showPanel(panel);
31766             return panel;
31767         }
31768         var el = panel.getEl();
31769         if(el.dom.parentNode != this.mgr.el.dom){
31770             this.mgr.el.dom.appendChild(el.dom);
31771         }
31772         if(panel.setRegion){
31773             panel.setRegion(this);
31774         }
31775         this.panels.add(panel);
31776         el.setStyle("position", "absolute");
31777         if(!panel.background){
31778             this.setActivePanel(panel);
31779             if(this.config.initialSize && this.panels.getCount()==1){
31780                 this.resizeTo(this.config.initialSize);
31781             }
31782         }
31783         this.fireEvent("paneladded", this, panel);
31784         return panel;
31785     },
31786     
31787     /**
31788      * Returns true if the panel is in this region.
31789      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31790      * @return {Boolean}
31791      */
31792     hasPanel : function(panel){
31793         if(typeof panel == "object"){ // must be panel obj
31794             panel = panel.getId();
31795         }
31796         return this.getPanel(panel) ? true : false;
31797     },
31798     
31799     /**
31800      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31801      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31802      * @param {Boolean} preservePanel Overrides the config preservePanel option
31803      * @return {Roo.ContentPanel} The panel that was removed
31804      */
31805     remove : function(panel, preservePanel){
31806         panel = this.getPanel(panel);
31807         if(!panel){
31808             return null;
31809         }
31810         var e = {};
31811         this.fireEvent("beforeremove", this, panel, e);
31812         if(e.cancel === true){
31813             return null;
31814         }
31815         var panelId = panel.getId();
31816         this.panels.removeKey(panelId);
31817         return panel;
31818     },
31819     
31820     /**
31821      * Returns the panel specified or null if it's not in this region.
31822      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31823      * @return {Roo.ContentPanel}
31824      */
31825     getPanel : function(id){
31826         if(typeof id == "object"){ // must be panel obj
31827             return id;
31828         }
31829         return this.panels.get(id);
31830     },
31831     
31832     /**
31833      * Returns this regions position (north/south/east/west/center).
31834      * @return {String} 
31835      */
31836     getPosition: function(){
31837         return this.position;    
31838     }
31839 });/*
31840  * Based on:
31841  * Ext JS Library 1.1.1
31842  * Copyright(c) 2006-2007, Ext JS, LLC.
31843  *
31844  * Originally Released Under LGPL - original licence link has changed is not relivant.
31845  *
31846  * Fork - LGPL
31847  * <script type="text/javascript">
31848  */
31849  
31850 /**
31851  * @class Roo.LayoutRegion
31852  * @extends Roo.BasicLayoutRegion
31853  * This class represents a region in a layout manager.
31854  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31855  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31856  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31857  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31858  * @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})
31859  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31860  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31861  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31862  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31863  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31864  * @cfg {String}    title           The title for the region (overrides panel titles)
31865  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31866  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31867  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31868  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31869  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31870  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31871  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31872  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31873  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31874  * @cfg {Boolean}   showPin         True to show a pin button
31875  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31876  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31877  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31878  * @cfg {Number}    width           For East/West panels
31879  * @cfg {Number}    height          For North/South panels
31880  * @cfg {Boolean}   split           To show the splitter
31881  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31882  */
31883 Roo.LayoutRegion = function(mgr, config, pos){
31884     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31885     var dh = Roo.DomHelper;
31886     /** This region's container element 
31887     * @type Roo.Element */
31888     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31889     /** This region's title element 
31890     * @type Roo.Element */
31891
31892     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31893         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31894         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31895     ]}, true);
31896     this.titleEl.enableDisplayMode();
31897     /** This region's title text element 
31898     * @type HTMLElement */
31899     this.titleTextEl = this.titleEl.dom.firstChild;
31900     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31901     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31902     this.closeBtn.enableDisplayMode();
31903     this.closeBtn.on("click", this.closeClicked, this);
31904     this.closeBtn.hide();
31905
31906     this.createBody(config);
31907     this.visible = true;
31908     this.collapsed = false;
31909
31910     if(config.hideWhenEmpty){
31911         this.hide();
31912         this.on("paneladded", this.validateVisibility, this);
31913         this.on("panelremoved", this.validateVisibility, this);
31914     }
31915     this.applyConfig(config);
31916 };
31917
31918 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31919
31920     createBody : function(){
31921         /** This region's body element 
31922         * @type Roo.Element */
31923         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31924     },
31925
31926     applyConfig : function(c){
31927         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31928             var dh = Roo.DomHelper;
31929             if(c.titlebar !== false){
31930                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31931                 this.collapseBtn.on("click", this.collapse, this);
31932                 this.collapseBtn.enableDisplayMode();
31933
31934                 if(c.showPin === true || this.showPin){
31935                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31936                     this.stickBtn.enableDisplayMode();
31937                     this.stickBtn.on("click", this.expand, this);
31938                     this.stickBtn.hide();
31939                 }
31940             }
31941             /** This region's collapsed element
31942             * @type Roo.Element */
31943             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31944                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31945             ]}, true);
31946             if(c.floatable !== false){
31947                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31948                this.collapsedEl.on("click", this.collapseClick, this);
31949             }
31950
31951             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31952                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31953                    id: "message", unselectable: "on", style:{"float":"left"}});
31954                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31955              }
31956             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31957             this.expandBtn.on("click", this.expand, this);
31958         }
31959         if(this.collapseBtn){
31960             this.collapseBtn.setVisible(c.collapsible == true);
31961         }
31962         this.cmargins = c.cmargins || this.cmargins ||
31963                          (this.position == "west" || this.position == "east" ?
31964                              {top: 0, left: 2, right:2, bottom: 0} :
31965                              {top: 2, left: 0, right:0, bottom: 2});
31966         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31967         this.bottomTabs = c.tabPosition != "top";
31968         this.autoScroll = c.autoScroll || false;
31969         if(this.autoScroll){
31970             this.bodyEl.setStyle("overflow", "auto");
31971         }else{
31972             this.bodyEl.setStyle("overflow", "hidden");
31973         }
31974         //if(c.titlebar !== false){
31975             if((!c.titlebar && !c.title) || c.titlebar === false){
31976                 this.titleEl.hide();
31977             }else{
31978                 this.titleEl.show();
31979                 if(c.title){
31980                     this.titleTextEl.innerHTML = c.title;
31981                 }
31982             }
31983         //}
31984         this.duration = c.duration || .30;
31985         this.slideDuration = c.slideDuration || .45;
31986         this.config = c;
31987         if(c.collapsed){
31988             this.collapse(true);
31989         }
31990         if(c.hidden){
31991             this.hide();
31992         }
31993     },
31994     /**
31995      * Returns true if this region is currently visible.
31996      * @return {Boolean}
31997      */
31998     isVisible : function(){
31999         return this.visible;
32000     },
32001
32002     /**
32003      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32004      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32005      */
32006     setCollapsedTitle : function(title){
32007         title = title || "&#160;";
32008         if(this.collapsedTitleTextEl){
32009             this.collapsedTitleTextEl.innerHTML = title;
32010         }
32011     },
32012
32013     getBox : function(){
32014         var b;
32015         if(!this.collapsed){
32016             b = this.el.getBox(false, true);
32017         }else{
32018             b = this.collapsedEl.getBox(false, true);
32019         }
32020         return b;
32021     },
32022
32023     getMargins : function(){
32024         return this.collapsed ? this.cmargins : this.margins;
32025     },
32026
32027     highlight : function(){
32028         this.el.addClass("x-layout-panel-dragover");
32029     },
32030
32031     unhighlight : function(){
32032         this.el.removeClass("x-layout-panel-dragover");
32033     },
32034
32035     updateBox : function(box){
32036         this.box = box;
32037         if(!this.collapsed){
32038             this.el.dom.style.left = box.x + "px";
32039             this.el.dom.style.top = box.y + "px";
32040             this.updateBody(box.width, box.height);
32041         }else{
32042             this.collapsedEl.dom.style.left = box.x + "px";
32043             this.collapsedEl.dom.style.top = box.y + "px";
32044             this.collapsedEl.setSize(box.width, box.height);
32045         }
32046         if(this.tabs){
32047             this.tabs.autoSizeTabs();
32048         }
32049     },
32050
32051     updateBody : function(w, h){
32052         if(w !== null){
32053             this.el.setWidth(w);
32054             w -= this.el.getBorderWidth("rl");
32055             if(this.config.adjustments){
32056                 w += this.config.adjustments[0];
32057             }
32058         }
32059         if(h !== null){
32060             this.el.setHeight(h);
32061             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32062             h -= this.el.getBorderWidth("tb");
32063             if(this.config.adjustments){
32064                 h += this.config.adjustments[1];
32065             }
32066             this.bodyEl.setHeight(h);
32067             if(this.tabs){
32068                 h = this.tabs.syncHeight(h);
32069             }
32070         }
32071         if(this.panelSize){
32072             w = w !== null ? w : this.panelSize.width;
32073             h = h !== null ? h : this.panelSize.height;
32074         }
32075         if(this.activePanel){
32076             var el = this.activePanel.getEl();
32077             w = w !== null ? w : el.getWidth();
32078             h = h !== null ? h : el.getHeight();
32079             this.panelSize = {width: w, height: h};
32080             this.activePanel.setSize(w, h);
32081         }
32082         if(Roo.isIE && this.tabs){
32083             this.tabs.el.repaint();
32084         }
32085     },
32086
32087     /**
32088      * Returns the container element for this region.
32089      * @return {Roo.Element}
32090      */
32091     getEl : function(){
32092         return this.el;
32093     },
32094
32095     /**
32096      * Hides this region.
32097      */
32098     hide : function(){
32099         if(!this.collapsed){
32100             this.el.dom.style.left = "-2000px";
32101             this.el.hide();
32102         }else{
32103             this.collapsedEl.dom.style.left = "-2000px";
32104             this.collapsedEl.hide();
32105         }
32106         this.visible = false;
32107         this.fireEvent("visibilitychange", this, false);
32108     },
32109
32110     /**
32111      * Shows this region if it was previously hidden.
32112      */
32113     show : function(){
32114         if(!this.collapsed){
32115             this.el.show();
32116         }else{
32117             this.collapsedEl.show();
32118         }
32119         this.visible = true;
32120         this.fireEvent("visibilitychange", this, true);
32121     },
32122
32123     closeClicked : function(){
32124         if(this.activePanel){
32125             this.remove(this.activePanel);
32126         }
32127     },
32128
32129     collapseClick : function(e){
32130         if(this.isSlid){
32131            e.stopPropagation();
32132            this.slideIn();
32133         }else{
32134            e.stopPropagation();
32135            this.slideOut();
32136         }
32137     },
32138
32139     /**
32140      * Collapses this region.
32141      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32142      */
32143     collapse : function(skipAnim){
32144         if(this.collapsed) return;
32145         this.collapsed = true;
32146         if(this.split){
32147             this.split.el.hide();
32148         }
32149         if(this.config.animate && skipAnim !== true){
32150             this.fireEvent("invalidated", this);
32151             this.animateCollapse();
32152         }else{
32153             this.el.setLocation(-20000,-20000);
32154             this.el.hide();
32155             this.collapsedEl.show();
32156             this.fireEvent("collapsed", this);
32157             this.fireEvent("invalidated", this);
32158         }
32159     },
32160
32161     animateCollapse : function(){
32162         // overridden
32163     },
32164
32165     /**
32166      * Expands this region if it was previously collapsed.
32167      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32168      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32169      */
32170     expand : function(e, skipAnim){
32171         if(e) e.stopPropagation();
32172         if(!this.collapsed || this.el.hasActiveFx()) return;
32173         if(this.isSlid){
32174             this.afterSlideIn();
32175             skipAnim = true;
32176         }
32177         this.collapsed = false;
32178         if(this.config.animate && skipAnim !== true){
32179             this.animateExpand();
32180         }else{
32181             this.el.show();
32182             if(this.split){
32183                 this.split.el.show();
32184             }
32185             this.collapsedEl.setLocation(-2000,-2000);
32186             this.collapsedEl.hide();
32187             this.fireEvent("invalidated", this);
32188             this.fireEvent("expanded", this);
32189         }
32190     },
32191
32192     animateExpand : function(){
32193         // overridden
32194     },
32195
32196     initTabs : function()
32197     {
32198         this.bodyEl.setStyle("overflow", "hidden");
32199         var ts = new Roo.TabPanel(
32200                 this.bodyEl.dom,
32201                 {
32202                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32203                     disableTooltips: this.config.disableTabTips,
32204                     toolbar : this.config.toolbar
32205                 }
32206         );
32207         if(this.config.hideTabs){
32208             ts.stripWrap.setDisplayed(false);
32209         }
32210         this.tabs = ts;
32211         ts.resizeTabs = this.config.resizeTabs === true;
32212         ts.minTabWidth = this.config.minTabWidth || 40;
32213         ts.maxTabWidth = this.config.maxTabWidth || 250;
32214         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32215         ts.monitorResize = false;
32216         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32217         ts.bodyEl.addClass('x-layout-tabs-body');
32218         this.panels.each(this.initPanelAsTab, this);
32219     },
32220
32221     initPanelAsTab : function(panel){
32222         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32223                     this.config.closeOnTab && panel.isClosable());
32224         if(panel.tabTip !== undefined){
32225             ti.setTooltip(panel.tabTip);
32226         }
32227         ti.on("activate", function(){
32228               this.setActivePanel(panel);
32229         }, this);
32230         if(this.config.closeOnTab){
32231             ti.on("beforeclose", function(t, e){
32232                 e.cancel = true;
32233                 this.remove(panel);
32234             }, this);
32235         }
32236         return ti;
32237     },
32238
32239     updatePanelTitle : function(panel, title){
32240         if(this.activePanel == panel){
32241             this.updateTitle(title);
32242         }
32243         if(this.tabs){
32244             var ti = this.tabs.getTab(panel.getEl().id);
32245             ti.setText(title);
32246             if(panel.tabTip !== undefined){
32247                 ti.setTooltip(panel.tabTip);
32248             }
32249         }
32250     },
32251
32252     updateTitle : function(title){
32253         if(this.titleTextEl && !this.config.title){
32254             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32255         }
32256     },
32257
32258     setActivePanel : function(panel){
32259         panel = this.getPanel(panel);
32260         if(this.activePanel && this.activePanel != panel){
32261             this.activePanel.setActiveState(false);
32262         }
32263         this.activePanel = panel;
32264         panel.setActiveState(true);
32265         if(this.panelSize){
32266             panel.setSize(this.panelSize.width, this.panelSize.height);
32267         }
32268         if(this.closeBtn){
32269             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32270         }
32271         this.updateTitle(panel.getTitle());
32272         if(this.tabs){
32273             this.fireEvent("invalidated", this);
32274         }
32275         this.fireEvent("panelactivated", this, panel);
32276     },
32277
32278     /**
32279      * Shows the specified panel.
32280      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32281      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32282      */
32283     showPanel : function(panel){
32284         if(panel = this.getPanel(panel)){
32285             if(this.tabs){
32286                 var tab = this.tabs.getTab(panel.getEl().id);
32287                 if(tab.isHidden()){
32288                     this.tabs.unhideTab(tab.id);
32289                 }
32290                 tab.activate();
32291             }else{
32292                 this.setActivePanel(panel);
32293             }
32294         }
32295         return panel;
32296     },
32297
32298     /**
32299      * Get the active panel for this region.
32300      * @return {Roo.ContentPanel} The active panel or null
32301      */
32302     getActivePanel : function(){
32303         return this.activePanel;
32304     },
32305
32306     validateVisibility : function(){
32307         if(this.panels.getCount() < 1){
32308             this.updateTitle("&#160;");
32309             this.closeBtn.hide();
32310             this.hide();
32311         }else{
32312             if(!this.isVisible()){
32313                 this.show();
32314             }
32315         }
32316     },
32317
32318     /**
32319      * Adds the passed ContentPanel(s) to this region.
32320      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32321      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32322      */
32323     add : function(panel){
32324         if(arguments.length > 1){
32325             for(var i = 0, len = arguments.length; i < len; i++) {
32326                 this.add(arguments[i]);
32327             }
32328             return null;
32329         }
32330         if(this.hasPanel(panel)){
32331             this.showPanel(panel);
32332             return panel;
32333         }
32334         panel.setRegion(this);
32335         this.panels.add(panel);
32336         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32337             this.bodyEl.dom.appendChild(panel.getEl().dom);
32338             if(panel.background !== true){
32339                 this.setActivePanel(panel);
32340             }
32341             this.fireEvent("paneladded", this, panel);
32342             return panel;
32343         }
32344         if(!this.tabs){
32345             this.initTabs();
32346         }else{
32347             this.initPanelAsTab(panel);
32348         }
32349         if(panel.background !== true){
32350             this.tabs.activate(panel.getEl().id);
32351         }
32352         this.fireEvent("paneladded", this, panel);
32353         return panel;
32354     },
32355
32356     /**
32357      * Hides the tab for the specified panel.
32358      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32359      */
32360     hidePanel : function(panel){
32361         if(this.tabs && (panel = this.getPanel(panel))){
32362             this.tabs.hideTab(panel.getEl().id);
32363         }
32364     },
32365
32366     /**
32367      * Unhides the tab for a previously hidden panel.
32368      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32369      */
32370     unhidePanel : function(panel){
32371         if(this.tabs && (panel = this.getPanel(panel))){
32372             this.tabs.unhideTab(panel.getEl().id);
32373         }
32374     },
32375
32376     clearPanels : function(){
32377         while(this.panels.getCount() > 0){
32378              this.remove(this.panels.first());
32379         }
32380     },
32381
32382     /**
32383      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32384      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32385      * @param {Boolean} preservePanel Overrides the config preservePanel option
32386      * @return {Roo.ContentPanel} The panel that was removed
32387      */
32388     remove : function(panel, preservePanel){
32389         panel = this.getPanel(panel);
32390         if(!panel){
32391             return null;
32392         }
32393         var e = {};
32394         this.fireEvent("beforeremove", this, panel, e);
32395         if(e.cancel === true){
32396             return null;
32397         }
32398         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32399         var panelId = panel.getId();
32400         this.panels.removeKey(panelId);
32401         if(preservePanel){
32402             document.body.appendChild(panel.getEl().dom);
32403         }
32404         if(this.tabs){
32405             this.tabs.removeTab(panel.getEl().id);
32406         }else if (!preservePanel){
32407             this.bodyEl.dom.removeChild(panel.getEl().dom);
32408         }
32409         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32410             var p = this.panels.first();
32411             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32412             tempEl.appendChild(p.getEl().dom);
32413             this.bodyEl.update("");
32414             this.bodyEl.dom.appendChild(p.getEl().dom);
32415             tempEl = null;
32416             this.updateTitle(p.getTitle());
32417             this.tabs = null;
32418             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32419             this.setActivePanel(p);
32420         }
32421         panel.setRegion(null);
32422         if(this.activePanel == panel){
32423             this.activePanel = null;
32424         }
32425         if(this.config.autoDestroy !== false && preservePanel !== true){
32426             try{panel.destroy();}catch(e){}
32427         }
32428         this.fireEvent("panelremoved", this, panel);
32429         return panel;
32430     },
32431
32432     /**
32433      * Returns the TabPanel component used by this region
32434      * @return {Roo.TabPanel}
32435      */
32436     getTabs : function(){
32437         return this.tabs;
32438     },
32439
32440     createTool : function(parentEl, className){
32441         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32442             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32443         btn.addClassOnOver("x-layout-tools-button-over");
32444         return btn;
32445     }
32446 });/*
32447  * Based on:
32448  * Ext JS Library 1.1.1
32449  * Copyright(c) 2006-2007, Ext JS, LLC.
32450  *
32451  * Originally Released Under LGPL - original licence link has changed is not relivant.
32452  *
32453  * Fork - LGPL
32454  * <script type="text/javascript">
32455  */
32456  
32457
32458
32459 /**
32460  * @class Roo.SplitLayoutRegion
32461  * @extends Roo.LayoutRegion
32462  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32463  */
32464 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32465     this.cursor = cursor;
32466     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32467 };
32468
32469 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32470     splitTip : "Drag to resize.",
32471     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32472     useSplitTips : false,
32473
32474     applyConfig : function(config){
32475         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32476         if(config.split){
32477             if(!this.split){
32478                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32479                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32480                 /** The SplitBar for this region 
32481                 * @type Roo.SplitBar */
32482                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32483                 this.split.on("moved", this.onSplitMove, this);
32484                 this.split.useShim = config.useShim === true;
32485                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32486                 if(this.useSplitTips){
32487                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32488                 }
32489                 if(config.collapsible){
32490                     this.split.el.on("dblclick", this.collapse,  this);
32491                 }
32492             }
32493             if(typeof config.minSize != "undefined"){
32494                 this.split.minSize = config.minSize;
32495             }
32496             if(typeof config.maxSize != "undefined"){
32497                 this.split.maxSize = config.maxSize;
32498             }
32499             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32500                 this.hideSplitter();
32501             }
32502         }
32503     },
32504
32505     getHMaxSize : function(){
32506          var cmax = this.config.maxSize || 10000;
32507          var center = this.mgr.getRegion("center");
32508          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32509     },
32510
32511     getVMaxSize : function(){
32512          var cmax = this.config.maxSize || 10000;
32513          var center = this.mgr.getRegion("center");
32514          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32515     },
32516
32517     onSplitMove : function(split, newSize){
32518         this.fireEvent("resized", this, newSize);
32519     },
32520     
32521     /** 
32522      * Returns the {@link Roo.SplitBar} for this region.
32523      * @return {Roo.SplitBar}
32524      */
32525     getSplitBar : function(){
32526         return this.split;
32527     },
32528     
32529     hide : function(){
32530         this.hideSplitter();
32531         Roo.SplitLayoutRegion.superclass.hide.call(this);
32532     },
32533
32534     hideSplitter : function(){
32535         if(this.split){
32536             this.split.el.setLocation(-2000,-2000);
32537             this.split.el.hide();
32538         }
32539     },
32540
32541     show : function(){
32542         if(this.split){
32543             this.split.el.show();
32544         }
32545         Roo.SplitLayoutRegion.superclass.show.call(this);
32546     },
32547     
32548     beforeSlide: function(){
32549         if(Roo.isGecko){// firefox overflow auto bug workaround
32550             this.bodyEl.clip();
32551             if(this.tabs) this.tabs.bodyEl.clip();
32552             if(this.activePanel){
32553                 this.activePanel.getEl().clip();
32554                 
32555                 if(this.activePanel.beforeSlide){
32556                     this.activePanel.beforeSlide();
32557                 }
32558             }
32559         }
32560     },
32561     
32562     afterSlide : function(){
32563         if(Roo.isGecko){// firefox overflow auto bug workaround
32564             this.bodyEl.unclip();
32565             if(this.tabs) this.tabs.bodyEl.unclip();
32566             if(this.activePanel){
32567                 this.activePanel.getEl().unclip();
32568                 if(this.activePanel.afterSlide){
32569                     this.activePanel.afterSlide();
32570                 }
32571             }
32572         }
32573     },
32574
32575     initAutoHide : function(){
32576         if(this.autoHide !== false){
32577             if(!this.autoHideHd){
32578                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32579                 this.autoHideHd = {
32580                     "mouseout": function(e){
32581                         if(!e.within(this.el, true)){
32582                             st.delay(500);
32583                         }
32584                     },
32585                     "mouseover" : function(e){
32586                         st.cancel();
32587                     },
32588                     scope : this
32589                 };
32590             }
32591             this.el.on(this.autoHideHd);
32592         }
32593     },
32594
32595     clearAutoHide : function(){
32596         if(this.autoHide !== false){
32597             this.el.un("mouseout", this.autoHideHd.mouseout);
32598             this.el.un("mouseover", this.autoHideHd.mouseover);
32599         }
32600     },
32601
32602     clearMonitor : function(){
32603         Roo.get(document).un("click", this.slideInIf, this);
32604     },
32605
32606     // these names are backwards but not changed for compat
32607     slideOut : function(){
32608         if(this.isSlid || this.el.hasActiveFx()){
32609             return;
32610         }
32611         this.isSlid = true;
32612         if(this.collapseBtn){
32613             this.collapseBtn.hide();
32614         }
32615         this.closeBtnState = this.closeBtn.getStyle('display');
32616         this.closeBtn.hide();
32617         if(this.stickBtn){
32618             this.stickBtn.show();
32619         }
32620         this.el.show();
32621         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32622         this.beforeSlide();
32623         this.el.setStyle("z-index", 10001);
32624         this.el.slideIn(this.getSlideAnchor(), {
32625             callback: function(){
32626                 this.afterSlide();
32627                 this.initAutoHide();
32628                 Roo.get(document).on("click", this.slideInIf, this);
32629                 this.fireEvent("slideshow", this);
32630             },
32631             scope: this,
32632             block: true
32633         });
32634     },
32635
32636     afterSlideIn : function(){
32637         this.clearAutoHide();
32638         this.isSlid = false;
32639         this.clearMonitor();
32640         this.el.setStyle("z-index", "");
32641         if(this.collapseBtn){
32642             this.collapseBtn.show();
32643         }
32644         this.closeBtn.setStyle('display', this.closeBtnState);
32645         if(this.stickBtn){
32646             this.stickBtn.hide();
32647         }
32648         this.fireEvent("slidehide", this);
32649     },
32650
32651     slideIn : function(cb){
32652         if(!this.isSlid || this.el.hasActiveFx()){
32653             Roo.callback(cb);
32654             return;
32655         }
32656         this.isSlid = false;
32657         this.beforeSlide();
32658         this.el.slideOut(this.getSlideAnchor(), {
32659             callback: function(){
32660                 this.el.setLeftTop(-10000, -10000);
32661                 this.afterSlide();
32662                 this.afterSlideIn();
32663                 Roo.callback(cb);
32664             },
32665             scope: this,
32666             block: true
32667         });
32668     },
32669     
32670     slideInIf : function(e){
32671         if(!e.within(this.el)){
32672             this.slideIn();
32673         }
32674     },
32675
32676     animateCollapse : function(){
32677         this.beforeSlide();
32678         this.el.setStyle("z-index", 20000);
32679         var anchor = this.getSlideAnchor();
32680         this.el.slideOut(anchor, {
32681             callback : function(){
32682                 this.el.setStyle("z-index", "");
32683                 this.collapsedEl.slideIn(anchor, {duration:.3});
32684                 this.afterSlide();
32685                 this.el.setLocation(-10000,-10000);
32686                 this.el.hide();
32687                 this.fireEvent("collapsed", this);
32688             },
32689             scope: this,
32690             block: true
32691         });
32692     },
32693
32694     animateExpand : function(){
32695         this.beforeSlide();
32696         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32697         this.el.setStyle("z-index", 20000);
32698         this.collapsedEl.hide({
32699             duration:.1
32700         });
32701         this.el.slideIn(this.getSlideAnchor(), {
32702             callback : function(){
32703                 this.el.setStyle("z-index", "");
32704                 this.afterSlide();
32705                 if(this.split){
32706                     this.split.el.show();
32707                 }
32708                 this.fireEvent("invalidated", this);
32709                 this.fireEvent("expanded", this);
32710             },
32711             scope: this,
32712             block: true
32713         });
32714     },
32715
32716     anchors : {
32717         "west" : "left",
32718         "east" : "right",
32719         "north" : "top",
32720         "south" : "bottom"
32721     },
32722
32723     sanchors : {
32724         "west" : "l",
32725         "east" : "r",
32726         "north" : "t",
32727         "south" : "b"
32728     },
32729
32730     canchors : {
32731         "west" : "tl-tr",
32732         "east" : "tr-tl",
32733         "north" : "tl-bl",
32734         "south" : "bl-tl"
32735     },
32736
32737     getAnchor : function(){
32738         return this.anchors[this.position];
32739     },
32740
32741     getCollapseAnchor : function(){
32742         return this.canchors[this.position];
32743     },
32744
32745     getSlideAnchor : function(){
32746         return this.sanchors[this.position];
32747     },
32748
32749     getAlignAdj : function(){
32750         var cm = this.cmargins;
32751         switch(this.position){
32752             case "west":
32753                 return [0, 0];
32754             break;
32755             case "east":
32756                 return [0, 0];
32757             break;
32758             case "north":
32759                 return [0, 0];
32760             break;
32761             case "south":
32762                 return [0, 0];
32763             break;
32764         }
32765     },
32766
32767     getExpandAdj : function(){
32768         var c = this.collapsedEl, cm = this.cmargins;
32769         switch(this.position){
32770             case "west":
32771                 return [-(cm.right+c.getWidth()+cm.left), 0];
32772             break;
32773             case "east":
32774                 return [cm.right+c.getWidth()+cm.left, 0];
32775             break;
32776             case "north":
32777                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32778             break;
32779             case "south":
32780                 return [0, cm.top+cm.bottom+c.getHeight()];
32781             break;
32782         }
32783     }
32784 });/*
32785  * Based on:
32786  * Ext JS Library 1.1.1
32787  * Copyright(c) 2006-2007, Ext JS, LLC.
32788  *
32789  * Originally Released Under LGPL - original licence link has changed is not relivant.
32790  *
32791  * Fork - LGPL
32792  * <script type="text/javascript">
32793  */
32794 /*
32795  * These classes are private internal classes
32796  */
32797 Roo.CenterLayoutRegion = function(mgr, config){
32798     Roo.LayoutRegion.call(this, mgr, config, "center");
32799     this.visible = true;
32800     this.minWidth = config.minWidth || 20;
32801     this.minHeight = config.minHeight || 20;
32802 };
32803
32804 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32805     hide : function(){
32806         // center panel can't be hidden
32807     },
32808     
32809     show : function(){
32810         // center panel can't be hidden
32811     },
32812     
32813     getMinWidth: function(){
32814         return this.minWidth;
32815     },
32816     
32817     getMinHeight: function(){
32818         return this.minHeight;
32819     }
32820 });
32821
32822
32823 Roo.NorthLayoutRegion = function(mgr, config){
32824     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32825     if(this.split){
32826         this.split.placement = Roo.SplitBar.TOP;
32827         this.split.orientation = Roo.SplitBar.VERTICAL;
32828         this.split.el.addClass("x-layout-split-v");
32829     }
32830     var size = config.initialSize || config.height;
32831     if(typeof size != "undefined"){
32832         this.el.setHeight(size);
32833     }
32834 };
32835 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32836     orientation: Roo.SplitBar.VERTICAL,
32837     getBox : function(){
32838         if(this.collapsed){
32839             return this.collapsedEl.getBox();
32840         }
32841         var box = this.el.getBox();
32842         if(this.split){
32843             box.height += this.split.el.getHeight();
32844         }
32845         return box;
32846     },
32847     
32848     updateBox : function(box){
32849         if(this.split && !this.collapsed){
32850             box.height -= this.split.el.getHeight();
32851             this.split.el.setLeft(box.x);
32852             this.split.el.setTop(box.y+box.height);
32853             this.split.el.setWidth(box.width);
32854         }
32855         if(this.collapsed){
32856             this.updateBody(box.width, null);
32857         }
32858         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32859     }
32860 });
32861
32862 Roo.SouthLayoutRegion = function(mgr, config){
32863     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32864     if(this.split){
32865         this.split.placement = Roo.SplitBar.BOTTOM;
32866         this.split.orientation = Roo.SplitBar.VERTICAL;
32867         this.split.el.addClass("x-layout-split-v");
32868     }
32869     var size = config.initialSize || config.height;
32870     if(typeof size != "undefined"){
32871         this.el.setHeight(size);
32872     }
32873 };
32874 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32875     orientation: Roo.SplitBar.VERTICAL,
32876     getBox : function(){
32877         if(this.collapsed){
32878             return this.collapsedEl.getBox();
32879         }
32880         var box = this.el.getBox();
32881         if(this.split){
32882             var sh = this.split.el.getHeight();
32883             box.height += sh;
32884             box.y -= sh;
32885         }
32886         return box;
32887     },
32888     
32889     updateBox : function(box){
32890         if(this.split && !this.collapsed){
32891             var sh = this.split.el.getHeight();
32892             box.height -= sh;
32893             box.y += sh;
32894             this.split.el.setLeft(box.x);
32895             this.split.el.setTop(box.y-sh);
32896             this.split.el.setWidth(box.width);
32897         }
32898         if(this.collapsed){
32899             this.updateBody(box.width, null);
32900         }
32901         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32902     }
32903 });
32904
32905 Roo.EastLayoutRegion = function(mgr, config){
32906     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32907     if(this.split){
32908         this.split.placement = Roo.SplitBar.RIGHT;
32909         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32910         this.split.el.addClass("x-layout-split-h");
32911     }
32912     var size = config.initialSize || config.width;
32913     if(typeof size != "undefined"){
32914         this.el.setWidth(size);
32915     }
32916 };
32917 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32918     orientation: Roo.SplitBar.HORIZONTAL,
32919     getBox : function(){
32920         if(this.collapsed){
32921             return this.collapsedEl.getBox();
32922         }
32923         var box = this.el.getBox();
32924         if(this.split){
32925             var sw = this.split.el.getWidth();
32926             box.width += sw;
32927             box.x -= sw;
32928         }
32929         return box;
32930     },
32931
32932     updateBox : function(box){
32933         if(this.split && !this.collapsed){
32934             var sw = this.split.el.getWidth();
32935             box.width -= sw;
32936             this.split.el.setLeft(box.x);
32937             this.split.el.setTop(box.y);
32938             this.split.el.setHeight(box.height);
32939             box.x += sw;
32940         }
32941         if(this.collapsed){
32942             this.updateBody(null, box.height);
32943         }
32944         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32945     }
32946 });
32947
32948 Roo.WestLayoutRegion = function(mgr, config){
32949     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32950     if(this.split){
32951         this.split.placement = Roo.SplitBar.LEFT;
32952         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32953         this.split.el.addClass("x-layout-split-h");
32954     }
32955     var size = config.initialSize || config.width;
32956     if(typeof size != "undefined"){
32957         this.el.setWidth(size);
32958     }
32959 };
32960 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32961     orientation: Roo.SplitBar.HORIZONTAL,
32962     getBox : function(){
32963         if(this.collapsed){
32964             return this.collapsedEl.getBox();
32965         }
32966         var box = this.el.getBox();
32967         if(this.split){
32968             box.width += this.split.el.getWidth();
32969         }
32970         return box;
32971     },
32972     
32973     updateBox : function(box){
32974         if(this.split && !this.collapsed){
32975             var sw = this.split.el.getWidth();
32976             box.width -= sw;
32977             this.split.el.setLeft(box.x+box.width);
32978             this.split.el.setTop(box.y);
32979             this.split.el.setHeight(box.height);
32980         }
32981         if(this.collapsed){
32982             this.updateBody(null, box.height);
32983         }
32984         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32985     }
32986 });
32987 /*
32988  * Based on:
32989  * Ext JS Library 1.1.1
32990  * Copyright(c) 2006-2007, Ext JS, LLC.
32991  *
32992  * Originally Released Under LGPL - original licence link has changed is not relivant.
32993  *
32994  * Fork - LGPL
32995  * <script type="text/javascript">
32996  */
32997  
32998  
32999 /*
33000  * Private internal class for reading and applying state
33001  */
33002 Roo.LayoutStateManager = function(layout){
33003      // default empty state
33004      this.state = {
33005         north: {},
33006         south: {},
33007         east: {},
33008         west: {}       
33009     };
33010 };
33011
33012 Roo.LayoutStateManager.prototype = {
33013     init : function(layout, provider){
33014         this.provider = provider;
33015         var state = provider.get(layout.id+"-layout-state");
33016         if(state){
33017             var wasUpdating = layout.isUpdating();
33018             if(!wasUpdating){
33019                 layout.beginUpdate();
33020             }
33021             for(var key in state){
33022                 if(typeof state[key] != "function"){
33023                     var rstate = state[key];
33024                     var r = layout.getRegion(key);
33025                     if(r && rstate){
33026                         if(rstate.size){
33027                             r.resizeTo(rstate.size);
33028                         }
33029                         if(rstate.collapsed == true){
33030                             r.collapse(true);
33031                         }else{
33032                             r.expand(null, true);
33033                         }
33034                     }
33035                 }
33036             }
33037             if(!wasUpdating){
33038                 layout.endUpdate();
33039             }
33040             this.state = state; 
33041         }
33042         this.layout = layout;
33043         layout.on("regionresized", this.onRegionResized, this);
33044         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33045         layout.on("regionexpanded", this.onRegionExpanded, this);
33046     },
33047     
33048     storeState : function(){
33049         this.provider.set(this.layout.id+"-layout-state", this.state);
33050     },
33051     
33052     onRegionResized : function(region, newSize){
33053         this.state[region.getPosition()].size = newSize;
33054         this.storeState();
33055     },
33056     
33057     onRegionCollapsed : function(region){
33058         this.state[region.getPosition()].collapsed = true;
33059         this.storeState();
33060     },
33061     
33062     onRegionExpanded : function(region){
33063         this.state[region.getPosition()].collapsed = false;
33064         this.storeState();
33065     }
33066 };/*
33067  * Based on:
33068  * Ext JS Library 1.1.1
33069  * Copyright(c) 2006-2007, Ext JS, LLC.
33070  *
33071  * Originally Released Under LGPL - original licence link has changed is not relivant.
33072  *
33073  * Fork - LGPL
33074  * <script type="text/javascript">
33075  */
33076 /**
33077  * @class Roo.ContentPanel
33078  * @extends Roo.util.Observable
33079  * A basic ContentPanel element.
33080  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33081  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33082  * @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
33083  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33084  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33085  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33086  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33087  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33088  * @cfg {String} title          The title for this panel
33089  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33090  * @cfg {String} url            Calls {@link #setUrl} with this value
33091  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33092  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33093  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33094  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33095
33096  * @constructor
33097  * Create a new ContentPanel.
33098  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33099  * @param {String/Object} config A string to set only the title or a config object
33100  * @param {String} content (optional) Set the HTML content for this panel
33101  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33102  */
33103 Roo.ContentPanel = function(el, config, content){
33104     
33105      
33106     /*
33107     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33108         config = el;
33109         el = Roo.id();
33110     }
33111     if (config && config.parentLayout) { 
33112         el = config.parentLayout.el.createChild(); 
33113     }
33114     */
33115     if(el.autoCreate){ // xtype is available if this is called from factory
33116         config = el;
33117         el = Roo.id();
33118     }
33119     this.el = Roo.get(el);
33120     if(!this.el && config && config.autoCreate){
33121         if(typeof config.autoCreate == "object"){
33122             if(!config.autoCreate.id){
33123                 config.autoCreate.id = config.id||el;
33124             }
33125             this.el = Roo.DomHelper.append(document.body,
33126                         config.autoCreate, true);
33127         }else{
33128             this.el = Roo.DomHelper.append(document.body,
33129                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33130         }
33131     }
33132     this.closable = false;
33133     this.loaded = false;
33134     this.active = false;
33135     if(typeof config == "string"){
33136         this.title = config;
33137     }else{
33138         Roo.apply(this, config);
33139     }
33140     
33141     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33142         this.wrapEl = this.el.wrap();
33143         this.toolbar.container = this.el.insertSibling(false, 'before');
33144         this.toolbar = new Roo.Toolbar(this.toolbar);
33145     }
33146     
33147     // xtype created footer. - not sure if will work as we normally have to render first..
33148     if (this.footer && !this.footer.el && this.footer.xtype) {
33149         if (!this.wrapEl) {
33150             this.wrapEl = this.el.wrap();
33151         }
33152     
33153         this.footer.container = this.wrapEl.createChild();
33154          
33155         this.footer = Roo.factory(this.footer, Roo);
33156         
33157     }
33158     
33159     if(this.resizeEl){
33160         this.resizeEl = Roo.get(this.resizeEl, true);
33161     }else{
33162         this.resizeEl = this.el;
33163     }
33164     this.addEvents({
33165         /**
33166          * @event activate
33167          * Fires when this panel is activated. 
33168          * @param {Roo.ContentPanel} this
33169          */
33170         "activate" : true,
33171         /**
33172          * @event deactivate
33173          * Fires when this panel is activated. 
33174          * @param {Roo.ContentPanel} this
33175          */
33176         "deactivate" : true,
33177
33178         /**
33179          * @event resize
33180          * Fires when this panel is resized if fitToFrame is true.
33181          * @param {Roo.ContentPanel} this
33182          * @param {Number} width The width after any component adjustments
33183          * @param {Number} height The height after any component adjustments
33184          */
33185         "resize" : true,
33186         
33187          /**
33188          * @event render
33189          * Fires when this tab is created
33190          * @param {Roo.ContentPanel} this
33191          */
33192         "render" : true
33193         
33194         
33195         
33196     });
33197     if(this.autoScroll){
33198         this.resizeEl.setStyle("overflow", "auto");
33199     } else {
33200         // fix randome scrolling
33201         this.el.on('scroll', function() {
33202             Roo.log('fix random scolling');
33203             this.scrollTo('top',0); 
33204         });
33205     }
33206     content = content || this.content;
33207     if(content){
33208         this.setContent(content);
33209     }
33210     if(config && config.url){
33211         this.setUrl(this.url, this.params, this.loadOnce);
33212     }
33213     
33214     
33215     
33216     Roo.ContentPanel.superclass.constructor.call(this);
33217     
33218     this.fireEvent('render', this);
33219 };
33220
33221 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33222     tabTip:'',
33223     setRegion : function(region){
33224         this.region = region;
33225         if(region){
33226            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
33227         }else{
33228            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
33229         } 
33230     },
33231     
33232     /**
33233      * Returns the toolbar for this Panel if one was configured. 
33234      * @return {Roo.Toolbar} 
33235      */
33236     getToolbar : function(){
33237         return this.toolbar;
33238     },
33239     
33240     setActiveState : function(active){
33241         this.active = active;
33242         if(!active){
33243             this.fireEvent("deactivate", this);
33244         }else{
33245             this.fireEvent("activate", this);
33246         }
33247     },
33248     /**
33249      * Updates this panel's element
33250      * @param {String} content The new content
33251      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33252     */
33253     setContent : function(content, loadScripts){
33254         this.el.update(content, loadScripts);
33255     },
33256
33257     ignoreResize : function(w, h){
33258         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33259             return true;
33260         }else{
33261             this.lastSize = {width: w, height: h};
33262             return false;
33263         }
33264     },
33265     /**
33266      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33267      * @return {Roo.UpdateManager} The UpdateManager
33268      */
33269     getUpdateManager : function(){
33270         return this.el.getUpdateManager();
33271     },
33272      /**
33273      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33274      * @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:
33275 <pre><code>
33276 panel.load({
33277     url: "your-url.php",
33278     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33279     callback: yourFunction,
33280     scope: yourObject, //(optional scope)
33281     discardUrl: false,
33282     nocache: false,
33283     text: "Loading...",
33284     timeout: 30,
33285     scripts: false
33286 });
33287 </code></pre>
33288      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33289      * 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.
33290      * @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}
33291      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33292      * @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.
33293      * @return {Roo.ContentPanel} this
33294      */
33295     load : function(){
33296         var um = this.el.getUpdateManager();
33297         um.update.apply(um, arguments);
33298         return this;
33299     },
33300
33301
33302     /**
33303      * 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.
33304      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33305      * @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)
33306      * @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)
33307      * @return {Roo.UpdateManager} The UpdateManager
33308      */
33309     setUrl : function(url, params, loadOnce){
33310         if(this.refreshDelegate){
33311             this.removeListener("activate", this.refreshDelegate);
33312         }
33313         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33314         this.on("activate", this.refreshDelegate);
33315         return this.el.getUpdateManager();
33316     },
33317     
33318     _handleRefresh : function(url, params, loadOnce){
33319         if(!loadOnce || !this.loaded){
33320             var updater = this.el.getUpdateManager();
33321             updater.update(url, params, this._setLoaded.createDelegate(this));
33322         }
33323     },
33324     
33325     _setLoaded : function(){
33326         this.loaded = true;
33327     }, 
33328     
33329     /**
33330      * Returns this panel's id
33331      * @return {String} 
33332      */
33333     getId : function(){
33334         return this.el.id;
33335     },
33336     
33337     /** 
33338      * Returns this panel's element - used by regiosn to add.
33339      * @return {Roo.Element} 
33340      */
33341     getEl : function(){
33342         return this.wrapEl || this.el;
33343     },
33344     
33345     adjustForComponents : function(width, height)
33346     {
33347         Roo.log('adjustForComponents ');
33348         if(this.resizeEl != this.el){
33349             width -= this.el.getFrameWidth('lr');
33350             height -= this.el.getFrameWidth('tb');
33351         }
33352         if(this.toolbar){
33353             var te = this.toolbar.getEl();
33354             height -= te.getHeight();
33355             te.setWidth(width);
33356         }
33357         if(this.footer){
33358             var te = this.footer.getEl();
33359             Roo.log("footer:" + te.getHeight());
33360             
33361             height -= te.getHeight();
33362             te.setWidth(width);
33363         }
33364         
33365         
33366         if(this.adjustments){
33367             width += this.adjustments[0];
33368             height += this.adjustments[1];
33369         }
33370         return {"width": width, "height": height};
33371     },
33372     
33373     setSize : function(width, height){
33374         if(this.fitToFrame && !this.ignoreResize(width, height)){
33375             if(this.fitContainer && this.resizeEl != this.el){
33376                 this.el.setSize(width, height);
33377             }
33378             var size = this.adjustForComponents(width, height);
33379             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33380             this.fireEvent('resize', this, size.width, size.height);
33381         }
33382     },
33383     
33384     /**
33385      * Returns this panel's title
33386      * @return {String} 
33387      */
33388     getTitle : function(){
33389         return this.title;
33390     },
33391     
33392     /**
33393      * Set this panel's title
33394      * @param {String} title
33395      */
33396     setTitle : function(title){
33397         this.title = title;
33398         if(this.region){
33399             this.region.updatePanelTitle(this, title);
33400         }
33401     },
33402     
33403     /**
33404      * Returns true is this panel was configured to be closable
33405      * @return {Boolean} 
33406      */
33407     isClosable : function(){
33408         return this.closable;
33409     },
33410     
33411     beforeSlide : function(){
33412         this.el.clip();
33413         this.resizeEl.clip();
33414     },
33415     
33416     afterSlide : function(){
33417         this.el.unclip();
33418         this.resizeEl.unclip();
33419     },
33420     
33421     /**
33422      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33423      *   Will fail silently if the {@link #setUrl} method has not been called.
33424      *   This does not activate the panel, just updates its content.
33425      */
33426     refresh : function(){
33427         if(this.refreshDelegate){
33428            this.loaded = false;
33429            this.refreshDelegate();
33430         }
33431     },
33432     
33433     /**
33434      * Destroys this panel
33435      */
33436     destroy : function(){
33437         this.el.removeAllListeners();
33438         var tempEl = document.createElement("span");
33439         tempEl.appendChild(this.el.dom);
33440         tempEl.innerHTML = "";
33441         this.el.remove();
33442         this.el = null;
33443     },
33444     
33445     /**
33446      * form - if the content panel contains a form - this is a reference to it.
33447      * @type {Roo.form.Form}
33448      */
33449     form : false,
33450     /**
33451      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33452      *    This contains a reference to it.
33453      * @type {Roo.View}
33454      */
33455     view : false,
33456     
33457       /**
33458      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33459      * <pre><code>
33460
33461 layout.addxtype({
33462        xtype : 'Form',
33463        items: [ .... ]
33464    }
33465 );
33466
33467 </code></pre>
33468      * @param {Object} cfg Xtype definition of item to add.
33469      */
33470     
33471     addxtype : function(cfg) {
33472         // add form..
33473         if (cfg.xtype.match(/^Form$/)) {
33474             
33475             var el;
33476             //if (this.footer) {
33477             //    el = this.footer.container.insertSibling(false, 'before');
33478             //} else {
33479                 el = this.el.createChild();
33480             //}
33481
33482             this.form = new  Roo.form.Form(cfg);
33483             
33484             
33485             if ( this.form.allItems.length) this.form.render(el.dom);
33486             return this.form;
33487         }
33488         // should only have one of theses..
33489         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33490             // views..
33491             cfg.el = this.el.appendChild(document.createElement("div"));
33492             // factory?
33493             
33494             var ret = new Roo.factory(cfg);
33495             ret.render && ret.render(false, ''); // render blank..
33496             this.view = ret;
33497             return ret;
33498         }
33499         return false;
33500     }
33501 });
33502
33503 /**
33504  * @class Roo.GridPanel
33505  * @extends Roo.ContentPanel
33506  * @constructor
33507  * Create a new GridPanel.
33508  * @param {Roo.grid.Grid} grid The grid for this panel
33509  * @param {String/Object} config A string to set only the panel's title, or a config object
33510  */
33511 Roo.GridPanel = function(grid, config){
33512     
33513   
33514     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33515         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33516         
33517     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33518     
33519     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33520     
33521     if(this.toolbar){
33522         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33523     }
33524     // xtype created footer. - not sure if will work as we normally have to render first..
33525     if (this.footer && !this.footer.el && this.footer.xtype) {
33526         
33527         this.footer.container = this.grid.getView().getFooterPanel(true);
33528         this.footer.dataSource = this.grid.dataSource;
33529         this.footer = Roo.factory(this.footer, Roo);
33530         
33531     }
33532     
33533     grid.monitorWindowResize = false; // turn off autosizing
33534     grid.autoHeight = false;
33535     grid.autoWidth = false;
33536     this.grid = grid;
33537     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33538 };
33539
33540 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33541     getId : function(){
33542         return this.grid.id;
33543     },
33544     
33545     /**
33546      * Returns the grid for this panel
33547      * @return {Roo.grid.Grid} 
33548      */
33549     getGrid : function(){
33550         return this.grid;    
33551     },
33552     
33553     setSize : function(width, height){
33554         if(!this.ignoreResize(width, height)){
33555             var grid = this.grid;
33556             var size = this.adjustForComponents(width, height);
33557             grid.getGridEl().setSize(size.width, size.height);
33558             grid.autoSize();
33559         }
33560     },
33561     
33562     beforeSlide : function(){
33563         this.grid.getView().scroller.clip();
33564     },
33565     
33566     afterSlide : function(){
33567         this.grid.getView().scroller.unclip();
33568     },
33569     
33570     destroy : function(){
33571         this.grid.destroy();
33572         delete this.grid;
33573         Roo.GridPanel.superclass.destroy.call(this); 
33574     }
33575 });
33576
33577
33578 /**
33579  * @class Roo.NestedLayoutPanel
33580  * @extends Roo.ContentPanel
33581  * @constructor
33582  * Create a new NestedLayoutPanel.
33583  * 
33584  * 
33585  * @param {Roo.BorderLayout} layout The layout for this panel
33586  * @param {String/Object} config A string to set only the title or a config object
33587  */
33588 Roo.NestedLayoutPanel = function(layout, config)
33589 {
33590     // construct with only one argument..
33591     /* FIXME - implement nicer consturctors
33592     if (layout.layout) {
33593         config = layout;
33594         layout = config.layout;
33595         delete config.layout;
33596     }
33597     if (layout.xtype && !layout.getEl) {
33598         // then layout needs constructing..
33599         layout = Roo.factory(layout, Roo);
33600     }
33601     */
33602     
33603     
33604     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33605     
33606     layout.monitorWindowResize = false; // turn off autosizing
33607     this.layout = layout;
33608     this.layout.getEl().addClass("x-layout-nested-layout");
33609     
33610     
33611     
33612     
33613 };
33614
33615 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33616
33617     setSize : function(width, height){
33618         if(!this.ignoreResize(width, height)){
33619             var size = this.adjustForComponents(width, height);
33620             var el = this.layout.getEl();
33621             el.setSize(size.width, size.height);
33622             var touch = el.dom.offsetWidth;
33623             this.layout.layout();
33624             // ie requires a double layout on the first pass
33625             if(Roo.isIE && !this.initialized){
33626                 this.initialized = true;
33627                 this.layout.layout();
33628             }
33629         }
33630     },
33631     
33632     // activate all subpanels if not currently active..
33633     
33634     setActiveState : function(active){
33635         this.active = active;
33636         if(!active){
33637             this.fireEvent("deactivate", this);
33638             return;
33639         }
33640         
33641         this.fireEvent("activate", this);
33642         // not sure if this should happen before or after..
33643         if (!this.layout) {
33644             return; // should not happen..
33645         }
33646         var reg = false;
33647         for (var r in this.layout.regions) {
33648             reg = this.layout.getRegion(r);
33649             if (reg.getActivePanel()) {
33650                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33651                 reg.setActivePanel(reg.getActivePanel());
33652                 continue;
33653             }
33654             if (!reg.panels.length) {
33655                 continue;
33656             }
33657             reg.showPanel(reg.getPanel(0));
33658         }
33659         
33660         
33661         
33662         
33663     },
33664     
33665     /**
33666      * Returns the nested BorderLayout for this panel
33667      * @return {Roo.BorderLayout} 
33668      */
33669     getLayout : function(){
33670         return this.layout;
33671     },
33672     
33673      /**
33674      * Adds a xtype elements to the layout of the nested panel
33675      * <pre><code>
33676
33677 panel.addxtype({
33678        xtype : 'ContentPanel',
33679        region: 'west',
33680        items: [ .... ]
33681    }
33682 );
33683
33684 panel.addxtype({
33685         xtype : 'NestedLayoutPanel',
33686         region: 'west',
33687         layout: {
33688            center: { },
33689            west: { }   
33690         },
33691         items : [ ... list of content panels or nested layout panels.. ]
33692    }
33693 );
33694 </code></pre>
33695      * @param {Object} cfg Xtype definition of item to add.
33696      */
33697     addxtype : function(cfg) {
33698         return this.layout.addxtype(cfg);
33699     
33700     }
33701 });
33702
33703 Roo.ScrollPanel = function(el, config, content){
33704     config = config || {};
33705     config.fitToFrame = true;
33706     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33707     
33708     this.el.dom.style.overflow = "hidden";
33709     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33710     this.el.removeClass("x-layout-inactive-content");
33711     this.el.on("mousewheel", this.onWheel, this);
33712
33713     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33714     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33715     up.unselectable(); down.unselectable();
33716     up.on("click", this.scrollUp, this);
33717     down.on("click", this.scrollDown, this);
33718     up.addClassOnOver("x-scroller-btn-over");
33719     down.addClassOnOver("x-scroller-btn-over");
33720     up.addClassOnClick("x-scroller-btn-click");
33721     down.addClassOnClick("x-scroller-btn-click");
33722     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33723
33724     this.resizeEl = this.el;
33725     this.el = wrap; this.up = up; this.down = down;
33726 };
33727
33728 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33729     increment : 100,
33730     wheelIncrement : 5,
33731     scrollUp : function(){
33732         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33733     },
33734
33735     scrollDown : function(){
33736         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33737     },
33738
33739     afterScroll : function(){
33740         var el = this.resizeEl;
33741         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33742         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33743         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33744     },
33745
33746     setSize : function(){
33747         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33748         this.afterScroll();
33749     },
33750
33751     onWheel : function(e){
33752         var d = e.getWheelDelta();
33753         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33754         this.afterScroll();
33755         e.stopEvent();
33756     },
33757
33758     setContent : function(content, loadScripts){
33759         this.resizeEl.update(content, loadScripts);
33760     }
33761
33762 });
33763
33764
33765
33766
33767
33768
33769
33770
33771
33772 /**
33773  * @class Roo.TreePanel
33774  * @extends Roo.ContentPanel
33775  * @constructor
33776  * Create a new TreePanel. - defaults to fit/scoll contents.
33777  * @param {String/Object} config A string to set only the panel's title, or a config object
33778  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33779  */
33780 Roo.TreePanel = function(config){
33781     var el = config.el;
33782     var tree = config.tree;
33783     delete config.tree; 
33784     delete config.el; // hopefull!
33785     
33786     // wrapper for IE7 strict & safari scroll issue
33787     
33788     var treeEl = el.createChild();
33789     config.resizeEl = treeEl;
33790     
33791     
33792     
33793     Roo.TreePanel.superclass.constructor.call(this, el, config);
33794  
33795  
33796     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33797     //console.log(tree);
33798     this.on('activate', function()
33799     {
33800         if (this.tree.rendered) {
33801             return;
33802         }
33803         //console.log('render tree');
33804         this.tree.render();
33805     });
33806     // this should not be needed.. - it's actually the 'el' that resizes?
33807     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
33808     
33809     //this.on('resize',  function (cp, w, h) {
33810     //        this.tree.innerCt.setWidth(w);
33811     //        this.tree.innerCt.setHeight(h);
33812     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
33813     //});
33814
33815         
33816     
33817 };
33818
33819 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33820     fitToFrame : true,
33821     autoScroll : true
33822 });
33823
33824
33825
33826
33827
33828
33829
33830
33831
33832
33833
33834 /*
33835  * Based on:
33836  * Ext JS Library 1.1.1
33837  * Copyright(c) 2006-2007, Ext JS, LLC.
33838  *
33839  * Originally Released Under LGPL - original licence link has changed is not relivant.
33840  *
33841  * Fork - LGPL
33842  * <script type="text/javascript">
33843  */
33844  
33845
33846 /**
33847  * @class Roo.ReaderLayout
33848  * @extends Roo.BorderLayout
33849  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33850  * center region containing two nested regions (a top one for a list view and one for item preview below),
33851  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33852  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33853  * expedites the setup of the overall layout and regions for this common application style.
33854  * Example:
33855  <pre><code>
33856 var reader = new Roo.ReaderLayout();
33857 var CP = Roo.ContentPanel;  // shortcut for adding
33858
33859 reader.beginUpdate();
33860 reader.add("north", new CP("north", "North"));
33861 reader.add("west", new CP("west", {title: "West"}));
33862 reader.add("east", new CP("east", {title: "East"}));
33863
33864 reader.regions.listView.add(new CP("listView", "List"));
33865 reader.regions.preview.add(new CP("preview", "Preview"));
33866 reader.endUpdate();
33867 </code></pre>
33868 * @constructor
33869 * Create a new ReaderLayout
33870 * @param {Object} config Configuration options
33871 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33872 * document.body if omitted)
33873 */
33874 Roo.ReaderLayout = function(config, renderTo){
33875     var c = config || {size:{}};
33876     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33877         north: c.north !== false ? Roo.apply({
33878             split:false,
33879             initialSize: 32,
33880             titlebar: false
33881         }, c.north) : false,
33882         west: c.west !== false ? Roo.apply({
33883             split:true,
33884             initialSize: 200,
33885             minSize: 175,
33886             maxSize: 400,
33887             titlebar: true,
33888             collapsible: true,
33889             animate: true,
33890             margins:{left:5,right:0,bottom:5,top:5},
33891             cmargins:{left:5,right:5,bottom:5,top:5}
33892         }, c.west) : false,
33893         east: c.east !== false ? Roo.apply({
33894             split:true,
33895             initialSize: 200,
33896             minSize: 175,
33897             maxSize: 400,
33898             titlebar: true,
33899             collapsible: true,
33900             animate: true,
33901             margins:{left:0,right:5,bottom:5,top:5},
33902             cmargins:{left:5,right:5,bottom:5,top:5}
33903         }, c.east) : false,
33904         center: Roo.apply({
33905             tabPosition: 'top',
33906             autoScroll:false,
33907             closeOnTab: true,
33908             titlebar:false,
33909             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33910         }, c.center)
33911     });
33912
33913     this.el.addClass('x-reader');
33914
33915     this.beginUpdate();
33916
33917     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33918         south: c.preview !== false ? Roo.apply({
33919             split:true,
33920             initialSize: 200,
33921             minSize: 100,
33922             autoScroll:true,
33923             collapsible:true,
33924             titlebar: true,
33925             cmargins:{top:5,left:0, right:0, bottom:0}
33926         }, c.preview) : false,
33927         center: Roo.apply({
33928             autoScroll:false,
33929             titlebar:false,
33930             minHeight:200
33931         }, c.listView)
33932     });
33933     this.add('center', new Roo.NestedLayoutPanel(inner,
33934             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33935
33936     this.endUpdate();
33937
33938     this.regions.preview = inner.getRegion('south');
33939     this.regions.listView = inner.getRegion('center');
33940 };
33941
33942 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33943  * Based on:
33944  * Ext JS Library 1.1.1
33945  * Copyright(c) 2006-2007, Ext JS, LLC.
33946  *
33947  * Originally Released Under LGPL - original licence link has changed is not relivant.
33948  *
33949  * Fork - LGPL
33950  * <script type="text/javascript">
33951  */
33952  
33953 /**
33954  * @class Roo.grid.Grid
33955  * @extends Roo.util.Observable
33956  * This class represents the primary interface of a component based grid control.
33957  * <br><br>Usage:<pre><code>
33958  var grid = new Roo.grid.Grid("my-container-id", {
33959      ds: myDataStore,
33960      cm: myColModel,
33961      selModel: mySelectionModel,
33962      autoSizeColumns: true,
33963      monitorWindowResize: false,
33964      trackMouseOver: true
33965  });
33966  // set any options
33967  grid.render();
33968  * </code></pre>
33969  * <b>Common Problems:</b><br/>
33970  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33971  * element will correct this<br/>
33972  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33973  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33974  * are unpredictable.<br/>
33975  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33976  * grid to calculate dimensions/offsets.<br/>
33977   * @constructor
33978  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33979  * The container MUST have some type of size defined for the grid to fill. The container will be
33980  * automatically set to position relative if it isn't already.
33981  * @param {Object} config A config object that sets properties on this grid.
33982  */
33983 Roo.grid.Grid = function(container, config){
33984         // initialize the container
33985         this.container = Roo.get(container);
33986         this.container.update("");
33987         this.container.setStyle("overflow", "hidden");
33988     this.container.addClass('x-grid-container');
33989
33990     this.id = this.container.id;
33991
33992     Roo.apply(this, config);
33993     // check and correct shorthanded configs
33994     if(this.ds){
33995         this.dataSource = this.ds;
33996         delete this.ds;
33997     }
33998     if(this.cm){
33999         this.colModel = this.cm;
34000         delete this.cm;
34001     }
34002     if(this.sm){
34003         this.selModel = this.sm;
34004         delete this.sm;
34005     }
34006
34007     if (this.selModel) {
34008         this.selModel = Roo.factory(this.selModel, Roo.grid);
34009         this.sm = this.selModel;
34010         this.sm.xmodule = this.xmodule || false;
34011     }
34012     if (typeof(this.colModel.config) == 'undefined') {
34013         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34014         this.cm = this.colModel;
34015         this.cm.xmodule = this.xmodule || false;
34016     }
34017     if (this.dataSource) {
34018         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34019         this.ds = this.dataSource;
34020         this.ds.xmodule = this.xmodule || false;
34021          
34022     }
34023     
34024     
34025     
34026     if(this.width){
34027         this.container.setWidth(this.width);
34028     }
34029
34030     if(this.height){
34031         this.container.setHeight(this.height);
34032     }
34033     /** @private */
34034         this.addEvents({
34035         // raw events
34036         /**
34037          * @event click
34038          * The raw click event for the entire grid.
34039          * @param {Roo.EventObject} e
34040          */
34041         "click" : true,
34042         /**
34043          * @event dblclick
34044          * The raw dblclick event for the entire grid.
34045          * @param {Roo.EventObject} e
34046          */
34047         "dblclick" : true,
34048         /**
34049          * @event contextmenu
34050          * The raw contextmenu event for the entire grid.
34051          * @param {Roo.EventObject} e
34052          */
34053         "contextmenu" : true,
34054         /**
34055          * @event mousedown
34056          * The raw mousedown event for the entire grid.
34057          * @param {Roo.EventObject} e
34058          */
34059         "mousedown" : true,
34060         /**
34061          * @event mouseup
34062          * The raw mouseup event for the entire grid.
34063          * @param {Roo.EventObject} e
34064          */
34065         "mouseup" : true,
34066         /**
34067          * @event mouseover
34068          * The raw mouseover event for the entire grid.
34069          * @param {Roo.EventObject} e
34070          */
34071         "mouseover" : true,
34072         /**
34073          * @event mouseout
34074          * The raw mouseout event for the entire grid.
34075          * @param {Roo.EventObject} e
34076          */
34077         "mouseout" : true,
34078         /**
34079          * @event keypress
34080          * The raw keypress event for the entire grid.
34081          * @param {Roo.EventObject} e
34082          */
34083         "keypress" : true,
34084         /**
34085          * @event keydown
34086          * The raw keydown event for the entire grid.
34087          * @param {Roo.EventObject} e
34088          */
34089         "keydown" : true,
34090
34091         // custom events
34092
34093         /**
34094          * @event cellclick
34095          * Fires when a cell is clicked
34096          * @param {Grid} this
34097          * @param {Number} rowIndex
34098          * @param {Number} columnIndex
34099          * @param {Roo.EventObject} e
34100          */
34101         "cellclick" : true,
34102         /**
34103          * @event celldblclick
34104          * Fires when a cell is double clicked
34105          * @param {Grid} this
34106          * @param {Number} rowIndex
34107          * @param {Number} columnIndex
34108          * @param {Roo.EventObject} e
34109          */
34110         "celldblclick" : true,
34111         /**
34112          * @event rowclick
34113          * Fires when a row is clicked
34114          * @param {Grid} this
34115          * @param {Number} rowIndex
34116          * @param {Roo.EventObject} e
34117          */
34118         "rowclick" : true,
34119         /**
34120          * @event rowdblclick
34121          * Fires when a row is double clicked
34122          * @param {Grid} this
34123          * @param {Number} rowIndex
34124          * @param {Roo.EventObject} e
34125          */
34126         "rowdblclick" : true,
34127         /**
34128          * @event headerclick
34129          * Fires when a header is clicked
34130          * @param {Grid} this
34131          * @param {Number} columnIndex
34132          * @param {Roo.EventObject} e
34133          */
34134         "headerclick" : true,
34135         /**
34136          * @event headerdblclick
34137          * Fires when a header cell is double clicked
34138          * @param {Grid} this
34139          * @param {Number} columnIndex
34140          * @param {Roo.EventObject} e
34141          */
34142         "headerdblclick" : true,
34143         /**
34144          * @event rowcontextmenu
34145          * Fires when a row is right clicked
34146          * @param {Grid} this
34147          * @param {Number} rowIndex
34148          * @param {Roo.EventObject} e
34149          */
34150         "rowcontextmenu" : true,
34151         /**
34152          * @event cellcontextmenu
34153          * Fires when a cell is right clicked
34154          * @param {Grid} this
34155          * @param {Number} rowIndex
34156          * @param {Number} cellIndex
34157          * @param {Roo.EventObject} e
34158          */
34159          "cellcontextmenu" : true,
34160         /**
34161          * @event headercontextmenu
34162          * Fires when a header is right clicked
34163          * @param {Grid} this
34164          * @param {Number} columnIndex
34165          * @param {Roo.EventObject} e
34166          */
34167         "headercontextmenu" : true,
34168         /**
34169          * @event bodyscroll
34170          * Fires when the body element is scrolled
34171          * @param {Number} scrollLeft
34172          * @param {Number} scrollTop
34173          */
34174         "bodyscroll" : true,
34175         /**
34176          * @event columnresize
34177          * Fires when the user resizes a column
34178          * @param {Number} columnIndex
34179          * @param {Number} newSize
34180          */
34181         "columnresize" : true,
34182         /**
34183          * @event columnmove
34184          * Fires when the user moves a column
34185          * @param {Number} oldIndex
34186          * @param {Number} newIndex
34187          */
34188         "columnmove" : true,
34189         /**
34190          * @event startdrag
34191          * Fires when row(s) start being dragged
34192          * @param {Grid} this
34193          * @param {Roo.GridDD} dd The drag drop object
34194          * @param {event} e The raw browser event
34195          */
34196         "startdrag" : true,
34197         /**
34198          * @event enddrag
34199          * Fires when a drag operation is complete
34200          * @param {Grid} this
34201          * @param {Roo.GridDD} dd The drag drop object
34202          * @param {event} e The raw browser event
34203          */
34204         "enddrag" : true,
34205         /**
34206          * @event dragdrop
34207          * Fires when dragged row(s) are dropped on a valid DD target
34208          * @param {Grid} this
34209          * @param {Roo.GridDD} dd The drag drop object
34210          * @param {String} targetId The target drag drop object
34211          * @param {event} e The raw browser event
34212          */
34213         "dragdrop" : true,
34214         /**
34215          * @event dragover
34216          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34217          * @param {Grid} this
34218          * @param {Roo.GridDD} dd The drag drop object
34219          * @param {String} targetId The target drag drop object
34220          * @param {event} e The raw browser event
34221          */
34222         "dragover" : true,
34223         /**
34224          * @event dragenter
34225          *  Fires when the dragged row(s) first cross another DD target while being dragged
34226          * @param {Grid} this
34227          * @param {Roo.GridDD} dd The drag drop object
34228          * @param {String} targetId The target drag drop object
34229          * @param {event} e The raw browser event
34230          */
34231         "dragenter" : true,
34232         /**
34233          * @event dragout
34234          * Fires when the dragged row(s) leave another DD target while being dragged
34235          * @param {Grid} this
34236          * @param {Roo.GridDD} dd The drag drop object
34237          * @param {String} targetId The target drag drop object
34238          * @param {event} e The raw browser event
34239          */
34240         "dragout" : true,
34241         /**
34242          * @event rowclass
34243          * Fires when a row is rendered, so you can change add a style to it.
34244          * @param {GridView} gridview   The grid view
34245          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
34246          */
34247         'rowclass' : true,
34248
34249         /**
34250          * @event render
34251          * Fires when the grid is rendered
34252          * @param {Grid} grid
34253          */
34254         'render' : true
34255     });
34256
34257     Roo.grid.Grid.superclass.constructor.call(this);
34258 };
34259 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
34260     
34261     /**
34262      * @cfg {String} ddGroup - drag drop group.
34263      */
34264
34265     /**
34266      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
34267      */
34268     minColumnWidth : 25,
34269
34270     /**
34271      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
34272      * <b>on initial render.</b> It is more efficient to explicitly size the columns
34273      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
34274      */
34275     autoSizeColumns : false,
34276
34277     /**
34278      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
34279      */
34280     autoSizeHeaders : true,
34281
34282     /**
34283      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
34284      */
34285     monitorWindowResize : true,
34286
34287     /**
34288      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34289      * rows measured to get a columns size. Default is 0 (all rows).
34290      */
34291     maxRowsToMeasure : 0,
34292
34293     /**
34294      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34295      */
34296     trackMouseOver : true,
34297
34298     /**
34299     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34300     */
34301     
34302     /**
34303     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34304     */
34305     enableDragDrop : false,
34306     
34307     /**
34308     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34309     */
34310     enableColumnMove : true,
34311     
34312     /**
34313     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34314     */
34315     enableColumnHide : true,
34316     
34317     /**
34318     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34319     */
34320     enableRowHeightSync : false,
34321     
34322     /**
34323     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34324     */
34325     stripeRows : true,
34326     
34327     /**
34328     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34329     */
34330     autoHeight : false,
34331
34332     /**
34333      * @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.
34334      */
34335     autoExpandColumn : false,
34336
34337     /**
34338     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34339     * Default is 50.
34340     */
34341     autoExpandMin : 50,
34342
34343     /**
34344     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34345     */
34346     autoExpandMax : 1000,
34347
34348     /**
34349     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34350     */
34351     view : null,
34352
34353     /**
34354     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34355     */
34356     loadMask : false,
34357     /**
34358     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
34359     */
34360     dropTarget: false,
34361     
34362    
34363     
34364     // private
34365     rendered : false,
34366
34367     /**
34368     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34369     * of a fixed width. Default is false.
34370     */
34371     /**
34372     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34373     */
34374     /**
34375      * Called once after all setup has been completed and the grid is ready to be rendered.
34376      * @return {Roo.grid.Grid} this
34377      */
34378     render : function()
34379     {
34380         var c = this.container;
34381         // try to detect autoHeight/width mode
34382         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34383             this.autoHeight = true;
34384         }
34385         var view = this.getView();
34386         view.init(this);
34387
34388         c.on("click", this.onClick, this);
34389         c.on("dblclick", this.onDblClick, this);
34390         c.on("contextmenu", this.onContextMenu, this);
34391         c.on("keydown", this.onKeyDown, this);
34392
34393         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34394
34395         this.getSelectionModel().init(this);
34396
34397         view.render();
34398
34399         if(this.loadMask){
34400             this.loadMask = new Roo.LoadMask(this.container,
34401                     Roo.apply({store:this.dataSource}, this.loadMask));
34402         }
34403         
34404         
34405         if (this.toolbar && this.toolbar.xtype) {
34406             this.toolbar.container = this.getView().getHeaderPanel(true);
34407             this.toolbar = new Roo.Toolbar(this.toolbar);
34408         }
34409         if (this.footer && this.footer.xtype) {
34410             this.footer.dataSource = this.getDataSource();
34411             this.footer.container = this.getView().getFooterPanel(true);
34412             this.footer = Roo.factory(this.footer, Roo);
34413         }
34414         if (this.dropTarget && this.dropTarget.xtype) {
34415             delete this.dropTarget.xtype;
34416             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34417         }
34418         
34419         
34420         this.rendered = true;
34421         this.fireEvent('render', this);
34422         return this;
34423     },
34424
34425         /**
34426          * Reconfigures the grid to use a different Store and Column Model.
34427          * The View will be bound to the new objects and refreshed.
34428          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34429          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34430          */
34431     reconfigure : function(dataSource, colModel){
34432         if(this.loadMask){
34433             this.loadMask.destroy();
34434             this.loadMask = new Roo.LoadMask(this.container,
34435                     Roo.apply({store:dataSource}, this.loadMask));
34436         }
34437         this.view.bind(dataSource, colModel);
34438         this.dataSource = dataSource;
34439         this.colModel = colModel;
34440         this.view.refresh(true);
34441     },
34442
34443     // private
34444     onKeyDown : function(e){
34445         this.fireEvent("keydown", e);
34446     },
34447
34448     /**
34449      * Destroy this grid.
34450      * @param {Boolean} removeEl True to remove the element
34451      */
34452     destroy : function(removeEl, keepListeners){
34453         if(this.loadMask){
34454             this.loadMask.destroy();
34455         }
34456         var c = this.container;
34457         c.removeAllListeners();
34458         this.view.destroy();
34459         this.colModel.purgeListeners();
34460         if(!keepListeners){
34461             this.purgeListeners();
34462         }
34463         c.update("");
34464         if(removeEl === true){
34465             c.remove();
34466         }
34467     },
34468
34469     // private
34470     processEvent : function(name, e){
34471         this.fireEvent(name, e);
34472         var t = e.getTarget();
34473         var v = this.view;
34474         var header = v.findHeaderIndex(t);
34475         if(header !== false){
34476             this.fireEvent("header" + name, this, header, e);
34477         }else{
34478             var row = v.findRowIndex(t);
34479             var cell = v.findCellIndex(t);
34480             if(row !== false){
34481                 this.fireEvent("row" + name, this, row, e);
34482                 if(cell !== false){
34483                     this.fireEvent("cell" + name, this, row, cell, e);
34484                 }
34485             }
34486         }
34487     },
34488
34489     // private
34490     onClick : function(e){
34491         this.processEvent("click", e);
34492     },
34493
34494     // private
34495     onContextMenu : function(e, t){
34496         this.processEvent("contextmenu", e);
34497     },
34498
34499     // private
34500     onDblClick : function(e){
34501         this.processEvent("dblclick", e);
34502     },
34503
34504     // private
34505     walkCells : function(row, col, step, fn, scope){
34506         var cm = this.colModel, clen = cm.getColumnCount();
34507         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34508         if(step < 0){
34509             if(col < 0){
34510                 row--;
34511                 first = false;
34512             }
34513             while(row >= 0){
34514                 if(!first){
34515                     col = clen-1;
34516                 }
34517                 first = false;
34518                 while(col >= 0){
34519                     if(fn.call(scope || this, row, col, cm) === true){
34520                         return [row, col];
34521                     }
34522                     col--;
34523                 }
34524                 row--;
34525             }
34526         } else {
34527             if(col >= clen){
34528                 row++;
34529                 first = false;
34530             }
34531             while(row < rlen){
34532                 if(!first){
34533                     col = 0;
34534                 }
34535                 first = false;
34536                 while(col < clen){
34537                     if(fn.call(scope || this, row, col, cm) === true){
34538                         return [row, col];
34539                     }
34540                     col++;
34541                 }
34542                 row++;
34543             }
34544         }
34545         return null;
34546     },
34547
34548     // private
34549     getSelections : function(){
34550         return this.selModel.getSelections();
34551     },
34552
34553     /**
34554      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34555      * but if manual update is required this method will initiate it.
34556      */
34557     autoSize : function(){
34558         if(this.rendered){
34559             this.view.layout();
34560             if(this.view.adjustForScroll){
34561                 this.view.adjustForScroll();
34562             }
34563         }
34564     },
34565
34566     /**
34567      * Returns the grid's underlying element.
34568      * @return {Element} The element
34569      */
34570     getGridEl : function(){
34571         return this.container;
34572     },
34573
34574     // private for compatibility, overridden by editor grid
34575     stopEditing : function(){},
34576
34577     /**
34578      * Returns the grid's SelectionModel.
34579      * @return {SelectionModel}
34580      */
34581     getSelectionModel : function(){
34582         if(!this.selModel){
34583             this.selModel = new Roo.grid.RowSelectionModel();
34584         }
34585         return this.selModel;
34586     },
34587
34588     /**
34589      * Returns the grid's DataSource.
34590      * @return {DataSource}
34591      */
34592     getDataSource : function(){
34593         return this.dataSource;
34594     },
34595
34596     /**
34597      * Returns the grid's ColumnModel.
34598      * @return {ColumnModel}
34599      */
34600     getColumnModel : function(){
34601         return this.colModel;
34602     },
34603
34604     /**
34605      * Returns the grid's GridView object.
34606      * @return {GridView}
34607      */
34608     getView : function(){
34609         if(!this.view){
34610             this.view = new Roo.grid.GridView(this.viewConfig);
34611         }
34612         return this.view;
34613     },
34614     /**
34615      * Called to get grid's drag proxy text, by default returns this.ddText.
34616      * @return {String}
34617      */
34618     getDragDropText : function(){
34619         var count = this.selModel.getCount();
34620         return String.format(this.ddText, count, count == 1 ? '' : 's');
34621     }
34622 });
34623 /**
34624  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34625  * %0 is replaced with the number of selected rows.
34626  * @type String
34627  */
34628 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34629  * Based on:
34630  * Ext JS Library 1.1.1
34631  * Copyright(c) 2006-2007, Ext JS, LLC.
34632  *
34633  * Originally Released Under LGPL - original licence link has changed is not relivant.
34634  *
34635  * Fork - LGPL
34636  * <script type="text/javascript">
34637  */
34638  
34639 Roo.grid.AbstractGridView = function(){
34640         this.grid = null;
34641         
34642         this.events = {
34643             "beforerowremoved" : true,
34644             "beforerowsinserted" : true,
34645             "beforerefresh" : true,
34646             "rowremoved" : true,
34647             "rowsinserted" : true,
34648             "rowupdated" : true,
34649             "refresh" : true
34650         };
34651     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34652 };
34653
34654 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34655     rowClass : "x-grid-row",
34656     cellClass : "x-grid-cell",
34657     tdClass : "x-grid-td",
34658     hdClass : "x-grid-hd",
34659     splitClass : "x-grid-hd-split",
34660     
34661         init: function(grid){
34662         this.grid = grid;
34663                 var cid = this.grid.getGridEl().id;
34664         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34665         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34666         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34667         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34668         },
34669         
34670         getColumnRenderers : function(){
34671         var renderers = [];
34672         var cm = this.grid.colModel;
34673         var colCount = cm.getColumnCount();
34674         for(var i = 0; i < colCount; i++){
34675             renderers[i] = cm.getRenderer(i);
34676         }
34677         return renderers;
34678     },
34679     
34680     getColumnIds : function(){
34681         var ids = [];
34682         var cm = this.grid.colModel;
34683         var colCount = cm.getColumnCount();
34684         for(var i = 0; i < colCount; i++){
34685             ids[i] = cm.getColumnId(i);
34686         }
34687         return ids;
34688     },
34689     
34690     getDataIndexes : function(){
34691         if(!this.indexMap){
34692             this.indexMap = this.buildIndexMap();
34693         }
34694         return this.indexMap.colToData;
34695     },
34696     
34697     getColumnIndexByDataIndex : function(dataIndex){
34698         if(!this.indexMap){
34699             this.indexMap = this.buildIndexMap();
34700         }
34701         return this.indexMap.dataToCol[dataIndex];
34702     },
34703     
34704     /**
34705      * Set a css style for a column dynamically. 
34706      * @param {Number} colIndex The index of the column
34707      * @param {String} name The css property name
34708      * @param {String} value The css value
34709      */
34710     setCSSStyle : function(colIndex, name, value){
34711         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34712         Roo.util.CSS.updateRule(selector, name, value);
34713     },
34714     
34715     generateRules : function(cm){
34716         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34717         Roo.util.CSS.removeStyleSheet(rulesId);
34718         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34719             var cid = cm.getColumnId(i);
34720             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34721                          this.tdSelector, cid, " {\n}\n",
34722                          this.hdSelector, cid, " {\n}\n",
34723                          this.splitSelector, cid, " {\n}\n");
34724         }
34725         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34726     }
34727 });/*
34728  * Based on:
34729  * Ext JS Library 1.1.1
34730  * Copyright(c) 2006-2007, Ext JS, LLC.
34731  *
34732  * Originally Released Under LGPL - original licence link has changed is not relivant.
34733  *
34734  * Fork - LGPL
34735  * <script type="text/javascript">
34736  */
34737
34738 // private
34739 // This is a support class used internally by the Grid components
34740 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34741     this.grid = grid;
34742     this.view = grid.getView();
34743     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34744     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34745     if(hd2){
34746         this.setHandleElId(Roo.id(hd));
34747         this.setOuterHandleElId(Roo.id(hd2));
34748     }
34749     this.scroll = false;
34750 };
34751 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34752     maxDragWidth: 120,
34753     getDragData : function(e){
34754         var t = Roo.lib.Event.getTarget(e);
34755         var h = this.view.findHeaderCell(t);
34756         if(h){
34757             return {ddel: h.firstChild, header:h};
34758         }
34759         return false;
34760     },
34761
34762     onInitDrag : function(e){
34763         this.view.headersDisabled = true;
34764         var clone = this.dragData.ddel.cloneNode(true);
34765         clone.id = Roo.id();
34766         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34767         this.proxy.update(clone);
34768         return true;
34769     },
34770
34771     afterValidDrop : function(){
34772         var v = this.view;
34773         setTimeout(function(){
34774             v.headersDisabled = false;
34775         }, 50);
34776     },
34777
34778     afterInvalidDrop : function(){
34779         var v = this.view;
34780         setTimeout(function(){
34781             v.headersDisabled = false;
34782         }, 50);
34783     }
34784 });
34785 /*
34786  * Based on:
34787  * Ext JS Library 1.1.1
34788  * Copyright(c) 2006-2007, Ext JS, LLC.
34789  *
34790  * Originally Released Under LGPL - original licence link has changed is not relivant.
34791  *
34792  * Fork - LGPL
34793  * <script type="text/javascript">
34794  */
34795 // private
34796 // This is a support class used internally by the Grid components
34797 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34798     this.grid = grid;
34799     this.view = grid.getView();
34800     // split the proxies so they don't interfere with mouse events
34801     this.proxyTop = Roo.DomHelper.append(document.body, {
34802         cls:"col-move-top", html:"&#160;"
34803     }, true);
34804     this.proxyBottom = Roo.DomHelper.append(document.body, {
34805         cls:"col-move-bottom", html:"&#160;"
34806     }, true);
34807     this.proxyTop.hide = this.proxyBottom.hide = function(){
34808         this.setLeftTop(-100,-100);
34809         this.setStyle("visibility", "hidden");
34810     };
34811     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34812     // temporarily disabled
34813     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34814     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34815 };
34816 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34817     proxyOffsets : [-4, -9],
34818     fly: Roo.Element.fly,
34819
34820     getTargetFromEvent : function(e){
34821         var t = Roo.lib.Event.getTarget(e);
34822         var cindex = this.view.findCellIndex(t);
34823         if(cindex !== false){
34824             return this.view.getHeaderCell(cindex);
34825         }
34826         return null;
34827     },
34828
34829     nextVisible : function(h){
34830         var v = this.view, cm = this.grid.colModel;
34831         h = h.nextSibling;
34832         while(h){
34833             if(!cm.isHidden(v.getCellIndex(h))){
34834                 return h;
34835             }
34836             h = h.nextSibling;
34837         }
34838         return null;
34839     },
34840
34841     prevVisible : function(h){
34842         var v = this.view, cm = this.grid.colModel;
34843         h = h.prevSibling;
34844         while(h){
34845             if(!cm.isHidden(v.getCellIndex(h))){
34846                 return h;
34847             }
34848             h = h.prevSibling;
34849         }
34850         return null;
34851     },
34852
34853     positionIndicator : function(h, n, e){
34854         var x = Roo.lib.Event.getPageX(e);
34855         var r = Roo.lib.Dom.getRegion(n.firstChild);
34856         var px, pt, py = r.top + this.proxyOffsets[1];
34857         if((r.right - x) <= (r.right-r.left)/2){
34858             px = r.right+this.view.borderWidth;
34859             pt = "after";
34860         }else{
34861             px = r.left;
34862             pt = "before";
34863         }
34864         var oldIndex = this.view.getCellIndex(h);
34865         var newIndex = this.view.getCellIndex(n);
34866
34867         if(this.grid.colModel.isFixed(newIndex)){
34868             return false;
34869         }
34870
34871         var locked = this.grid.colModel.isLocked(newIndex);
34872
34873         if(pt == "after"){
34874             newIndex++;
34875         }
34876         if(oldIndex < newIndex){
34877             newIndex--;
34878         }
34879         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34880             return false;
34881         }
34882         px +=  this.proxyOffsets[0];
34883         this.proxyTop.setLeftTop(px, py);
34884         this.proxyTop.show();
34885         if(!this.bottomOffset){
34886             this.bottomOffset = this.view.mainHd.getHeight();
34887         }
34888         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34889         this.proxyBottom.show();
34890         return pt;
34891     },
34892
34893     onNodeEnter : function(n, dd, e, data){
34894         if(data.header != n){
34895             this.positionIndicator(data.header, n, e);
34896         }
34897     },
34898
34899     onNodeOver : function(n, dd, e, data){
34900         var result = false;
34901         if(data.header != n){
34902             result = this.positionIndicator(data.header, n, e);
34903         }
34904         if(!result){
34905             this.proxyTop.hide();
34906             this.proxyBottom.hide();
34907         }
34908         return result ? this.dropAllowed : this.dropNotAllowed;
34909     },
34910
34911     onNodeOut : function(n, dd, e, data){
34912         this.proxyTop.hide();
34913         this.proxyBottom.hide();
34914     },
34915
34916     onNodeDrop : function(n, dd, e, data){
34917         var h = data.header;
34918         if(h != n){
34919             var cm = this.grid.colModel;
34920             var x = Roo.lib.Event.getPageX(e);
34921             var r = Roo.lib.Dom.getRegion(n.firstChild);
34922             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34923             var oldIndex = this.view.getCellIndex(h);
34924             var newIndex = this.view.getCellIndex(n);
34925             var locked = cm.isLocked(newIndex);
34926             if(pt == "after"){
34927                 newIndex++;
34928             }
34929             if(oldIndex < newIndex){
34930                 newIndex--;
34931             }
34932             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34933                 return false;
34934             }
34935             cm.setLocked(oldIndex, locked, true);
34936             cm.moveColumn(oldIndex, newIndex);
34937             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34938             return true;
34939         }
34940         return false;
34941     }
34942 });
34943 /*
34944  * Based on:
34945  * Ext JS Library 1.1.1
34946  * Copyright(c) 2006-2007, Ext JS, LLC.
34947  *
34948  * Originally Released Under LGPL - original licence link has changed is not relivant.
34949  *
34950  * Fork - LGPL
34951  * <script type="text/javascript">
34952  */
34953   
34954 /**
34955  * @class Roo.grid.GridView
34956  * @extends Roo.util.Observable
34957  *
34958  * @constructor
34959  * @param {Object} config
34960  */
34961 Roo.grid.GridView = function(config){
34962     Roo.grid.GridView.superclass.constructor.call(this);
34963     this.el = null;
34964
34965     Roo.apply(this, config);
34966 };
34967
34968 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34969
34970     
34971     rowClass : "x-grid-row",
34972
34973     cellClass : "x-grid-col",
34974
34975     tdClass : "x-grid-td",
34976
34977     hdClass : "x-grid-hd",
34978
34979     splitClass : "x-grid-split",
34980
34981     sortClasses : ["sort-asc", "sort-desc"],
34982
34983     enableMoveAnim : false,
34984
34985     hlColor: "C3DAF9",
34986
34987     dh : Roo.DomHelper,
34988
34989     fly : Roo.Element.fly,
34990
34991     css : Roo.util.CSS,
34992
34993     borderWidth: 1,
34994
34995     splitOffset: 3,
34996
34997     scrollIncrement : 22,
34998
34999     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35000
35001     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35002
35003     bind : function(ds, cm){
35004         if(this.ds){
35005             this.ds.un("load", this.onLoad, this);
35006             this.ds.un("datachanged", this.onDataChange, this);
35007             this.ds.un("add", this.onAdd, this);
35008             this.ds.un("remove", this.onRemove, this);
35009             this.ds.un("update", this.onUpdate, this);
35010             this.ds.un("clear", this.onClear, this);
35011         }
35012         if(ds){
35013             ds.on("load", this.onLoad, this);
35014             ds.on("datachanged", this.onDataChange, this);
35015             ds.on("add", this.onAdd, this);
35016             ds.on("remove", this.onRemove, this);
35017             ds.on("update", this.onUpdate, this);
35018             ds.on("clear", this.onClear, this);
35019         }
35020         this.ds = ds;
35021
35022         if(this.cm){
35023             this.cm.un("widthchange", this.onColWidthChange, this);
35024             this.cm.un("headerchange", this.onHeaderChange, this);
35025             this.cm.un("hiddenchange", this.onHiddenChange, this);
35026             this.cm.un("columnmoved", this.onColumnMove, this);
35027             this.cm.un("columnlockchange", this.onColumnLock, this);
35028         }
35029         if(cm){
35030             this.generateRules(cm);
35031             cm.on("widthchange", this.onColWidthChange, this);
35032             cm.on("headerchange", this.onHeaderChange, this);
35033             cm.on("hiddenchange", this.onHiddenChange, this);
35034             cm.on("columnmoved", this.onColumnMove, this);
35035             cm.on("columnlockchange", this.onColumnLock, this);
35036         }
35037         this.cm = cm;
35038     },
35039
35040     init: function(grid){
35041         Roo.grid.GridView.superclass.init.call(this, grid);
35042
35043         this.bind(grid.dataSource, grid.colModel);
35044
35045         grid.on("headerclick", this.handleHeaderClick, this);
35046
35047         if(grid.trackMouseOver){
35048             grid.on("mouseover", this.onRowOver, this);
35049             grid.on("mouseout", this.onRowOut, this);
35050         }
35051         grid.cancelTextSelection = function(){};
35052         this.gridId = grid.id;
35053
35054         var tpls = this.templates || {};
35055
35056         if(!tpls.master){
35057             tpls.master = new Roo.Template(
35058                '<div class="x-grid" hidefocus="true">',
35059                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35060                   '<div class="x-grid-topbar"></div>',
35061                   '<div class="x-grid-scroller"><div></div></div>',
35062                   '<div class="x-grid-locked">',
35063                       '<div class="x-grid-header">{lockedHeader}</div>',
35064                       '<div class="x-grid-body">{lockedBody}</div>',
35065                   "</div>",
35066                   '<div class="x-grid-viewport">',
35067                       '<div class="x-grid-header">{header}</div>',
35068                       '<div class="x-grid-body">{body}</div>',
35069                   "</div>",
35070                   '<div class="x-grid-bottombar"></div>',
35071                  
35072                   '<div class="x-grid-resize-proxy">&#160;</div>',
35073                "</div>"
35074             );
35075             tpls.master.disableformats = true;
35076         }
35077
35078         if(!tpls.header){
35079             tpls.header = new Roo.Template(
35080                '<table border="0" cellspacing="0" cellpadding="0">',
35081                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35082                "</table>{splits}"
35083             );
35084             tpls.header.disableformats = true;
35085         }
35086         tpls.header.compile();
35087
35088         if(!tpls.hcell){
35089             tpls.hcell = new Roo.Template(
35090                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35091                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35092                 "</div></td>"
35093              );
35094              tpls.hcell.disableFormats = true;
35095         }
35096         tpls.hcell.compile();
35097
35098         if(!tpls.hsplit){
35099             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
35100             tpls.hsplit.disableFormats = true;
35101         }
35102         tpls.hsplit.compile();
35103
35104         if(!tpls.body){
35105             tpls.body = new Roo.Template(
35106                '<table border="0" cellspacing="0" cellpadding="0">',
35107                "<tbody>{rows}</tbody>",
35108                "</table>"
35109             );
35110             tpls.body.disableFormats = true;
35111         }
35112         tpls.body.compile();
35113
35114         if(!tpls.row){
35115             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35116             tpls.row.disableFormats = true;
35117         }
35118         tpls.row.compile();
35119
35120         if(!tpls.cell){
35121             tpls.cell = new Roo.Template(
35122                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35123                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
35124                 "</td>"
35125             );
35126             tpls.cell.disableFormats = true;
35127         }
35128         tpls.cell.compile();
35129
35130         this.templates = tpls;
35131     },
35132
35133     // remap these for backwards compat
35134     onColWidthChange : function(){
35135         this.updateColumns.apply(this, arguments);
35136     },
35137     onHeaderChange : function(){
35138         this.updateHeaders.apply(this, arguments);
35139     }, 
35140     onHiddenChange : function(){
35141         this.handleHiddenChange.apply(this, arguments);
35142     },
35143     onColumnMove : function(){
35144         this.handleColumnMove.apply(this, arguments);
35145     },
35146     onColumnLock : function(){
35147         this.handleLockChange.apply(this, arguments);
35148     },
35149
35150     onDataChange : function(){
35151         this.refresh();
35152         this.updateHeaderSortState();
35153     },
35154
35155     onClear : function(){
35156         this.refresh();
35157     },
35158
35159     onUpdate : function(ds, record){
35160         this.refreshRow(record);
35161     },
35162
35163     refreshRow : function(record){
35164         var ds = this.ds, index;
35165         if(typeof record == 'number'){
35166             index = record;
35167             record = ds.getAt(index);
35168         }else{
35169             index = ds.indexOf(record);
35170         }
35171         this.insertRows(ds, index, index, true);
35172         this.onRemove(ds, record, index+1, true);
35173         this.syncRowHeights(index, index);
35174         this.layout();
35175         this.fireEvent("rowupdated", this, index, record);
35176     },
35177
35178     onAdd : function(ds, records, index){
35179         this.insertRows(ds, index, index + (records.length-1));
35180     },
35181
35182     onRemove : function(ds, record, index, isUpdate){
35183         if(isUpdate !== true){
35184             this.fireEvent("beforerowremoved", this, index, record);
35185         }
35186         var bt = this.getBodyTable(), lt = this.getLockedTable();
35187         if(bt.rows[index]){
35188             bt.firstChild.removeChild(bt.rows[index]);
35189         }
35190         if(lt.rows[index]){
35191             lt.firstChild.removeChild(lt.rows[index]);
35192         }
35193         if(isUpdate !== true){
35194             this.stripeRows(index);
35195             this.syncRowHeights(index, index);
35196             this.layout();
35197             this.fireEvent("rowremoved", this, index, record);
35198         }
35199     },
35200
35201     onLoad : function(){
35202         this.scrollToTop();
35203     },
35204
35205     /**
35206      * Scrolls the grid to the top
35207      */
35208     scrollToTop : function(){
35209         if(this.scroller){
35210             this.scroller.dom.scrollTop = 0;
35211             this.syncScroll();
35212         }
35213     },
35214
35215     /**
35216      * Gets a panel in the header of the grid that can be used for toolbars etc.
35217      * After modifying the contents of this panel a call to grid.autoSize() may be
35218      * required to register any changes in size.
35219      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35220      * @return Roo.Element
35221      */
35222     getHeaderPanel : function(doShow){
35223         if(doShow){
35224             this.headerPanel.show();
35225         }
35226         return this.headerPanel;
35227     },
35228
35229     /**
35230      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35231      * After modifying the contents of this panel a call to grid.autoSize() may be
35232      * required to register any changes in size.
35233      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35234      * @return Roo.Element
35235      */
35236     getFooterPanel : function(doShow){
35237         if(doShow){
35238             this.footerPanel.show();
35239         }
35240         return this.footerPanel;
35241     },
35242
35243     initElements : function(){
35244         var E = Roo.Element;
35245         var el = this.grid.getGridEl().dom.firstChild;
35246         var cs = el.childNodes;
35247
35248         this.el = new E(el);
35249         
35250          this.focusEl = new E(el.firstChild);
35251         this.focusEl.swallowEvent("click", true);
35252         
35253         this.headerPanel = new E(cs[1]);
35254         this.headerPanel.enableDisplayMode("block");
35255
35256         this.scroller = new E(cs[2]);
35257         this.scrollSizer = new E(this.scroller.dom.firstChild);
35258
35259         this.lockedWrap = new E(cs[3]);
35260         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35261         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35262
35263         this.mainWrap = new E(cs[4]);
35264         this.mainHd = new E(this.mainWrap.dom.firstChild);
35265         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35266
35267         this.footerPanel = new E(cs[5]);
35268         this.footerPanel.enableDisplayMode("block");
35269
35270         this.resizeProxy = new E(cs[6]);
35271
35272         this.headerSelector = String.format(
35273            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35274            this.lockedHd.id, this.mainHd.id
35275         );
35276
35277         this.splitterSelector = String.format(
35278            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35279            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35280         );
35281     },
35282     idToCssName : function(s)
35283     {
35284         return s.replace(/[^a-z0-9]+/ig, '-');
35285     },
35286
35287     getHeaderCell : function(index){
35288         return Roo.DomQuery.select(this.headerSelector)[index];
35289     },
35290
35291     getHeaderCellMeasure : function(index){
35292         return this.getHeaderCell(index).firstChild;
35293     },
35294
35295     getHeaderCellText : function(index){
35296         return this.getHeaderCell(index).firstChild.firstChild;
35297     },
35298
35299     getLockedTable : function(){
35300         return this.lockedBody.dom.firstChild;
35301     },
35302
35303     getBodyTable : function(){
35304         return this.mainBody.dom.firstChild;
35305     },
35306
35307     getLockedRow : function(index){
35308         return this.getLockedTable().rows[index];
35309     },
35310
35311     getRow : function(index){
35312         return this.getBodyTable().rows[index];
35313     },
35314
35315     getRowComposite : function(index){
35316         if(!this.rowEl){
35317             this.rowEl = new Roo.CompositeElementLite();
35318         }
35319         var els = [], lrow, mrow;
35320         if(lrow = this.getLockedRow(index)){
35321             els.push(lrow);
35322         }
35323         if(mrow = this.getRow(index)){
35324             els.push(mrow);
35325         }
35326         this.rowEl.elements = els;
35327         return this.rowEl;
35328     },
35329     /**
35330      * Gets the 'td' of the cell
35331      * 
35332      * @param {Integer} rowIndex row to select
35333      * @param {Integer} colIndex column to select
35334      * 
35335      * @return {Object} 
35336      */
35337     getCell : function(rowIndex, colIndex){
35338         var locked = this.cm.getLockedCount();
35339         var source;
35340         if(colIndex < locked){
35341             source = this.lockedBody.dom.firstChild;
35342         }else{
35343             source = this.mainBody.dom.firstChild;
35344             colIndex -= locked;
35345         }
35346         return source.rows[rowIndex].childNodes[colIndex];
35347     },
35348
35349     getCellText : function(rowIndex, colIndex){
35350         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35351     },
35352
35353     getCellBox : function(cell){
35354         var b = this.fly(cell).getBox();
35355         if(Roo.isOpera){ // opera fails to report the Y
35356             b.y = cell.offsetTop + this.mainBody.getY();
35357         }
35358         return b;
35359     },
35360
35361     getCellIndex : function(cell){
35362         var id = String(cell.className).match(this.cellRE);
35363         if(id){
35364             return parseInt(id[1], 10);
35365         }
35366         return 0;
35367     },
35368
35369     findHeaderIndex : function(n){
35370         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35371         return r ? this.getCellIndex(r) : false;
35372     },
35373
35374     findHeaderCell : function(n){
35375         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35376         return r ? r : false;
35377     },
35378
35379     findRowIndex : function(n){
35380         if(!n){
35381             return false;
35382         }
35383         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35384         return r ? r.rowIndex : false;
35385     },
35386
35387     findCellIndex : function(node){
35388         var stop = this.el.dom;
35389         while(node && node != stop){
35390             if(this.findRE.test(node.className)){
35391                 return this.getCellIndex(node);
35392             }
35393             node = node.parentNode;
35394         }
35395         return false;
35396     },
35397
35398     getColumnId : function(index){
35399         return this.cm.getColumnId(index);
35400     },
35401
35402     getSplitters : function()
35403     {
35404         if(this.splitterSelector){
35405            return Roo.DomQuery.select(this.splitterSelector);
35406         }else{
35407             return null;
35408       }
35409     },
35410
35411     getSplitter : function(index){
35412         return this.getSplitters()[index];
35413     },
35414
35415     onRowOver : function(e, t){
35416         var row;
35417         if((row = this.findRowIndex(t)) !== false){
35418             this.getRowComposite(row).addClass("x-grid-row-over");
35419         }
35420     },
35421
35422     onRowOut : function(e, t){
35423         var row;
35424         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35425             this.getRowComposite(row).removeClass("x-grid-row-over");
35426         }
35427     },
35428
35429     renderHeaders : function(){
35430         var cm = this.cm;
35431         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35432         var cb = [], lb = [], sb = [], lsb = [], p = {};
35433         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35434             p.cellId = "x-grid-hd-0-" + i;
35435             p.splitId = "x-grid-csplit-0-" + i;
35436             p.id = cm.getColumnId(i);
35437             p.title = cm.getColumnTooltip(i) || "";
35438             p.value = cm.getColumnHeader(i) || "";
35439             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35440             if(!cm.isLocked(i)){
35441                 cb[cb.length] = ct.apply(p);
35442                 sb[sb.length] = st.apply(p);
35443             }else{
35444                 lb[lb.length] = ct.apply(p);
35445                 lsb[lsb.length] = st.apply(p);
35446             }
35447         }
35448         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35449                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35450     },
35451
35452     updateHeaders : function(){
35453         var html = this.renderHeaders();
35454         this.lockedHd.update(html[0]);
35455         this.mainHd.update(html[1]);
35456     },
35457
35458     /**
35459      * Focuses the specified row.
35460      * @param {Number} row The row index
35461      */
35462     focusRow : function(row)
35463     {
35464         //Roo.log('GridView.focusRow');
35465         var x = this.scroller.dom.scrollLeft;
35466         this.focusCell(row, 0, false);
35467         this.scroller.dom.scrollLeft = x;
35468     },
35469
35470     /**
35471      * Focuses the specified cell.
35472      * @param {Number} row The row index
35473      * @param {Number} col The column index
35474      * @param {Boolean} hscroll false to disable horizontal scrolling
35475      */
35476     focusCell : function(row, col, hscroll)
35477     {
35478         //Roo.log('GridView.focusCell');
35479         var el = this.ensureVisible(row, col, hscroll);
35480         this.focusEl.alignTo(el, "tl-tl");
35481         if(Roo.isGecko){
35482             this.focusEl.focus();
35483         }else{
35484             this.focusEl.focus.defer(1, this.focusEl);
35485         }
35486     },
35487
35488     /**
35489      * Scrolls the specified cell into view
35490      * @param {Number} row The row index
35491      * @param {Number} col The column index
35492      * @param {Boolean} hscroll false to disable horizontal scrolling
35493      */
35494     ensureVisible : function(row, col, hscroll)
35495     {
35496         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35497         //return null; //disable for testing.
35498         if(typeof row != "number"){
35499             row = row.rowIndex;
35500         }
35501         if(row < 0 && row >= this.ds.getCount()){
35502             return  null;
35503         }
35504         col = (col !== undefined ? col : 0);
35505         var cm = this.grid.colModel;
35506         while(cm.isHidden(col)){
35507             col++;
35508         }
35509
35510         var el = this.getCell(row, col);
35511         if(!el){
35512             return null;
35513         }
35514         var c = this.scroller.dom;
35515
35516         var ctop = parseInt(el.offsetTop, 10);
35517         var cleft = parseInt(el.offsetLeft, 10);
35518         var cbot = ctop + el.offsetHeight;
35519         var cright = cleft + el.offsetWidth;
35520         
35521         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35522         var stop = parseInt(c.scrollTop, 10);
35523         var sleft = parseInt(c.scrollLeft, 10);
35524         var sbot = stop + ch;
35525         var sright = sleft + c.clientWidth;
35526         /*
35527         Roo.log('GridView.ensureVisible:' +
35528                 ' ctop:' + ctop +
35529                 ' c.clientHeight:' + c.clientHeight +
35530                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35531                 ' stop:' + stop +
35532                 ' cbot:' + cbot +
35533                 ' sbot:' + sbot +
35534                 ' ch:' + ch  
35535                 );
35536         */
35537         if(ctop < stop){
35538              c.scrollTop = ctop;
35539             //Roo.log("set scrolltop to ctop DISABLE?");
35540         }else if(cbot > sbot){
35541             //Roo.log("set scrolltop to cbot-ch");
35542             c.scrollTop = cbot-ch;
35543         }
35544         
35545         if(hscroll !== false){
35546             if(cleft < sleft){
35547                 c.scrollLeft = cleft;
35548             }else if(cright > sright){
35549                 c.scrollLeft = cright-c.clientWidth;
35550             }
35551         }
35552          
35553         return el;
35554     },
35555
35556     updateColumns : function(){
35557         this.grid.stopEditing();
35558         var cm = this.grid.colModel, colIds = this.getColumnIds();
35559         //var totalWidth = cm.getTotalWidth();
35560         var pos = 0;
35561         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35562             //if(cm.isHidden(i)) continue;
35563             var w = cm.getColumnWidth(i);
35564             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35565             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35566         }
35567         this.updateSplitters();
35568     },
35569
35570     generateRules : function(cm){
35571         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35572         Roo.util.CSS.removeStyleSheet(rulesId);
35573         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35574             var cid = cm.getColumnId(i);
35575             var align = '';
35576             if(cm.config[i].align){
35577                 align = 'text-align:'+cm.config[i].align+';';
35578             }
35579             var hidden = '';
35580             if(cm.isHidden(i)){
35581                 hidden = 'display:none;';
35582             }
35583             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35584             ruleBuf.push(
35585                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35586                     this.hdSelector, cid, " {\n", align, width, "}\n",
35587                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35588                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35589         }
35590         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35591     },
35592
35593     updateSplitters : function(){
35594         var cm = this.cm, s = this.getSplitters();
35595         if(s){ // splitters not created yet
35596             var pos = 0, locked = true;
35597             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35598                 if(cm.isHidden(i)) continue;
35599                 var w = cm.getColumnWidth(i); // make sure it's a number
35600                 if(!cm.isLocked(i) && locked){
35601                     pos = 0;
35602                     locked = false;
35603                 }
35604                 pos += w;
35605                 s[i].style.left = (pos-this.splitOffset) + "px";
35606             }
35607         }
35608     },
35609
35610     handleHiddenChange : function(colModel, colIndex, hidden){
35611         if(hidden){
35612             this.hideColumn(colIndex);
35613         }else{
35614             this.unhideColumn(colIndex);
35615         }
35616     },
35617
35618     hideColumn : function(colIndex){
35619         var cid = this.getColumnId(colIndex);
35620         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35621         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35622         if(Roo.isSafari){
35623             this.updateHeaders();
35624         }
35625         this.updateSplitters();
35626         this.layout();
35627     },
35628
35629     unhideColumn : function(colIndex){
35630         var cid = this.getColumnId(colIndex);
35631         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35632         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35633
35634         if(Roo.isSafari){
35635             this.updateHeaders();
35636         }
35637         this.updateSplitters();
35638         this.layout();
35639     },
35640
35641     insertRows : function(dm, firstRow, lastRow, isUpdate){
35642         if(firstRow == 0 && lastRow == dm.getCount()-1){
35643             this.refresh();
35644         }else{
35645             if(!isUpdate){
35646                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35647             }
35648             var s = this.getScrollState();
35649             var markup = this.renderRows(firstRow, lastRow);
35650             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35651             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35652             this.restoreScroll(s);
35653             if(!isUpdate){
35654                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35655                 this.syncRowHeights(firstRow, lastRow);
35656                 this.stripeRows(firstRow);
35657                 this.layout();
35658             }
35659         }
35660     },
35661
35662     bufferRows : function(markup, target, index){
35663         var before = null, trows = target.rows, tbody = target.tBodies[0];
35664         if(index < trows.length){
35665             before = trows[index];
35666         }
35667         var b = document.createElement("div");
35668         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35669         var rows = b.firstChild.rows;
35670         for(var i = 0, len = rows.length; i < len; i++){
35671             if(before){
35672                 tbody.insertBefore(rows[0], before);
35673             }else{
35674                 tbody.appendChild(rows[0]);
35675             }
35676         }
35677         b.innerHTML = "";
35678         b = null;
35679     },
35680
35681     deleteRows : function(dm, firstRow, lastRow){
35682         if(dm.getRowCount()<1){
35683             this.fireEvent("beforerefresh", this);
35684             this.mainBody.update("");
35685             this.lockedBody.update("");
35686             this.fireEvent("refresh", this);
35687         }else{
35688             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35689             var bt = this.getBodyTable();
35690             var tbody = bt.firstChild;
35691             var rows = bt.rows;
35692             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35693                 tbody.removeChild(rows[firstRow]);
35694             }
35695             this.stripeRows(firstRow);
35696             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35697         }
35698     },
35699
35700     updateRows : function(dataSource, firstRow, lastRow){
35701         var s = this.getScrollState();
35702         this.refresh();
35703         this.restoreScroll(s);
35704     },
35705
35706     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35707         if(!noRefresh){
35708            this.refresh();
35709         }
35710         this.updateHeaderSortState();
35711     },
35712
35713     getScrollState : function(){
35714         
35715         var sb = this.scroller.dom;
35716         return {left: sb.scrollLeft, top: sb.scrollTop};
35717     },
35718
35719     stripeRows : function(startRow){
35720         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35721             return;
35722         }
35723         startRow = startRow || 0;
35724         var rows = this.getBodyTable().rows;
35725         var lrows = this.getLockedTable().rows;
35726         var cls = ' x-grid-row-alt ';
35727         for(var i = startRow, len = rows.length; i < len; i++){
35728             var row = rows[i], lrow = lrows[i];
35729             var isAlt = ((i+1) % 2 == 0);
35730             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35731             if(isAlt == hasAlt){
35732                 continue;
35733             }
35734             if(isAlt){
35735                 row.className += " x-grid-row-alt";
35736             }else{
35737                 row.className = row.className.replace("x-grid-row-alt", "");
35738             }
35739             if(lrow){
35740                 lrow.className = row.className;
35741             }
35742         }
35743     },
35744
35745     restoreScroll : function(state){
35746         //Roo.log('GridView.restoreScroll');
35747         var sb = this.scroller.dom;
35748         sb.scrollLeft = state.left;
35749         sb.scrollTop = state.top;
35750         this.syncScroll();
35751     },
35752
35753     syncScroll : function(){
35754         //Roo.log('GridView.syncScroll');
35755         var sb = this.scroller.dom;
35756         var sh = this.mainHd.dom;
35757         var bs = this.mainBody.dom;
35758         var lv = this.lockedBody.dom;
35759         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35760         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35761     },
35762
35763     handleScroll : function(e){
35764         this.syncScroll();
35765         var sb = this.scroller.dom;
35766         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35767         e.stopEvent();
35768     },
35769
35770     handleWheel : function(e){
35771         var d = e.getWheelDelta();
35772         this.scroller.dom.scrollTop -= d*22;
35773         // set this here to prevent jumpy scrolling on large tables
35774         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35775         e.stopEvent();
35776     },
35777
35778     renderRows : function(startRow, endRow){
35779         // pull in all the crap needed to render rows
35780         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35781         var colCount = cm.getColumnCount();
35782
35783         if(ds.getCount() < 1){
35784             return ["", ""];
35785         }
35786
35787         // build a map for all the columns
35788         var cs = [];
35789         for(var i = 0; i < colCount; i++){
35790             var name = cm.getDataIndex(i);
35791             cs[i] = {
35792                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35793                 renderer : cm.getRenderer(i),
35794                 id : cm.getColumnId(i),
35795                 locked : cm.isLocked(i)
35796             };
35797         }
35798
35799         startRow = startRow || 0;
35800         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35801
35802         // records to render
35803         var rs = ds.getRange(startRow, endRow);
35804
35805         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35806     },
35807
35808     // As much as I hate to duplicate code, this was branched because FireFox really hates
35809     // [].join("") on strings. The performance difference was substantial enough to
35810     // branch this function
35811     doRender : Roo.isGecko ?
35812             function(cs, rs, ds, startRow, colCount, stripe){
35813                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35814                 // buffers
35815                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35816                 
35817                 var hasListener = this.grid.hasListener('rowclass');
35818                 var rowcfg = {};
35819                 for(var j = 0, len = rs.length; j < len; j++){
35820                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35821                     for(var i = 0; i < colCount; i++){
35822                         c = cs[i];
35823                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35824                         p.id = c.id;
35825                         p.css = p.attr = "";
35826                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35827                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35828                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35829                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35830                         }
35831                         var markup = ct.apply(p);
35832                         if(!c.locked){
35833                             cb+= markup;
35834                         }else{
35835                             lcb+= markup;
35836                         }
35837                     }
35838                     var alt = [];
35839                     if(stripe && ((rowIndex+1) % 2 == 0)){
35840                         alt.push("x-grid-row-alt")
35841                     }
35842                     if(r.dirty){
35843                         alt.push(  " x-grid-dirty-row");
35844                     }
35845                     rp.cells = lcb;
35846                     if(this.getRowClass){
35847                         alt.push(this.getRowClass(r, rowIndex));
35848                     }
35849                     if (hasListener) {
35850                         rowcfg = {
35851                              
35852                             record: r,
35853                             rowIndex : rowIndex,
35854                             rowClass : ''
35855                         }
35856                         this.grid.fireEvent('rowclass', this, rowcfg);
35857                         alt.push(rowcfg.rowClass);
35858                     }
35859                     rp.alt = alt.join(" ");
35860                     lbuf+= rt.apply(rp);
35861                     rp.cells = cb;
35862                     buf+=  rt.apply(rp);
35863                 }
35864                 return [lbuf, buf];
35865             } :
35866             function(cs, rs, ds, startRow, colCount, stripe){
35867                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35868                 // buffers
35869                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35870                 var hasListener = this.grid.hasListener('rowclass');
35871  
35872                 var rowcfg = {};
35873                 for(var j = 0, len = rs.length; j < len; j++){
35874                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35875                     for(var i = 0; i < colCount; i++){
35876                         c = cs[i];
35877                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35878                         p.id = c.id;
35879                         p.css = p.attr = "";
35880                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35881                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35882                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35883                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35884                         }
35885                         
35886                         var markup = ct.apply(p);
35887                         if(!c.locked){
35888                             cb[cb.length] = markup;
35889                         }else{
35890                             lcb[lcb.length] = markup;
35891                         }
35892                     }
35893                     var alt = [];
35894                     if(stripe && ((rowIndex+1) % 2 == 0)){
35895                         alt.push( "x-grid-row-alt");
35896                     }
35897                     if(r.dirty){
35898                         alt.push(" x-grid-dirty-row");
35899                     }
35900                     rp.cells = lcb;
35901                     if(this.getRowClass){
35902                         alt.push( this.getRowClass(r, rowIndex));
35903                     }
35904                     if (hasListener) {
35905                         rowcfg = {
35906                              
35907                             record: r,
35908                             rowIndex : rowIndex,
35909                             rowClass : ''
35910                         }
35911                         this.grid.fireEvent('rowclass', this, rowcfg);
35912                         alt.push(rowcfg.rowClass);
35913                     }
35914                     rp.alt = alt.join(" ");
35915                     rp.cells = lcb.join("");
35916                     lbuf[lbuf.length] = rt.apply(rp);
35917                     rp.cells = cb.join("");
35918                     buf[buf.length] =  rt.apply(rp);
35919                 }
35920                 return [lbuf.join(""), buf.join("")];
35921             },
35922
35923     renderBody : function(){
35924         var markup = this.renderRows();
35925         var bt = this.templates.body;
35926         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35927     },
35928
35929     /**
35930      * Refreshes the grid
35931      * @param {Boolean} headersToo
35932      */
35933     refresh : function(headersToo){
35934         this.fireEvent("beforerefresh", this);
35935         this.grid.stopEditing();
35936         var result = this.renderBody();
35937         this.lockedBody.update(result[0]);
35938         this.mainBody.update(result[1]);
35939         if(headersToo === true){
35940             this.updateHeaders();
35941             this.updateColumns();
35942             this.updateSplitters();
35943             this.updateHeaderSortState();
35944         }
35945         this.syncRowHeights();
35946         this.layout();
35947         this.fireEvent("refresh", this);
35948     },
35949
35950     handleColumnMove : function(cm, oldIndex, newIndex){
35951         this.indexMap = null;
35952         var s = this.getScrollState();
35953         this.refresh(true);
35954         this.restoreScroll(s);
35955         this.afterMove(newIndex);
35956     },
35957
35958     afterMove : function(colIndex){
35959         if(this.enableMoveAnim && Roo.enableFx){
35960             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35961         }
35962         // if multisort - fix sortOrder, and reload..
35963         if (this.grid.dataSource.multiSort) {
35964             // the we can call sort again..
35965             var dm = this.grid.dataSource;
35966             var cm = this.grid.colModel;
35967             var so = [];
35968             for(var i = 0; i < cm.config.length; i++ ) {
35969                 
35970                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35971                     continue; // dont' bother, it's not in sort list or being set.
35972                 }
35973                 
35974                 so.push(cm.config[i].dataIndex);
35975             };
35976             dm.sortOrder = so;
35977             dm.load(dm.lastOptions);
35978             
35979             
35980         }
35981         
35982     },
35983
35984     updateCell : function(dm, rowIndex, dataIndex){
35985         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35986         if(typeof colIndex == "undefined"){ // not present in grid
35987             return;
35988         }
35989         var cm = this.grid.colModel;
35990         var cell = this.getCell(rowIndex, colIndex);
35991         var cellText = this.getCellText(rowIndex, colIndex);
35992
35993         var p = {
35994             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35995             id : cm.getColumnId(colIndex),
35996             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35997         };
35998         var renderer = cm.getRenderer(colIndex);
35999         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36000         if(typeof val == "undefined" || val === "") val = "&#160;";
36001         cellText.innerHTML = val;
36002         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36003         this.syncRowHeights(rowIndex, rowIndex);
36004     },
36005
36006     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36007         var maxWidth = 0;
36008         if(this.grid.autoSizeHeaders){
36009             var h = this.getHeaderCellMeasure(colIndex);
36010             maxWidth = Math.max(maxWidth, h.scrollWidth);
36011         }
36012         var tb, index;
36013         if(this.cm.isLocked(colIndex)){
36014             tb = this.getLockedTable();
36015             index = colIndex;
36016         }else{
36017             tb = this.getBodyTable();
36018             index = colIndex - this.cm.getLockedCount();
36019         }
36020         if(tb && tb.rows){
36021             var rows = tb.rows;
36022             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36023             for(var i = 0; i < stopIndex; i++){
36024                 var cell = rows[i].childNodes[index].firstChild;
36025                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36026             }
36027         }
36028         return maxWidth + /*margin for error in IE*/ 5;
36029     },
36030     /**
36031      * Autofit a column to its content.
36032      * @param {Number} colIndex
36033      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36034      */
36035      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36036          if(this.cm.isHidden(colIndex)){
36037              return; // can't calc a hidden column
36038          }
36039         if(forceMinSize){
36040             var cid = this.cm.getColumnId(colIndex);
36041             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36042            if(this.grid.autoSizeHeaders){
36043                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36044            }
36045         }
36046         var newWidth = this.calcColumnWidth(colIndex);
36047         this.cm.setColumnWidth(colIndex,
36048             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36049         if(!suppressEvent){
36050             this.grid.fireEvent("columnresize", colIndex, newWidth);
36051         }
36052     },
36053
36054     /**
36055      * Autofits all columns to their content and then expands to fit any extra space in the grid
36056      */
36057      autoSizeColumns : function(){
36058         var cm = this.grid.colModel;
36059         var colCount = cm.getColumnCount();
36060         for(var i = 0; i < colCount; i++){
36061             this.autoSizeColumn(i, true, true);
36062         }
36063         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36064             this.fitColumns();
36065         }else{
36066             this.updateColumns();
36067             this.layout();
36068         }
36069     },
36070
36071     /**
36072      * Autofits all columns to the grid's width proportionate with their current size
36073      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36074      */
36075     fitColumns : function(reserveScrollSpace){
36076         var cm = this.grid.colModel;
36077         var colCount = cm.getColumnCount();
36078         var cols = [];
36079         var width = 0;
36080         var i, w;
36081         for (i = 0; i < colCount; i++){
36082             if(!cm.isHidden(i) && !cm.isFixed(i)){
36083                 w = cm.getColumnWidth(i);
36084                 cols.push(i);
36085                 cols.push(w);
36086                 width += w;
36087             }
36088         }
36089         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36090         if(reserveScrollSpace){
36091             avail -= 17;
36092         }
36093         var frac = (avail - cm.getTotalWidth())/width;
36094         while (cols.length){
36095             w = cols.pop();
36096             i = cols.pop();
36097             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36098         }
36099         this.updateColumns();
36100         this.layout();
36101     },
36102
36103     onRowSelect : function(rowIndex){
36104         var row = this.getRowComposite(rowIndex);
36105         row.addClass("x-grid-row-selected");
36106     },
36107
36108     onRowDeselect : function(rowIndex){
36109         var row = this.getRowComposite(rowIndex);
36110         row.removeClass("x-grid-row-selected");
36111     },
36112
36113     onCellSelect : function(row, col){
36114         var cell = this.getCell(row, col);
36115         if(cell){
36116             Roo.fly(cell).addClass("x-grid-cell-selected");
36117         }
36118     },
36119
36120     onCellDeselect : function(row, col){
36121         var cell = this.getCell(row, col);
36122         if(cell){
36123             Roo.fly(cell).removeClass("x-grid-cell-selected");
36124         }
36125     },
36126
36127     updateHeaderSortState : function(){
36128         
36129         // sort state can be single { field: xxx, direction : yyy}
36130         // or   { xxx=>ASC , yyy : DESC ..... }
36131         
36132         var mstate = {};
36133         if (!this.ds.multiSort) { 
36134             var state = this.ds.getSortState();
36135             if(!state){
36136                 return;
36137             }
36138             mstate[state.field] = state.direction;
36139             // FIXME... - this is not used here.. but might be elsewhere..
36140             this.sortState = state;
36141             
36142         } else {
36143             mstate = this.ds.sortToggle;
36144         }
36145         //remove existing sort classes..
36146         
36147         var sc = this.sortClasses;
36148         var hds = this.el.select(this.headerSelector).removeClass(sc);
36149         
36150         for(var f in mstate) {
36151         
36152             var sortColumn = this.cm.findColumnIndex(f);
36153             
36154             if(sortColumn != -1){
36155                 var sortDir = mstate[f];        
36156                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36157             }
36158         }
36159         
36160          
36161         
36162     },
36163
36164
36165     handleHeaderClick : function(g, index){
36166         if(this.headersDisabled){
36167             return;
36168         }
36169         var dm = g.dataSource, cm = g.colModel;
36170         if(!cm.isSortable(index)){
36171             return;
36172         }
36173         g.stopEditing();
36174         
36175         if (dm.multiSort) {
36176             // update the sortOrder
36177             var so = [];
36178             for(var i = 0; i < cm.config.length; i++ ) {
36179                 
36180                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36181                     continue; // dont' bother, it's not in sort list or being set.
36182                 }
36183                 
36184                 so.push(cm.config[i].dataIndex);
36185             };
36186             dm.sortOrder = so;
36187         }
36188         
36189         
36190         dm.sort(cm.getDataIndex(index));
36191     },
36192
36193
36194     destroy : function(){
36195         if(this.colMenu){
36196             this.colMenu.removeAll();
36197             Roo.menu.MenuMgr.unregister(this.colMenu);
36198             this.colMenu.getEl().remove();
36199             delete this.colMenu;
36200         }
36201         if(this.hmenu){
36202             this.hmenu.removeAll();
36203             Roo.menu.MenuMgr.unregister(this.hmenu);
36204             this.hmenu.getEl().remove();
36205             delete this.hmenu;
36206         }
36207         if(this.grid.enableColumnMove){
36208             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36209             if(dds){
36210                 for(var dd in dds){
36211                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36212                         var elid = dds[dd].dragElId;
36213                         dds[dd].unreg();
36214                         Roo.get(elid).remove();
36215                     } else if(dds[dd].config.isTarget){
36216                         dds[dd].proxyTop.remove();
36217                         dds[dd].proxyBottom.remove();
36218                         dds[dd].unreg();
36219                     }
36220                     if(Roo.dd.DDM.locationCache[dd]){
36221                         delete Roo.dd.DDM.locationCache[dd];
36222                     }
36223                 }
36224                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36225             }
36226         }
36227         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36228         this.bind(null, null);
36229         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36230     },
36231
36232     handleLockChange : function(){
36233         this.refresh(true);
36234     },
36235
36236     onDenyColumnLock : function(){
36237
36238     },
36239
36240     onDenyColumnHide : function(){
36241
36242     },
36243
36244     handleHdMenuClick : function(item){
36245         var index = this.hdCtxIndex;
36246         var cm = this.cm, ds = this.ds;
36247         switch(item.id){
36248             case "asc":
36249                 ds.sort(cm.getDataIndex(index), "ASC");
36250                 break;
36251             case "desc":
36252                 ds.sort(cm.getDataIndex(index), "DESC");
36253                 break;
36254             case "lock":
36255                 var lc = cm.getLockedCount();
36256                 if(cm.getColumnCount(true) <= lc+1){
36257                     this.onDenyColumnLock();
36258                     return;
36259                 }
36260                 if(lc != index){
36261                     cm.setLocked(index, true, true);
36262                     cm.moveColumn(index, lc);
36263                     this.grid.fireEvent("columnmove", index, lc);
36264                 }else{
36265                     cm.setLocked(index, true);
36266                 }
36267             break;
36268             case "unlock":
36269                 var lc = cm.getLockedCount();
36270                 if((lc-1) != index){
36271                     cm.setLocked(index, false, true);
36272                     cm.moveColumn(index, lc-1);
36273                     this.grid.fireEvent("columnmove", index, lc-1);
36274                 }else{
36275                     cm.setLocked(index, false);
36276                 }
36277             break;
36278             default:
36279                 index = cm.getIndexById(item.id.substr(4));
36280                 if(index != -1){
36281                     if(item.checked && cm.getColumnCount(true) <= 1){
36282                         this.onDenyColumnHide();
36283                         return false;
36284                     }
36285                     cm.setHidden(index, item.checked);
36286                 }
36287         }
36288         return true;
36289     },
36290
36291     beforeColMenuShow : function(){
36292         var cm = this.cm,  colCount = cm.getColumnCount();
36293         this.colMenu.removeAll();
36294         for(var i = 0; i < colCount; i++){
36295             this.colMenu.add(new Roo.menu.CheckItem({
36296                 id: "col-"+cm.getColumnId(i),
36297                 text: cm.getColumnHeader(i),
36298                 checked: !cm.isHidden(i),
36299                 hideOnClick:false
36300             }));
36301         }
36302     },
36303
36304     handleHdCtx : function(g, index, e){
36305         e.stopEvent();
36306         var hd = this.getHeaderCell(index);
36307         this.hdCtxIndex = index;
36308         var ms = this.hmenu.items, cm = this.cm;
36309         ms.get("asc").setDisabled(!cm.isSortable(index));
36310         ms.get("desc").setDisabled(!cm.isSortable(index));
36311         if(this.grid.enableColLock !== false){
36312             ms.get("lock").setDisabled(cm.isLocked(index));
36313             ms.get("unlock").setDisabled(!cm.isLocked(index));
36314         }
36315         this.hmenu.show(hd, "tl-bl");
36316     },
36317
36318     handleHdOver : function(e){
36319         var hd = this.findHeaderCell(e.getTarget());
36320         if(hd && !this.headersDisabled){
36321             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36322                this.fly(hd).addClass("x-grid-hd-over");
36323             }
36324         }
36325     },
36326
36327     handleHdOut : function(e){
36328         var hd = this.findHeaderCell(e.getTarget());
36329         if(hd){
36330             this.fly(hd).removeClass("x-grid-hd-over");
36331         }
36332     },
36333
36334     handleSplitDblClick : function(e, t){
36335         var i = this.getCellIndex(t);
36336         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36337             this.autoSizeColumn(i, true);
36338             this.layout();
36339         }
36340     },
36341
36342     render : function(){
36343
36344         var cm = this.cm;
36345         var colCount = cm.getColumnCount();
36346
36347         if(this.grid.monitorWindowResize === true){
36348             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36349         }
36350         var header = this.renderHeaders();
36351         var body = this.templates.body.apply({rows:""});
36352         var html = this.templates.master.apply({
36353             lockedBody: body,
36354             body: body,
36355             lockedHeader: header[0],
36356             header: header[1]
36357         });
36358
36359         //this.updateColumns();
36360
36361         this.grid.getGridEl().dom.innerHTML = html;
36362
36363         this.initElements();
36364         
36365         // a kludge to fix the random scolling effect in webkit
36366         this.el.on("scroll", function() {
36367             this.el.dom.scrollTop=0; // hopefully not recursive..
36368         },this);
36369
36370         this.scroller.on("scroll", this.handleScroll, this);
36371         this.lockedBody.on("mousewheel", this.handleWheel, this);
36372         this.mainBody.on("mousewheel", this.handleWheel, this);
36373
36374         this.mainHd.on("mouseover", this.handleHdOver, this);
36375         this.mainHd.on("mouseout", this.handleHdOut, this);
36376         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36377                 {delegate: "."+this.splitClass});
36378
36379         this.lockedHd.on("mouseover", this.handleHdOver, this);
36380         this.lockedHd.on("mouseout", this.handleHdOut, this);
36381         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36382                 {delegate: "."+this.splitClass});
36383
36384         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36385             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36386         }
36387
36388         this.updateSplitters();
36389
36390         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36391             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36392             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36393         }
36394
36395         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36396             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36397             this.hmenu.add(
36398                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36399                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36400             );
36401             if(this.grid.enableColLock !== false){
36402                 this.hmenu.add('-',
36403                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36404                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36405                 );
36406             }
36407             if(this.grid.enableColumnHide !== false){
36408
36409                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36410                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36411                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36412
36413                 this.hmenu.add('-',
36414                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36415                 );
36416             }
36417             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36418
36419             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36420         }
36421
36422         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36423             this.dd = new Roo.grid.GridDragZone(this.grid, {
36424                 ddGroup : this.grid.ddGroup || 'GridDD'
36425             });
36426         }
36427
36428         /*
36429         for(var i = 0; i < colCount; i++){
36430             if(cm.isHidden(i)){
36431                 this.hideColumn(i);
36432             }
36433             if(cm.config[i].align){
36434                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36435                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36436             }
36437         }*/
36438         
36439         this.updateHeaderSortState();
36440
36441         this.beforeInitialResize();
36442         this.layout(true);
36443
36444         // two part rendering gives faster view to the user
36445         this.renderPhase2.defer(1, this);
36446     },
36447
36448     renderPhase2 : function(){
36449         // render the rows now
36450         this.refresh();
36451         if(this.grid.autoSizeColumns){
36452             this.autoSizeColumns();
36453         }
36454     },
36455
36456     beforeInitialResize : function(){
36457
36458     },
36459
36460     onColumnSplitterMoved : function(i, w){
36461         this.userResized = true;
36462         var cm = this.grid.colModel;
36463         cm.setColumnWidth(i, w, true);
36464         var cid = cm.getColumnId(i);
36465         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36466         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36467         this.updateSplitters();
36468         this.layout();
36469         this.grid.fireEvent("columnresize", i, w);
36470     },
36471
36472     syncRowHeights : function(startIndex, endIndex){
36473         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36474             startIndex = startIndex || 0;
36475             var mrows = this.getBodyTable().rows;
36476             var lrows = this.getLockedTable().rows;
36477             var len = mrows.length-1;
36478             endIndex = Math.min(endIndex || len, len);
36479             for(var i = startIndex; i <= endIndex; i++){
36480                 var m = mrows[i], l = lrows[i];
36481                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36482                 m.style.height = l.style.height = h + "px";
36483             }
36484         }
36485     },
36486
36487     layout : function(initialRender, is2ndPass){
36488         var g = this.grid;
36489         var auto = g.autoHeight;
36490         var scrollOffset = 16;
36491         var c = g.getGridEl(), cm = this.cm,
36492                 expandCol = g.autoExpandColumn,
36493                 gv = this;
36494         //c.beginMeasure();
36495
36496         if(!c.dom.offsetWidth){ // display:none?
36497             if(initialRender){
36498                 this.lockedWrap.show();
36499                 this.mainWrap.show();
36500             }
36501             return;
36502         }
36503
36504         var hasLock = this.cm.isLocked(0);
36505
36506         var tbh = this.headerPanel.getHeight();
36507         var bbh = this.footerPanel.getHeight();
36508
36509         if(auto){
36510             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36511             var newHeight = ch + c.getBorderWidth("tb");
36512             if(g.maxHeight){
36513                 newHeight = Math.min(g.maxHeight, newHeight);
36514             }
36515             c.setHeight(newHeight);
36516         }
36517
36518         if(g.autoWidth){
36519             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36520         }
36521
36522         var s = this.scroller;
36523
36524         var csize = c.getSize(true);
36525
36526         this.el.setSize(csize.width, csize.height);
36527
36528         this.headerPanel.setWidth(csize.width);
36529         this.footerPanel.setWidth(csize.width);
36530
36531         var hdHeight = this.mainHd.getHeight();
36532         var vw = csize.width;
36533         var vh = csize.height - (tbh + bbh);
36534
36535         s.setSize(vw, vh);
36536
36537         var bt = this.getBodyTable();
36538         var ltWidth = hasLock ?
36539                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36540
36541         var scrollHeight = bt.offsetHeight;
36542         var scrollWidth = ltWidth + bt.offsetWidth;
36543         var vscroll = false, hscroll = false;
36544
36545         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36546
36547         var lw = this.lockedWrap, mw = this.mainWrap;
36548         var lb = this.lockedBody, mb = this.mainBody;
36549
36550         setTimeout(function(){
36551             var t = s.dom.offsetTop;
36552             var w = s.dom.clientWidth,
36553                 h = s.dom.clientHeight;
36554
36555             lw.setTop(t);
36556             lw.setSize(ltWidth, h);
36557
36558             mw.setLeftTop(ltWidth, t);
36559             mw.setSize(w-ltWidth, h);
36560
36561             lb.setHeight(h-hdHeight);
36562             mb.setHeight(h-hdHeight);
36563
36564             if(is2ndPass !== true && !gv.userResized && expandCol){
36565                 // high speed resize without full column calculation
36566                 
36567                 var ci = cm.getIndexById(expandCol);
36568                 if (ci < 0) {
36569                     ci = cm.findColumnIndex(expandCol);
36570                 }
36571                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36572                 var expandId = cm.getColumnId(ci);
36573                 var  tw = cm.getTotalWidth(false);
36574                 var currentWidth = cm.getColumnWidth(ci);
36575                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36576                 if(currentWidth != cw){
36577                     cm.setColumnWidth(ci, cw, true);
36578                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36579                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36580                     gv.updateSplitters();
36581                     gv.layout(false, true);
36582                 }
36583             }
36584
36585             if(initialRender){
36586                 lw.show();
36587                 mw.show();
36588             }
36589             //c.endMeasure();
36590         }, 10);
36591     },
36592
36593     onWindowResize : function(){
36594         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36595             return;
36596         }
36597         this.layout();
36598     },
36599
36600     appendFooter : function(parentEl){
36601         return null;
36602     },
36603
36604     sortAscText : "Sort Ascending",
36605     sortDescText : "Sort Descending",
36606     lockText : "Lock Column",
36607     unlockText : "Unlock Column",
36608     columnsText : "Columns"
36609 });
36610
36611
36612 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36613     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36614     this.proxy.el.addClass('x-grid3-col-dd');
36615 };
36616
36617 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36618     handleMouseDown : function(e){
36619
36620     },
36621
36622     callHandleMouseDown : function(e){
36623         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36624     }
36625 });
36626 /*
36627  * Based on:
36628  * Ext JS Library 1.1.1
36629  * Copyright(c) 2006-2007, Ext JS, LLC.
36630  *
36631  * Originally Released Under LGPL - original licence link has changed is not relivant.
36632  *
36633  * Fork - LGPL
36634  * <script type="text/javascript">
36635  */
36636  
36637 // private
36638 // This is a support class used internally by the Grid components
36639 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36640     this.grid = grid;
36641     this.view = grid.getView();
36642     this.proxy = this.view.resizeProxy;
36643     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
36644         "gridSplitters" + this.grid.getGridEl().id, {
36645         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
36646     });
36647     this.setHandleElId(Roo.id(hd));
36648     this.setOuterHandleElId(Roo.id(hd2));
36649     this.scroll = false;
36650 };
36651 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36652     fly: Roo.Element.fly,
36653
36654     b4StartDrag : function(x, y){
36655         this.view.headersDisabled = true;
36656         this.proxy.setHeight(this.view.mainWrap.getHeight());
36657         var w = this.cm.getColumnWidth(this.cellIndex);
36658         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36659         this.resetConstraints();
36660         this.setXConstraint(minw, 1000);
36661         this.setYConstraint(0, 0);
36662         this.minX = x - minw;
36663         this.maxX = x + 1000;
36664         this.startPos = x;
36665         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36666     },
36667
36668
36669     handleMouseDown : function(e){
36670         ev = Roo.EventObject.setEvent(e);
36671         var t = this.fly(ev.getTarget());
36672         if(t.hasClass("x-grid-split")){
36673             this.cellIndex = this.view.getCellIndex(t.dom);
36674             this.split = t.dom;
36675             this.cm = this.grid.colModel;
36676             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36677                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36678             }
36679         }
36680     },
36681
36682     endDrag : function(e){
36683         this.view.headersDisabled = false;
36684         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36685         var diff = endX - this.startPos;
36686         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
36687     },
36688
36689     autoOffset : function(){
36690         this.setDelta(0,0);
36691     }
36692 });/*
36693  * Based on:
36694  * Ext JS Library 1.1.1
36695  * Copyright(c) 2006-2007, Ext JS, LLC.
36696  *
36697  * Originally Released Under LGPL - original licence link has changed is not relivant.
36698  *
36699  * Fork - LGPL
36700  * <script type="text/javascript">
36701  */
36702  
36703 // private
36704 // This is a support class used internally by the Grid components
36705 Roo.grid.GridDragZone = function(grid, config){
36706     this.view = grid.getView();
36707     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36708     if(this.view.lockedBody){
36709         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36710         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36711     }
36712     this.scroll = false;
36713     this.grid = grid;
36714     this.ddel = document.createElement('div');
36715     this.ddel.className = 'x-grid-dd-wrap';
36716 };
36717
36718 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36719     ddGroup : "GridDD",
36720
36721     getDragData : function(e){
36722         var t = Roo.lib.Event.getTarget(e);
36723         var rowIndex = this.view.findRowIndex(t);
36724         if(rowIndex !== false){
36725             var sm = this.grid.selModel;
36726             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36727               //  sm.mouseDown(e, t);
36728             //}
36729             if (e.hasModifier()){
36730                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36731             }
36732             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
36733         }
36734         return false;
36735     },
36736
36737     onInitDrag : function(e){
36738         var data = this.dragData;
36739         this.ddel.innerHTML = this.grid.getDragDropText();
36740         this.proxy.update(this.ddel);
36741         // fire start drag?
36742     },
36743
36744     afterRepair : function(){
36745         this.dragging = false;
36746     },
36747
36748     getRepairXY : function(e, data){
36749         return false;
36750     },
36751
36752     onEndDrag : function(data, e){
36753         // fire end drag?
36754     },
36755
36756     onValidDrop : function(dd, e, id){
36757         // fire drag drop?
36758         this.hideProxy();
36759     },
36760
36761     beforeInvalidDrop : function(e, id){
36762
36763     }
36764 });/*
36765  * Based on:
36766  * Ext JS Library 1.1.1
36767  * Copyright(c) 2006-2007, Ext JS, LLC.
36768  *
36769  * Originally Released Under LGPL - original licence link has changed is not relivant.
36770  *
36771  * Fork - LGPL
36772  * <script type="text/javascript">
36773  */
36774  
36775
36776 /**
36777  * @class Roo.grid.ColumnModel
36778  * @extends Roo.util.Observable
36779  * This is the default implementation of a ColumnModel used by the Grid. It defines
36780  * the columns in the grid.
36781  * <br>Usage:<br>
36782  <pre><code>
36783  var colModel = new Roo.grid.ColumnModel([
36784         {header: "Ticker", width: 60, sortable: true, locked: true},
36785         {header: "Company Name", width: 150, sortable: true},
36786         {header: "Market Cap.", width: 100, sortable: true},
36787         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36788         {header: "Employees", width: 100, sortable: true, resizable: false}
36789  ]);
36790  </code></pre>
36791  * <p>
36792  
36793  * The config options listed for this class are options which may appear in each
36794  * individual column definition.
36795  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36796  * @constructor
36797  * @param {Object} config An Array of column config objects. See this class's
36798  * config objects for details.
36799 */
36800 Roo.grid.ColumnModel = function(config){
36801         /**
36802      * The config passed into the constructor
36803      */
36804     this.config = config;
36805     this.lookup = {};
36806
36807     // if no id, create one
36808     // if the column does not have a dataIndex mapping,
36809     // map it to the order it is in the config
36810     for(var i = 0, len = config.length; i < len; i++){
36811         var c = config[i];
36812         if(typeof c.dataIndex == "undefined"){
36813             c.dataIndex = i;
36814         }
36815         if(typeof c.renderer == "string"){
36816             c.renderer = Roo.util.Format[c.renderer];
36817         }
36818         if(typeof c.id == "undefined"){
36819             c.id = Roo.id();
36820         }
36821         if(c.editor && c.editor.xtype){
36822             c.editor  = Roo.factory(c.editor, Roo.grid);
36823         }
36824         if(c.editor && c.editor.isFormField){
36825             c.editor = new Roo.grid.GridEditor(c.editor);
36826         }
36827         this.lookup[c.id] = c;
36828     }
36829
36830     /**
36831      * The width of columns which have no width specified (defaults to 100)
36832      * @type Number
36833      */
36834     this.defaultWidth = 100;
36835
36836     /**
36837      * Default sortable of columns which have no sortable specified (defaults to false)
36838      * @type Boolean
36839      */
36840     this.defaultSortable = false;
36841
36842     this.addEvents({
36843         /**
36844              * @event widthchange
36845              * Fires when the width of a column changes.
36846              * @param {ColumnModel} this
36847              * @param {Number} columnIndex The column index
36848              * @param {Number} newWidth The new width
36849              */
36850             "widthchange": true,
36851         /**
36852              * @event headerchange
36853              * Fires when the text of a header changes.
36854              * @param {ColumnModel} this
36855              * @param {Number} columnIndex The column index
36856              * @param {Number} newText The new header text
36857              */
36858             "headerchange": true,
36859         /**
36860              * @event hiddenchange
36861              * Fires when a column is hidden or "unhidden".
36862              * @param {ColumnModel} this
36863              * @param {Number} columnIndex The column index
36864              * @param {Boolean} hidden true if hidden, false otherwise
36865              */
36866             "hiddenchange": true,
36867             /**
36868          * @event columnmoved
36869          * Fires when a column is moved.
36870          * @param {ColumnModel} this
36871          * @param {Number} oldIndex
36872          * @param {Number} newIndex
36873          */
36874         "columnmoved" : true,
36875         /**
36876          * @event columlockchange
36877          * Fires when a column's locked state is changed
36878          * @param {ColumnModel} this
36879          * @param {Number} colIndex
36880          * @param {Boolean} locked true if locked
36881          */
36882         "columnlockchange" : true
36883     });
36884     Roo.grid.ColumnModel.superclass.constructor.call(this);
36885 };
36886 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36887     /**
36888      * @cfg {String} header The header text to display in the Grid view.
36889      */
36890     /**
36891      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36892      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36893      * specified, the column's index is used as an index into the Record's data Array.
36894      */
36895     /**
36896      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36897      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36898      */
36899     /**
36900      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36901      * Defaults to the value of the {@link #defaultSortable} property.
36902      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36903      */
36904     /**
36905      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36906      */
36907     /**
36908      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36909      */
36910     /**
36911      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36912      */
36913     /**
36914      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36915      */
36916     /**
36917      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36918      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36919      * default renderer uses the raw data value.
36920      */
36921        /**
36922      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36923      */
36924     /**
36925      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36926      */
36927
36928     /**
36929      * Returns the id of the column at the specified index.
36930      * @param {Number} index The column index
36931      * @return {String} the id
36932      */
36933     getColumnId : function(index){
36934         return this.config[index].id;
36935     },
36936
36937     /**
36938      * Returns the column for a specified id.
36939      * @param {String} id The column id
36940      * @return {Object} the column
36941      */
36942     getColumnById : function(id){
36943         return this.lookup[id];
36944     },
36945
36946     
36947     /**
36948      * Returns the column for a specified dataIndex.
36949      * @param {String} dataIndex The column dataIndex
36950      * @return {Object|Boolean} the column or false if not found
36951      */
36952     getColumnByDataIndex: function(dataIndex){
36953         var index = this.findColumnIndex(dataIndex);
36954         return index > -1 ? this.config[index] : false;
36955     },
36956     
36957     /**
36958      * Returns the index for a specified column id.
36959      * @param {String} id The column id
36960      * @return {Number} the index, or -1 if not found
36961      */
36962     getIndexById : function(id){
36963         for(var i = 0, len = this.config.length; i < len; i++){
36964             if(this.config[i].id == id){
36965                 return i;
36966             }
36967         }
36968         return -1;
36969     },
36970     
36971     /**
36972      * Returns the index for a specified column dataIndex.
36973      * @param {String} dataIndex The column dataIndex
36974      * @return {Number} the index, or -1 if not found
36975      */
36976     
36977     findColumnIndex : function(dataIndex){
36978         for(var i = 0, len = this.config.length; i < len; i++){
36979             if(this.config[i].dataIndex == dataIndex){
36980                 return i;
36981             }
36982         }
36983         return -1;
36984     },
36985     
36986     
36987     moveColumn : function(oldIndex, newIndex){
36988         var c = this.config[oldIndex];
36989         this.config.splice(oldIndex, 1);
36990         this.config.splice(newIndex, 0, c);
36991         this.dataMap = null;
36992         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36993     },
36994
36995     isLocked : function(colIndex){
36996         return this.config[colIndex].locked === true;
36997     },
36998
36999     setLocked : function(colIndex, value, suppressEvent){
37000         if(this.isLocked(colIndex) == value){
37001             return;
37002         }
37003         this.config[colIndex].locked = value;
37004         if(!suppressEvent){
37005             this.fireEvent("columnlockchange", this, colIndex, value);
37006         }
37007     },
37008
37009     getTotalLockedWidth : function(){
37010         var totalWidth = 0;
37011         for(var i = 0; i < this.config.length; i++){
37012             if(this.isLocked(i) && !this.isHidden(i)){
37013                 this.totalWidth += this.getColumnWidth(i);
37014             }
37015         }
37016         return totalWidth;
37017     },
37018
37019     getLockedCount : function(){
37020         for(var i = 0, len = this.config.length; i < len; i++){
37021             if(!this.isLocked(i)){
37022                 return i;
37023             }
37024         }
37025     },
37026
37027     /**
37028      * Returns the number of columns.
37029      * @return {Number}
37030      */
37031     getColumnCount : function(visibleOnly){
37032         if(visibleOnly === true){
37033             var c = 0;
37034             for(var i = 0, len = this.config.length; i < len; i++){
37035                 if(!this.isHidden(i)){
37036                     c++;
37037                 }
37038             }
37039             return c;
37040         }
37041         return this.config.length;
37042     },
37043
37044     /**
37045      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37046      * @param {Function} fn
37047      * @param {Object} scope (optional)
37048      * @return {Array} result
37049      */
37050     getColumnsBy : function(fn, scope){
37051         var r = [];
37052         for(var i = 0, len = this.config.length; i < len; i++){
37053             var c = this.config[i];
37054             if(fn.call(scope||this, c, i) === true){
37055                 r[r.length] = c;
37056             }
37057         }
37058         return r;
37059     },
37060
37061     /**
37062      * Returns true if the specified column is sortable.
37063      * @param {Number} col The column index
37064      * @return {Boolean}
37065      */
37066     isSortable : function(col){
37067         if(typeof this.config[col].sortable == "undefined"){
37068             return this.defaultSortable;
37069         }
37070         return this.config[col].sortable;
37071     },
37072
37073     /**
37074      * Returns the rendering (formatting) function defined for the column.
37075      * @param {Number} col The column index.
37076      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37077      */
37078     getRenderer : function(col){
37079         if(!this.config[col].renderer){
37080             return Roo.grid.ColumnModel.defaultRenderer;
37081         }
37082         return this.config[col].renderer;
37083     },
37084
37085     /**
37086      * Sets the rendering (formatting) function for a column.
37087      * @param {Number} col The column index
37088      * @param {Function} fn The function to use to process the cell's raw data
37089      * to return HTML markup for the grid view. The render function is called with
37090      * the following parameters:<ul>
37091      * <li>Data value.</li>
37092      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37093      * <li>css A CSS style string to apply to the table cell.</li>
37094      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37095      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37096      * <li>Row index</li>
37097      * <li>Column index</li>
37098      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37099      */
37100     setRenderer : function(col, fn){
37101         this.config[col].renderer = fn;
37102     },
37103
37104     /**
37105      * Returns the width for the specified column.
37106      * @param {Number} col The column index
37107      * @return {Number}
37108      */
37109     getColumnWidth : function(col){
37110         return this.config[col].width * 1 || this.defaultWidth;
37111     },
37112
37113     /**
37114      * Sets the width for a column.
37115      * @param {Number} col The column index
37116      * @param {Number} width The new width
37117      */
37118     setColumnWidth : function(col, width, suppressEvent){
37119         this.config[col].width = width;
37120         this.totalWidth = null;
37121         if(!suppressEvent){
37122              this.fireEvent("widthchange", this, col, width);
37123         }
37124     },
37125
37126     /**
37127      * Returns the total width of all columns.
37128      * @param {Boolean} includeHidden True to include hidden column widths
37129      * @return {Number}
37130      */
37131     getTotalWidth : function(includeHidden){
37132         if(!this.totalWidth){
37133             this.totalWidth = 0;
37134             for(var i = 0, len = this.config.length; i < len; i++){
37135                 if(includeHidden || !this.isHidden(i)){
37136                     this.totalWidth += this.getColumnWidth(i);
37137                 }
37138             }
37139         }
37140         return this.totalWidth;
37141     },
37142
37143     /**
37144      * Returns the header for the specified column.
37145      * @param {Number} col The column index
37146      * @return {String}
37147      */
37148     getColumnHeader : function(col){
37149         return this.config[col].header;
37150     },
37151
37152     /**
37153      * Sets the header for a column.
37154      * @param {Number} col The column index
37155      * @param {String} header The new header
37156      */
37157     setColumnHeader : function(col, header){
37158         this.config[col].header = header;
37159         this.fireEvent("headerchange", this, col, header);
37160     },
37161
37162     /**
37163      * Returns the tooltip for the specified column.
37164      * @param {Number} col The column index
37165      * @return {String}
37166      */
37167     getColumnTooltip : function(col){
37168             return this.config[col].tooltip;
37169     },
37170     /**
37171      * Sets the tooltip for a column.
37172      * @param {Number} col The column index
37173      * @param {String} tooltip The new tooltip
37174      */
37175     setColumnTooltip : function(col, tooltip){
37176             this.config[col].tooltip = tooltip;
37177     },
37178
37179     /**
37180      * Returns the dataIndex for the specified column.
37181      * @param {Number} col The column index
37182      * @return {Number}
37183      */
37184     getDataIndex : function(col){
37185         return this.config[col].dataIndex;
37186     },
37187
37188     /**
37189      * Sets the dataIndex for a column.
37190      * @param {Number} col The column index
37191      * @param {Number} dataIndex The new dataIndex
37192      */
37193     setDataIndex : function(col, dataIndex){
37194         this.config[col].dataIndex = dataIndex;
37195     },
37196
37197     
37198     
37199     /**
37200      * Returns true if the cell is editable.
37201      * @param {Number} colIndex The column index
37202      * @param {Number} rowIndex The row index
37203      * @return {Boolean}
37204      */
37205     isCellEditable : function(colIndex, rowIndex){
37206         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
37207     },
37208
37209     /**
37210      * Returns the editor defined for the cell/column.
37211      * return false or null to disable editing.
37212      * @param {Number} colIndex The column index
37213      * @param {Number} rowIndex The row index
37214      * @return {Object}
37215      */
37216     getCellEditor : function(colIndex, rowIndex){
37217         return this.config[colIndex].editor;
37218     },
37219
37220     /**
37221      * Sets if a column is editable.
37222      * @param {Number} col The column index
37223      * @param {Boolean} editable True if the column is editable
37224      */
37225     setEditable : function(col, editable){
37226         this.config[col].editable = editable;
37227     },
37228
37229
37230     /**
37231      * Returns true if the column is hidden.
37232      * @param {Number} colIndex The column index
37233      * @return {Boolean}
37234      */
37235     isHidden : function(colIndex){
37236         return this.config[colIndex].hidden;
37237     },
37238
37239
37240     /**
37241      * Returns true if the column width cannot be changed
37242      */
37243     isFixed : function(colIndex){
37244         return this.config[colIndex].fixed;
37245     },
37246
37247     /**
37248      * Returns true if the column can be resized
37249      * @return {Boolean}
37250      */
37251     isResizable : function(colIndex){
37252         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
37253     },
37254     /**
37255      * Sets if a column is hidden.
37256      * @param {Number} colIndex The column index
37257      * @param {Boolean} hidden True if the column is hidden
37258      */
37259     setHidden : function(colIndex, hidden){
37260         this.config[colIndex].hidden = hidden;
37261         this.totalWidth = null;
37262         this.fireEvent("hiddenchange", this, colIndex, hidden);
37263     },
37264
37265     /**
37266      * Sets the editor for a column.
37267      * @param {Number} col The column index
37268      * @param {Object} editor The editor object
37269      */
37270     setEditor : function(col, editor){
37271         this.config[col].editor = editor;
37272     }
37273 });
37274
37275 Roo.grid.ColumnModel.defaultRenderer = function(value){
37276         if(typeof value == "string" && value.length < 1){
37277             return "&#160;";
37278         }
37279         return value;
37280 };
37281
37282 // Alias for backwards compatibility
37283 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
37284 /*
37285  * Based on:
37286  * Ext JS Library 1.1.1
37287  * Copyright(c) 2006-2007, Ext JS, LLC.
37288  *
37289  * Originally Released Under LGPL - original licence link has changed is not relivant.
37290  *
37291  * Fork - LGPL
37292  * <script type="text/javascript">
37293  */
37294
37295 /**
37296  * @class Roo.grid.AbstractSelectionModel
37297  * @extends Roo.util.Observable
37298  * Abstract base class for grid SelectionModels.  It provides the interface that should be
37299  * implemented by descendant classes.  This class should not be directly instantiated.
37300  * @constructor
37301  */
37302 Roo.grid.AbstractSelectionModel = function(){
37303     this.locked = false;
37304     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
37305 };
37306
37307 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
37308     /** @ignore Called by the grid automatically. Do not call directly. */
37309     init : function(grid){
37310         this.grid = grid;
37311         this.initEvents();
37312     },
37313
37314     /**
37315      * Locks the selections.
37316      */
37317     lock : function(){
37318         this.locked = true;
37319     },
37320
37321     /**
37322      * Unlocks the selections.
37323      */
37324     unlock : function(){
37325         this.locked = false;
37326     },
37327
37328     /**
37329      * Returns true if the selections are locked.
37330      * @return {Boolean}
37331      */
37332     isLocked : function(){
37333         return this.locked;
37334     }
37335 });/*
37336  * Based on:
37337  * Ext JS Library 1.1.1
37338  * Copyright(c) 2006-2007, Ext JS, LLC.
37339  *
37340  * Originally Released Under LGPL - original licence link has changed is not relivant.
37341  *
37342  * Fork - LGPL
37343  * <script type="text/javascript">
37344  */
37345 /**
37346  * @extends Roo.grid.AbstractSelectionModel
37347  * @class Roo.grid.RowSelectionModel
37348  * The default SelectionModel used by {@link Roo.grid.Grid}.
37349  * It supports multiple selections and keyboard selection/navigation. 
37350  * @constructor
37351  * @param {Object} config
37352  */
37353 Roo.grid.RowSelectionModel = function(config){
37354     Roo.apply(this, config);
37355     this.selections = new Roo.util.MixedCollection(false, function(o){
37356         return o.id;
37357     });
37358
37359     this.last = false;
37360     this.lastActive = false;
37361
37362     this.addEvents({
37363         /**
37364              * @event selectionchange
37365              * Fires when the selection changes
37366              * @param {SelectionModel} this
37367              */
37368             "selectionchange" : true,
37369         /**
37370              * @event afterselectionchange
37371              * Fires after the selection changes (eg. by key press or clicking)
37372              * @param {SelectionModel} this
37373              */
37374             "afterselectionchange" : true,
37375         /**
37376              * @event beforerowselect
37377              * Fires when a row is selected being selected, return false to cancel.
37378              * @param {SelectionModel} this
37379              * @param {Number} rowIndex The selected index
37380              * @param {Boolean} keepExisting False if other selections will be cleared
37381              */
37382             "beforerowselect" : true,
37383         /**
37384              * @event rowselect
37385              * Fires when a row is selected.
37386              * @param {SelectionModel} this
37387              * @param {Number} rowIndex The selected index
37388              * @param {Roo.data.Record} r The record
37389              */
37390             "rowselect" : true,
37391         /**
37392              * @event rowdeselect
37393              * Fires when a row is deselected.
37394              * @param {SelectionModel} this
37395              * @param {Number} rowIndex The selected index
37396              */
37397         "rowdeselect" : true
37398     });
37399     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37400     this.locked = false;
37401 };
37402
37403 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
37404     /**
37405      * @cfg {Boolean} singleSelect
37406      * True to allow selection of only one row at a time (defaults to false)
37407      */
37408     singleSelect : false,
37409
37410     // private
37411     initEvents : function(){
37412
37413         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37414             this.grid.on("mousedown", this.handleMouseDown, this);
37415         }else{ // allow click to work like normal
37416             this.grid.on("rowclick", this.handleDragableRowClick, this);
37417         }
37418
37419         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37420             "up" : function(e){
37421                 if(!e.shiftKey){
37422                     this.selectPrevious(e.shiftKey);
37423                 }else if(this.last !== false && this.lastActive !== false){
37424                     var last = this.last;
37425                     this.selectRange(this.last,  this.lastActive-1);
37426                     this.grid.getView().focusRow(this.lastActive);
37427                     if(last !== false){
37428                         this.last = last;
37429                     }
37430                 }else{
37431                     this.selectFirstRow();
37432                 }
37433                 this.fireEvent("afterselectionchange", this);
37434             },
37435             "down" : function(e){
37436                 if(!e.shiftKey){
37437                     this.selectNext(e.shiftKey);
37438                 }else if(this.last !== false && this.lastActive !== false){
37439                     var last = this.last;
37440                     this.selectRange(this.last,  this.lastActive+1);
37441                     this.grid.getView().focusRow(this.lastActive);
37442                     if(last !== false){
37443                         this.last = last;
37444                     }
37445                 }else{
37446                     this.selectFirstRow();
37447                 }
37448                 this.fireEvent("afterselectionchange", this);
37449             },
37450             scope: this
37451         });
37452
37453         var view = this.grid.view;
37454         view.on("refresh", this.onRefresh, this);
37455         view.on("rowupdated", this.onRowUpdated, this);
37456         view.on("rowremoved", this.onRemove, this);
37457     },
37458
37459     // private
37460     onRefresh : function(){
37461         var ds = this.grid.dataSource, i, v = this.grid.view;
37462         var s = this.selections;
37463         s.each(function(r){
37464             if((i = ds.indexOfId(r.id)) != -1){
37465                 v.onRowSelect(i);
37466             }else{
37467                 s.remove(r);
37468             }
37469         });
37470     },
37471
37472     // private
37473     onRemove : function(v, index, r){
37474         this.selections.remove(r);
37475     },
37476
37477     // private
37478     onRowUpdated : function(v, index, r){
37479         if(this.isSelected(r)){
37480             v.onRowSelect(index);
37481         }
37482     },
37483
37484     /**
37485      * Select records.
37486      * @param {Array} records The records to select
37487      * @param {Boolean} keepExisting (optional) True to keep existing selections
37488      */
37489     selectRecords : function(records, keepExisting){
37490         if(!keepExisting){
37491             this.clearSelections();
37492         }
37493         var ds = this.grid.dataSource;
37494         for(var i = 0, len = records.length; i < len; i++){
37495             this.selectRow(ds.indexOf(records[i]), true);
37496         }
37497     },
37498
37499     /**
37500      * Gets the number of selected rows.
37501      * @return {Number}
37502      */
37503     getCount : function(){
37504         return this.selections.length;
37505     },
37506
37507     /**
37508      * Selects the first row in the grid.
37509      */
37510     selectFirstRow : function(){
37511         this.selectRow(0);
37512     },
37513
37514     /**
37515      * Select the last row.
37516      * @param {Boolean} keepExisting (optional) True to keep existing selections
37517      */
37518     selectLastRow : function(keepExisting){
37519         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
37520     },
37521
37522     /**
37523      * Selects the row immediately following the last selected row.
37524      * @param {Boolean} keepExisting (optional) True to keep existing selections
37525      */
37526     selectNext : function(keepExisting){
37527         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
37528             this.selectRow(this.last+1, keepExisting);
37529             this.grid.getView().focusRow(this.last);
37530         }
37531     },
37532
37533     /**
37534      * Selects the row that precedes the last selected row.
37535      * @param {Boolean} keepExisting (optional) True to keep existing selections
37536      */
37537     selectPrevious : function(keepExisting){
37538         if(this.last){
37539             this.selectRow(this.last-1, keepExisting);
37540             this.grid.getView().focusRow(this.last);
37541         }
37542     },
37543
37544     /**
37545      * Returns the selected records
37546      * @return {Array} Array of selected records
37547      */
37548     getSelections : function(){
37549         return [].concat(this.selections.items);
37550     },
37551
37552     /**
37553      * Returns the first selected record.
37554      * @return {Record}
37555      */
37556     getSelected : function(){
37557         return this.selections.itemAt(0);
37558     },
37559
37560
37561     /**
37562      * Clears all selections.
37563      */
37564     clearSelections : function(fast){
37565         if(this.locked) return;
37566         if(fast !== true){
37567             var ds = this.grid.dataSource;
37568             var s = this.selections;
37569             s.each(function(r){
37570                 this.deselectRow(ds.indexOfId(r.id));
37571             }, this);
37572             s.clear();
37573         }else{
37574             this.selections.clear();
37575         }
37576         this.last = false;
37577     },
37578
37579
37580     /**
37581      * Selects all rows.
37582      */
37583     selectAll : function(){
37584         if(this.locked) return;
37585         this.selections.clear();
37586         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
37587             this.selectRow(i, true);
37588         }
37589     },
37590
37591     /**
37592      * Returns True if there is a selection.
37593      * @return {Boolean}
37594      */
37595     hasSelection : function(){
37596         return this.selections.length > 0;
37597     },
37598
37599     /**
37600      * Returns True if the specified row is selected.
37601      * @param {Number/Record} record The record or index of the record to check
37602      * @return {Boolean}
37603      */
37604     isSelected : function(index){
37605         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
37606         return (r && this.selections.key(r.id) ? true : false);
37607     },
37608
37609     /**
37610      * Returns True if the specified record id is selected.
37611      * @param {String} id The id of record to check
37612      * @return {Boolean}
37613      */
37614     isIdSelected : function(id){
37615         return (this.selections.key(id) ? true : false);
37616     },
37617
37618     // private
37619     handleMouseDown : function(e, t){
37620         var view = this.grid.getView(), rowIndex;
37621         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37622             return;
37623         };
37624         if(e.shiftKey && this.last !== false){
37625             var last = this.last;
37626             this.selectRange(last, rowIndex, e.ctrlKey);
37627             this.last = last; // reset the last
37628             view.focusRow(rowIndex);
37629         }else{
37630             var isSelected = this.isSelected(rowIndex);
37631             if(e.button !== 0 && isSelected){
37632                 view.focusRow(rowIndex);
37633             }else if(e.ctrlKey && isSelected){
37634                 this.deselectRow(rowIndex);
37635             }else if(!isSelected){
37636                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37637                 view.focusRow(rowIndex);
37638             }
37639         }
37640         this.fireEvent("afterselectionchange", this);
37641     },
37642     // private
37643     handleDragableRowClick :  function(grid, rowIndex, e) 
37644     {
37645         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37646             this.selectRow(rowIndex, false);
37647             grid.view.focusRow(rowIndex);
37648              this.fireEvent("afterselectionchange", this);
37649         }
37650     },
37651     
37652     /**
37653      * Selects multiple rows.
37654      * @param {Array} rows Array of the indexes of the row to select
37655      * @param {Boolean} keepExisting (optional) True to keep existing selections
37656      */
37657     selectRows : function(rows, keepExisting){
37658         if(!keepExisting){
37659             this.clearSelections();
37660         }
37661         for(var i = 0, len = rows.length; i < len; i++){
37662             this.selectRow(rows[i], true);
37663         }
37664     },
37665
37666     /**
37667      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37668      * @param {Number} startRow The index of the first row in the range
37669      * @param {Number} endRow The index of the last row in the range
37670      * @param {Boolean} keepExisting (optional) True to retain existing selections
37671      */
37672     selectRange : function(startRow, endRow, keepExisting){
37673         if(this.locked) return;
37674         if(!keepExisting){
37675             this.clearSelections();
37676         }
37677         if(startRow <= endRow){
37678             for(var i = startRow; i <= endRow; i++){
37679                 this.selectRow(i, true);
37680             }
37681         }else{
37682             for(var i = startRow; i >= endRow; i--){
37683                 this.selectRow(i, true);
37684             }
37685         }
37686     },
37687
37688     /**
37689      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37690      * @param {Number} startRow The index of the first row in the range
37691      * @param {Number} endRow The index of the last row in the range
37692      */
37693     deselectRange : function(startRow, endRow, preventViewNotify){
37694         if(this.locked) return;
37695         for(var i = startRow; i <= endRow; i++){
37696             this.deselectRow(i, preventViewNotify);
37697         }
37698     },
37699
37700     /**
37701      * Selects a row.
37702      * @param {Number} row The index of the row to select
37703      * @param {Boolean} keepExisting (optional) True to keep existing selections
37704      */
37705     selectRow : function(index, keepExisting, preventViewNotify){
37706         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
37707         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37708             if(!keepExisting || this.singleSelect){
37709                 this.clearSelections();
37710             }
37711             var r = this.grid.dataSource.getAt(index);
37712             this.selections.add(r);
37713             this.last = this.lastActive = index;
37714             if(!preventViewNotify){
37715                 this.grid.getView().onRowSelect(index);
37716             }
37717             this.fireEvent("rowselect", this, index, r);
37718             this.fireEvent("selectionchange", this);
37719         }
37720     },
37721
37722     /**
37723      * Deselects a row.
37724      * @param {Number} row The index of the row to deselect
37725      */
37726     deselectRow : function(index, preventViewNotify){
37727         if(this.locked) return;
37728         if(this.last == index){
37729             this.last = false;
37730         }
37731         if(this.lastActive == index){
37732             this.lastActive = false;
37733         }
37734         var r = this.grid.dataSource.getAt(index);
37735         this.selections.remove(r);
37736         if(!preventViewNotify){
37737             this.grid.getView().onRowDeselect(index);
37738         }
37739         this.fireEvent("rowdeselect", this, index);
37740         this.fireEvent("selectionchange", this);
37741     },
37742
37743     // private
37744     restoreLast : function(){
37745         if(this._last){
37746             this.last = this._last;
37747         }
37748     },
37749
37750     // private
37751     acceptsNav : function(row, col, cm){
37752         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37753     },
37754
37755     // private
37756     onEditorKey : function(field, e){
37757         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37758         if(k == e.TAB){
37759             e.stopEvent();
37760             ed.completeEdit();
37761             if(e.shiftKey){
37762                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37763             }else{
37764                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37765             }
37766         }else if(k == e.ENTER && !e.ctrlKey){
37767             e.stopEvent();
37768             ed.completeEdit();
37769             if(e.shiftKey){
37770                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37771             }else{
37772                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37773             }
37774         }else if(k == e.ESC){
37775             ed.cancelEdit();
37776         }
37777         if(newCell){
37778             g.startEditing(newCell[0], newCell[1]);
37779         }
37780     }
37781 });/*
37782  * Based on:
37783  * Ext JS Library 1.1.1
37784  * Copyright(c) 2006-2007, Ext JS, LLC.
37785  *
37786  * Originally Released Under LGPL - original licence link has changed is not relivant.
37787  *
37788  * Fork - LGPL
37789  * <script type="text/javascript">
37790  */
37791 /**
37792  * @class Roo.grid.CellSelectionModel
37793  * @extends Roo.grid.AbstractSelectionModel
37794  * This class provides the basic implementation for cell selection in a grid.
37795  * @constructor
37796  * @param {Object} config The object containing the configuration of this model.
37797  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37798  */
37799 Roo.grid.CellSelectionModel = function(config){
37800     Roo.apply(this, config);
37801
37802     this.selection = null;
37803
37804     this.addEvents({
37805         /**
37806              * @event beforerowselect
37807              * Fires before a cell is selected.
37808              * @param {SelectionModel} this
37809              * @param {Number} rowIndex The selected row index
37810              * @param {Number} colIndex The selected cell index
37811              */
37812             "beforecellselect" : true,
37813         /**
37814              * @event cellselect
37815              * Fires when a cell is selected.
37816              * @param {SelectionModel} this
37817              * @param {Number} rowIndex The selected row index
37818              * @param {Number} colIndex The selected cell index
37819              */
37820             "cellselect" : true,
37821         /**
37822              * @event selectionchange
37823              * Fires when the active selection changes.
37824              * @param {SelectionModel} this
37825              * @param {Object} selection null for no selection or an object (o) with two properties
37826                 <ul>
37827                 <li>o.record: the record object for the row the selection is in</li>
37828                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37829                 </ul>
37830              */
37831             "selectionchange" : true,
37832         /**
37833              * @event tabend
37834              * Fires when the tab (or enter) was pressed on the last editable cell
37835              * You can use this to trigger add new row.
37836              * @param {SelectionModel} this
37837              */
37838             "tabend" : true,
37839          /**
37840              * @event beforeeditnext
37841              * Fires before the next editable sell is made active
37842              * You can use this to skip to another cell or fire the tabend
37843              *    if you set cell to false
37844              * @param {Object} eventdata object : { cell : [ row, col ] } 
37845              */
37846             "beforeeditnext" : true
37847     });
37848     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37849 };
37850
37851 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37852     
37853     enter_is_tab: false,
37854
37855     /** @ignore */
37856     initEvents : function(){
37857         this.grid.on("mousedown", this.handleMouseDown, this);
37858         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37859         var view = this.grid.view;
37860         view.on("refresh", this.onViewChange, this);
37861         view.on("rowupdated", this.onRowUpdated, this);
37862         view.on("beforerowremoved", this.clearSelections, this);
37863         view.on("beforerowsinserted", this.clearSelections, this);
37864         if(this.grid.isEditor){
37865             this.grid.on("beforeedit", this.beforeEdit,  this);
37866         }
37867     },
37868
37869         //private
37870     beforeEdit : function(e){
37871         this.select(e.row, e.column, false, true, e.record);
37872     },
37873
37874         //private
37875     onRowUpdated : function(v, index, r){
37876         if(this.selection && this.selection.record == r){
37877             v.onCellSelect(index, this.selection.cell[1]);
37878         }
37879     },
37880
37881         //private
37882     onViewChange : function(){
37883         this.clearSelections(true);
37884     },
37885
37886         /**
37887          * Returns the currently selected cell,.
37888          * @return {Array} The selected cell (row, column) or null if none selected.
37889          */
37890     getSelectedCell : function(){
37891         return this.selection ? this.selection.cell : null;
37892     },
37893
37894     /**
37895      * Clears all selections.
37896      * @param {Boolean} true to prevent the gridview from being notified about the change.
37897      */
37898     clearSelections : function(preventNotify){
37899         var s = this.selection;
37900         if(s){
37901             if(preventNotify !== true){
37902                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37903             }
37904             this.selection = null;
37905             this.fireEvent("selectionchange", this, null);
37906         }
37907     },
37908
37909     /**
37910      * Returns true if there is a selection.
37911      * @return {Boolean}
37912      */
37913     hasSelection : function(){
37914         return this.selection ? true : false;
37915     },
37916
37917     /** @ignore */
37918     handleMouseDown : function(e, t){
37919         var v = this.grid.getView();
37920         if(this.isLocked()){
37921             return;
37922         };
37923         var row = v.findRowIndex(t);
37924         var cell = v.findCellIndex(t);
37925         if(row !== false && cell !== false){
37926             this.select(row, cell);
37927         }
37928     },
37929
37930     /**
37931      * Selects a cell.
37932      * @param {Number} rowIndex
37933      * @param {Number} collIndex
37934      */
37935     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37936         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37937             this.clearSelections();
37938             r = r || this.grid.dataSource.getAt(rowIndex);
37939             this.selection = {
37940                 record : r,
37941                 cell : [rowIndex, colIndex]
37942             };
37943             if(!preventViewNotify){
37944                 var v = this.grid.getView();
37945                 v.onCellSelect(rowIndex, colIndex);
37946                 if(preventFocus !== true){
37947                     v.focusCell(rowIndex, colIndex);
37948                 }
37949             }
37950             this.fireEvent("cellselect", this, rowIndex, colIndex);
37951             this.fireEvent("selectionchange", this, this.selection);
37952         }
37953     },
37954
37955         //private
37956     isSelectable : function(rowIndex, colIndex, cm){
37957         return !cm.isHidden(colIndex);
37958     },
37959
37960     /** @ignore */
37961     handleKeyDown : function(e){
37962         //Roo.log('Cell Sel Model handleKeyDown');
37963         if(!e.isNavKeyPress()){
37964             return;
37965         }
37966         var g = this.grid, s = this.selection;
37967         if(!s){
37968             e.stopEvent();
37969             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
37970             if(cell){
37971                 this.select(cell[0], cell[1]);
37972             }
37973             return;
37974         }
37975         var sm = this;
37976         var walk = function(row, col, step){
37977             return g.walkCells(row, col, step, sm.isSelectable,  sm);
37978         };
37979         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37980         var newCell;
37981
37982       
37983
37984         switch(k){
37985             case e.TAB:
37986                 // handled by onEditorKey
37987                 if (g.isEditor && g.editing) {
37988                     return;
37989                 }
37990                 if(e.shiftKey) {
37991                     newCell = walk(r, c-1, -1);
37992                 } else {
37993                     newCell = walk(r, c+1, 1);
37994                 }
37995                 break;
37996             
37997             case e.DOWN:
37998                newCell = walk(r+1, c, 1);
37999                 break;
38000             
38001             case e.UP:
38002                 newCell = walk(r-1, c, -1);
38003                 break;
38004             
38005             case e.RIGHT:
38006                 newCell = walk(r, c+1, 1);
38007                 break;
38008             
38009             case e.LEFT:
38010                 newCell = walk(r, c-1, -1);
38011                 break;
38012             
38013             case e.ENTER:
38014                 
38015                 if(g.isEditor && !g.editing){
38016                    g.startEditing(r, c);
38017                    e.stopEvent();
38018                    return;
38019                 }
38020                 
38021                 
38022              break;
38023         };
38024         if(newCell){
38025             this.select(newCell[0], newCell[1]);
38026             e.stopEvent();
38027             
38028         }
38029     },
38030
38031     acceptsNav : function(row, col, cm){
38032         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38033     },
38034     /**
38035      * Selects a cell.
38036      * @param {Number} field (not used) - as it's normally used as a listener
38037      * @param {Number} e - event - fake it by using
38038      *
38039      * var e = Roo.EventObjectImpl.prototype;
38040      * e.keyCode = e.TAB
38041      *
38042      * 
38043      */
38044     onEditorKey : function(field, e){
38045         
38046         var k = e.getKey(),
38047             newCell,
38048             g = this.grid,
38049             ed = g.activeEditor,
38050             forward = false;
38051         ///Roo.log('onEditorKey' + k);
38052         
38053         
38054         if (this.enter_is_tab && k == e.ENTER) {
38055             k = e.TAB;
38056         }
38057         
38058         if(k == e.TAB){
38059             if(e.shiftKey){
38060                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38061             }else{
38062                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38063                 forward = true;
38064             }
38065             
38066             e.stopEvent();
38067             
38068         } else if(k == e.ENTER &&  !e.ctrlKey){
38069             ed.completeEdit();
38070             e.stopEvent();
38071             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38072         
38073                 } else if(k == e.ESC){
38074             ed.cancelEdit();
38075         }
38076                 
38077         if (newCell) {
38078             var ecall = { cell : newCell, forward : forward };
38079             this.fireEvent('beforeeditnext', ecall );
38080             newCell = ecall.cell;
38081                         forward = ecall.forward;
38082         }
38083                 
38084         if(newCell){
38085             //Roo.log('next cell after edit');
38086             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38087         } else if (forward) {
38088             // tabbed past last
38089             this.fireEvent.defer(100, this, ['tabend',this]);
38090         }
38091     }
38092 });/*
38093  * Based on:
38094  * Ext JS Library 1.1.1
38095  * Copyright(c) 2006-2007, Ext JS, LLC.
38096  *
38097  * Originally Released Under LGPL - original licence link has changed is not relivant.
38098  *
38099  * Fork - LGPL
38100  * <script type="text/javascript">
38101  */
38102  
38103 /**
38104  * @class Roo.grid.EditorGrid
38105  * @extends Roo.grid.Grid
38106  * Class for creating and editable grid.
38107  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38108  * The container MUST have some type of size defined for the grid to fill. The container will be 
38109  * automatically set to position relative if it isn't already.
38110  * @param {Object} dataSource The data model to bind to
38111  * @param {Object} colModel The column model with info about this grid's columns
38112  */
38113 Roo.grid.EditorGrid = function(container, config){
38114     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38115     this.getGridEl().addClass("xedit-grid");
38116
38117     if(!this.selModel){
38118         this.selModel = new Roo.grid.CellSelectionModel();
38119     }
38120
38121     this.activeEditor = null;
38122
38123         this.addEvents({
38124             /**
38125              * @event beforeedit
38126              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38127              * <ul style="padding:5px;padding-left:16px;">
38128              * <li>grid - This grid</li>
38129              * <li>record - The record being edited</li>
38130              * <li>field - The field name being edited</li>
38131              * <li>value - The value for the field being edited.</li>
38132              * <li>row - The grid row index</li>
38133              * <li>column - The grid column index</li>
38134              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38135              * </ul>
38136              * @param {Object} e An edit event (see above for description)
38137              */
38138             "beforeedit" : true,
38139             /**
38140              * @event afteredit
38141              * Fires after a cell is edited. <br />
38142              * <ul style="padding:5px;padding-left:16px;">
38143              * <li>grid - This grid</li>
38144              * <li>record - The record being edited</li>
38145              * <li>field - The field name being edited</li>
38146              * <li>value - The value being set</li>
38147              * <li>originalValue - The original value for the field, before the edit.</li>
38148              * <li>row - The grid row index</li>
38149              * <li>column - The grid column index</li>
38150              * </ul>
38151              * @param {Object} e An edit event (see above for description)
38152              */
38153             "afteredit" : true,
38154             /**
38155              * @event validateedit
38156              * Fires after a cell is edited, but before the value is set in the record. 
38157          * You can use this to modify the value being set in the field, Return false
38158              * to cancel the change. The edit event object has the following properties <br />
38159              * <ul style="padding:5px;padding-left:16px;">
38160          * <li>editor - This editor</li>
38161              * <li>grid - This grid</li>
38162              * <li>record - The record being edited</li>
38163              * <li>field - The field name being edited</li>
38164              * <li>value - The value being set</li>
38165              * <li>originalValue - The original value for the field, before the edit.</li>
38166              * <li>row - The grid row index</li>
38167              * <li>column - The grid column index</li>
38168              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38169              * </ul>
38170              * @param {Object} e An edit event (see above for description)
38171              */
38172             "validateedit" : true
38173         });
38174     this.on("bodyscroll", this.stopEditing,  this);
38175     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38176 };
38177
38178 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38179     /**
38180      * @cfg {Number} clicksToEdit
38181      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38182      */
38183     clicksToEdit: 2,
38184
38185     // private
38186     isEditor : true,
38187     // private
38188     trackMouseOver: false, // causes very odd FF errors
38189
38190     onCellDblClick : function(g, row, col){
38191         this.startEditing(row, col);
38192     },
38193
38194     onEditComplete : function(ed, value, startValue){
38195         this.editing = false;
38196         this.activeEditor = null;
38197         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
38198         var r = ed.record;
38199         var field = this.colModel.getDataIndex(ed.col);
38200         var e = {
38201             grid: this,
38202             record: r,
38203             field: field,
38204             originalValue: startValue,
38205             value: value,
38206             row: ed.row,
38207             column: ed.col,
38208             cancel:false,
38209             editor: ed
38210         };
38211         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
38212         cell.show();
38213           
38214         if(String(value) !== String(startValue)){
38215             
38216             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
38217                 r.set(field, e.value);
38218                 // if we are dealing with a combo box..
38219                 // then we also set the 'name' colum to be the displayField
38220                 if (ed.field.displayField && ed.field.name) {
38221                     r.set(ed.field.name, ed.field.el.dom.value);
38222                 }
38223                 
38224                 delete e.cancel; //?? why!!!
38225                 this.fireEvent("afteredit", e);
38226             }
38227         } else {
38228             this.fireEvent("afteredit", e); // always fire it!
38229         }
38230         this.view.focusCell(ed.row, ed.col);
38231     },
38232
38233     /**
38234      * Starts editing the specified for the specified row/column
38235      * @param {Number} rowIndex
38236      * @param {Number} colIndex
38237      */
38238     startEditing : function(row, col){
38239         this.stopEditing();
38240         if(this.colModel.isCellEditable(col, row)){
38241             this.view.ensureVisible(row, col, true);
38242           
38243             var r = this.dataSource.getAt(row);
38244             var field = this.colModel.getDataIndex(col);
38245             var cell = Roo.get(this.view.getCell(row,col));
38246             var e = {
38247                 grid: this,
38248                 record: r,
38249                 field: field,
38250                 value: r.data[field],
38251                 row: row,
38252                 column: col,
38253                 cancel:false 
38254             };
38255             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
38256                 this.editing = true;
38257                 var ed = this.colModel.getCellEditor(col, row);
38258                 
38259                 if (!ed) {
38260                     return;
38261                 }
38262                 if(!ed.rendered){
38263                     ed.render(ed.parentEl || document.body);
38264                 }
38265                 ed.field.reset();
38266                
38267                 cell.hide();
38268                 
38269                 (function(){ // complex but required for focus issues in safari, ie and opera
38270                     ed.row = row;
38271                     ed.col = col;
38272                     ed.record = r;
38273                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
38274                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
38275                     this.activeEditor = ed;
38276                     var v = r.data[field];
38277                     ed.startEdit(this.view.getCell(row, col), v);
38278                     // combo's with 'displayField and name set
38279                     if (ed.field.displayField && ed.field.name) {
38280                         ed.field.el.dom.value = r.data[ed.field.name];
38281                     }
38282                     
38283                     
38284                 }).defer(50, this);
38285             }
38286         }
38287     },
38288         
38289     /**
38290      * Stops any active editing
38291      */
38292     stopEditing : function(){
38293         if(this.activeEditor){
38294             this.activeEditor.completeEdit();
38295         }
38296         this.activeEditor = null;
38297     }
38298 });/*
38299  * Based on:
38300  * Ext JS Library 1.1.1
38301  * Copyright(c) 2006-2007, Ext JS, LLC.
38302  *
38303  * Originally Released Under LGPL - original licence link has changed is not relivant.
38304  *
38305  * Fork - LGPL
38306  * <script type="text/javascript">
38307  */
38308
38309 // private - not really -- you end up using it !
38310 // This is a support class used internally by the Grid components
38311
38312 /**
38313  * @class Roo.grid.GridEditor
38314  * @extends Roo.Editor
38315  * Class for creating and editable grid elements.
38316  * @param {Object} config any settings (must include field)
38317  */
38318 Roo.grid.GridEditor = function(field, config){
38319     if (!config && field.field) {
38320         config = field;
38321         field = Roo.factory(config.field, Roo.form);
38322     }
38323     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
38324     field.monitorTab = false;
38325 };
38326
38327 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
38328     
38329     /**
38330      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
38331      */
38332     
38333     alignment: "tl-tl",
38334     autoSize: "width",
38335     hideEl : false,
38336     cls: "x-small-editor x-grid-editor",
38337     shim:false,
38338     shadow:"frame"
38339 });/*
38340  * Based on:
38341  * Ext JS Library 1.1.1
38342  * Copyright(c) 2006-2007, Ext JS, LLC.
38343  *
38344  * Originally Released Under LGPL - original licence link has changed is not relivant.
38345  *
38346  * Fork - LGPL
38347  * <script type="text/javascript">
38348  */
38349   
38350
38351   
38352 Roo.grid.PropertyRecord = Roo.data.Record.create([
38353     {name:'name',type:'string'},  'value'
38354 ]);
38355
38356
38357 Roo.grid.PropertyStore = function(grid, source){
38358     this.grid = grid;
38359     this.store = new Roo.data.Store({
38360         recordType : Roo.grid.PropertyRecord
38361     });
38362     this.store.on('update', this.onUpdate,  this);
38363     if(source){
38364         this.setSource(source);
38365     }
38366     Roo.grid.PropertyStore.superclass.constructor.call(this);
38367 };
38368
38369
38370
38371 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38372     setSource : function(o){
38373         this.source = o;
38374         this.store.removeAll();
38375         var data = [];
38376         for(var k in o){
38377             if(this.isEditableValue(o[k])){
38378                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38379             }
38380         }
38381         this.store.loadRecords({records: data}, {}, true);
38382     },
38383
38384     onUpdate : function(ds, record, type){
38385         if(type == Roo.data.Record.EDIT){
38386             var v = record.data['value'];
38387             var oldValue = record.modified['value'];
38388             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38389                 this.source[record.id] = v;
38390                 record.commit();
38391                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38392             }else{
38393                 record.reject();
38394             }
38395         }
38396     },
38397
38398     getProperty : function(row){
38399        return this.store.getAt(row);
38400     },
38401
38402     isEditableValue: function(val){
38403         if(val && val instanceof Date){
38404             return true;
38405         }else if(typeof val == 'object' || typeof val == 'function'){
38406             return false;
38407         }
38408         return true;
38409     },
38410
38411     setValue : function(prop, value){
38412         this.source[prop] = value;
38413         this.store.getById(prop).set('value', value);
38414     },
38415
38416     getSource : function(){
38417         return this.source;
38418     }
38419 });
38420
38421 Roo.grid.PropertyColumnModel = function(grid, store){
38422     this.grid = grid;
38423     var g = Roo.grid;
38424     g.PropertyColumnModel.superclass.constructor.call(this, [
38425         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38426         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38427     ]);
38428     this.store = store;
38429     this.bselect = Roo.DomHelper.append(document.body, {
38430         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38431             {tag: 'option', value: 'true', html: 'true'},
38432             {tag: 'option', value: 'false', html: 'false'}
38433         ]
38434     });
38435     Roo.id(this.bselect);
38436     var f = Roo.form;
38437     this.editors = {
38438         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38439         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38440         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38441         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38442         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38443     };
38444     this.renderCellDelegate = this.renderCell.createDelegate(this);
38445     this.renderPropDelegate = this.renderProp.createDelegate(this);
38446 };
38447
38448 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38449     
38450     
38451     nameText : 'Name',
38452     valueText : 'Value',
38453     
38454     dateFormat : 'm/j/Y',
38455     
38456     
38457     renderDate : function(dateVal){
38458         return dateVal.dateFormat(this.dateFormat);
38459     },
38460
38461     renderBool : function(bVal){
38462         return bVal ? 'true' : 'false';
38463     },
38464
38465     isCellEditable : function(colIndex, rowIndex){
38466         return colIndex == 1;
38467     },
38468
38469     getRenderer : function(col){
38470         return col == 1 ?
38471             this.renderCellDelegate : this.renderPropDelegate;
38472     },
38473
38474     renderProp : function(v){
38475         return this.getPropertyName(v);
38476     },
38477
38478     renderCell : function(val){
38479         var rv = val;
38480         if(val instanceof Date){
38481             rv = this.renderDate(val);
38482         }else if(typeof val == 'boolean'){
38483             rv = this.renderBool(val);
38484         }
38485         return Roo.util.Format.htmlEncode(rv);
38486     },
38487
38488     getPropertyName : function(name){
38489         var pn = this.grid.propertyNames;
38490         return pn && pn[name] ? pn[name] : name;
38491     },
38492
38493     getCellEditor : function(colIndex, rowIndex){
38494         var p = this.store.getProperty(rowIndex);
38495         var n = p.data['name'], val = p.data['value'];
38496         
38497         if(typeof(this.grid.customEditors[n]) == 'string'){
38498             return this.editors[this.grid.customEditors[n]];
38499         }
38500         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38501             return this.grid.customEditors[n];
38502         }
38503         if(val instanceof Date){
38504             return this.editors['date'];
38505         }else if(typeof val == 'number'){
38506             return this.editors['number'];
38507         }else if(typeof val == 'boolean'){
38508             return this.editors['boolean'];
38509         }else{
38510             return this.editors['string'];
38511         }
38512     }
38513 });
38514
38515 /**
38516  * @class Roo.grid.PropertyGrid
38517  * @extends Roo.grid.EditorGrid
38518  * This class represents the  interface of a component based property grid control.
38519  * <br><br>Usage:<pre><code>
38520  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38521       
38522  });
38523  // set any options
38524  grid.render();
38525  * </code></pre>
38526   
38527  * @constructor
38528  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38529  * The container MUST have some type of size defined for the grid to fill. The container will be
38530  * automatically set to position relative if it isn't already.
38531  * @param {Object} config A config object that sets properties on this grid.
38532  */
38533 Roo.grid.PropertyGrid = function(container, config){
38534     config = config || {};
38535     var store = new Roo.grid.PropertyStore(this);
38536     this.store = store;
38537     var cm = new Roo.grid.PropertyColumnModel(this, store);
38538     store.store.sort('name', 'ASC');
38539     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38540         ds: store.store,
38541         cm: cm,
38542         enableColLock:false,
38543         enableColumnMove:false,
38544         stripeRows:false,
38545         trackMouseOver: false,
38546         clicksToEdit:1
38547     }, config));
38548     this.getGridEl().addClass('x-props-grid');
38549     this.lastEditRow = null;
38550     this.on('columnresize', this.onColumnResize, this);
38551     this.addEvents({
38552          /**
38553              * @event beforepropertychange
38554              * Fires before a property changes (return false to stop?)
38555              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38556              * @param {String} id Record Id
38557              * @param {String} newval New Value
38558          * @param {String} oldval Old Value
38559              */
38560         "beforepropertychange": true,
38561         /**
38562              * @event propertychange
38563              * Fires after a property changes
38564              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38565              * @param {String} id Record Id
38566              * @param {String} newval New Value
38567          * @param {String} oldval Old Value
38568              */
38569         "propertychange": true
38570     });
38571     this.customEditors = this.customEditors || {};
38572 };
38573 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38574     
38575      /**
38576      * @cfg {Object} customEditors map of colnames=> custom editors.
38577      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38578      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38579      * false disables editing of the field.
38580          */
38581     
38582       /**
38583      * @cfg {Object} propertyNames map of property Names to their displayed value
38584          */
38585     
38586     render : function(){
38587         Roo.grid.PropertyGrid.superclass.render.call(this);
38588         this.autoSize.defer(100, this);
38589     },
38590
38591     autoSize : function(){
38592         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38593         if(this.view){
38594             this.view.fitColumns();
38595         }
38596     },
38597
38598     onColumnResize : function(){
38599         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38600         this.autoSize();
38601     },
38602     /**
38603      * Sets the data for the Grid
38604      * accepts a Key => Value object of all the elements avaiable.
38605      * @param {Object} data  to appear in grid.
38606      */
38607     setSource : function(source){
38608         this.store.setSource(source);
38609         //this.autoSize();
38610     },
38611     /**
38612      * Gets all the data from the grid.
38613      * @return {Object} data  data stored in grid
38614      */
38615     getSource : function(){
38616         return this.store.getSource();
38617     }
38618 });/*
38619  * Based on:
38620  * Ext JS Library 1.1.1
38621  * Copyright(c) 2006-2007, Ext JS, LLC.
38622  *
38623  * Originally Released Under LGPL - original licence link has changed is not relivant.
38624  *
38625  * Fork - LGPL
38626  * <script type="text/javascript">
38627  */
38628  
38629 /**
38630  * @class Roo.LoadMask
38631  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38632  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38633  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38634  * element's UpdateManager load indicator and will be destroyed after the initial load.
38635  * @constructor
38636  * Create a new LoadMask
38637  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38638  * @param {Object} config The config object
38639  */
38640 Roo.LoadMask = function(el, config){
38641     this.el = Roo.get(el);
38642     Roo.apply(this, config);
38643     if(this.store){
38644         this.store.on('beforeload', this.onBeforeLoad, this);
38645         this.store.on('load', this.onLoad, this);
38646         this.store.on('loadexception', this.onLoadException, this);
38647         this.removeMask = false;
38648     }else{
38649         var um = this.el.getUpdateManager();
38650         um.showLoadIndicator = false; // disable the default indicator
38651         um.on('beforeupdate', this.onBeforeLoad, this);
38652         um.on('update', this.onLoad, this);
38653         um.on('failure', this.onLoad, this);
38654         this.removeMask = true;
38655     }
38656 };
38657
38658 Roo.LoadMask.prototype = {
38659     /**
38660      * @cfg {Boolean} removeMask
38661      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38662      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38663      */
38664     /**
38665      * @cfg {String} msg
38666      * The text to display in a centered loading message box (defaults to 'Loading...')
38667      */
38668     msg : 'Loading...',
38669     /**
38670      * @cfg {String} msgCls
38671      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38672      */
38673     msgCls : 'x-mask-loading',
38674
38675     /**
38676      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38677      * @type Boolean
38678      */
38679     disabled: false,
38680
38681     /**
38682      * Disables the mask to prevent it from being displayed
38683      */
38684     disable : function(){
38685        this.disabled = true;
38686     },
38687
38688     /**
38689      * Enables the mask so that it can be displayed
38690      */
38691     enable : function(){
38692         this.disabled = false;
38693     },
38694     
38695     onLoadException : function()
38696     {
38697         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38698             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38699         }
38700         this.el.unmask(this.removeMask);
38701     },
38702     // private
38703     onLoad : function()
38704     {
38705         this.el.unmask(this.removeMask);
38706     },
38707
38708     // private
38709     onBeforeLoad : function(){
38710         if(!this.disabled){
38711             this.el.mask(this.msg, this.msgCls);
38712         }
38713     },
38714
38715     // private
38716     destroy : function(){
38717         if(this.store){
38718             this.store.un('beforeload', this.onBeforeLoad, this);
38719             this.store.un('load', this.onLoad, this);
38720             this.store.un('loadexception', this.onLoadException, this);
38721         }else{
38722             var um = this.el.getUpdateManager();
38723             um.un('beforeupdate', this.onBeforeLoad, this);
38724             um.un('update', this.onLoad, this);
38725             um.un('failure', this.onLoad, this);
38726         }
38727     }
38728 };/*
38729  * Based on:
38730  * Ext JS Library 1.1.1
38731  * Copyright(c) 2006-2007, Ext JS, LLC.
38732  *
38733  * Originally Released Under LGPL - original licence link has changed is not relivant.
38734  *
38735  * Fork - LGPL
38736  * <script type="text/javascript">
38737  */
38738
38739
38740 /**
38741  * @class Roo.XTemplate
38742  * @extends Roo.Template
38743  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38744 <pre><code>
38745 var t = new Roo.XTemplate(
38746         '&lt;select name="{name}"&gt;',
38747                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38748         '&lt;/select&gt;'
38749 );
38750  
38751 // then append, applying the master template values
38752  </code></pre>
38753  *
38754  * Supported features:
38755  *
38756  *  Tags:
38757
38758 <pre><code>
38759       {a_variable} - output encoded.
38760       {a_variable.format:("Y-m-d")} - call a method on the variable
38761       {a_variable:raw} - unencoded output
38762       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38763       {a_variable:this.method_on_template(...)} - call a method on the template object.
38764  
38765 </code></pre>
38766  *  The tpl tag:
38767 <pre><code>
38768         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38769         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38770         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38771         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38772   
38773         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38774         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38775 </code></pre>
38776  *      
38777  */
38778 Roo.XTemplate = function()
38779 {
38780     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38781     if (this.html) {
38782         this.compile();
38783     }
38784 };
38785
38786
38787 Roo.extend(Roo.XTemplate, Roo.Template, {
38788
38789     /**
38790      * The various sub templates
38791      */
38792     tpls : false,
38793     /**
38794      *
38795      * basic tag replacing syntax
38796      * WORD:WORD()
38797      *
38798      * // you can fake an object call by doing this
38799      *  x.t:(test,tesT) 
38800      * 
38801      */
38802     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38803
38804     /**
38805      * compile the template
38806      *
38807      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38808      *
38809      */
38810     compile: function()
38811     {
38812         var s = this.html;
38813      
38814         s = ['<tpl>', s, '</tpl>'].join('');
38815     
38816         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38817             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38818             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38819             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38820             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38821             m,
38822             id     = 0,
38823             tpls   = [];
38824     
38825         while(true == !!(m = s.match(re))){
38826             var forMatch   = m[0].match(nameRe),
38827                 ifMatch   = m[0].match(ifRe),
38828                 execMatch   = m[0].match(execRe),
38829                 namedMatch   = m[0].match(namedRe),
38830                 
38831                 exp  = null, 
38832                 fn   = null,
38833                 exec = null,
38834                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38835                 
38836             if (ifMatch) {
38837                 // if - puts fn into test..
38838                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38839                 if(exp){
38840                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38841                 }
38842             }
38843             
38844             if (execMatch) {
38845                 // exec - calls a function... returns empty if true is  returned.
38846                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38847                 if(exp){
38848                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38849                 }
38850             }
38851             
38852             
38853             if (name) {
38854                 // for = 
38855                 switch(name){
38856                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38857                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38858                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38859                 }
38860             }
38861             var uid = namedMatch ? namedMatch[1] : id;
38862             
38863             
38864             tpls.push({
38865                 id:     namedMatch ? namedMatch[1] : id,
38866                 target: name,
38867                 exec:   exec,
38868                 test:   fn,
38869                 body:   m[1] || ''
38870             });
38871             if (namedMatch) {
38872                 s = s.replace(m[0], '');
38873             } else { 
38874                 s = s.replace(m[0], '{xtpl'+ id + '}');
38875             }
38876             ++id;
38877         }
38878         this.tpls = [];
38879         for(var i = tpls.length-1; i >= 0; --i){
38880             this.compileTpl(tpls[i]);
38881             this.tpls[tpls[i].id] = tpls[i];
38882         }
38883         this.master = tpls[tpls.length-1];
38884         return this;
38885     },
38886     /**
38887      * same as applyTemplate, except it's done to one of the subTemplates
38888      * when using named templates, you can do:
38889      *
38890      * var str = pl.applySubTemplate('your-name', values);
38891      *
38892      * 
38893      * @param {Number} id of the template
38894      * @param {Object} values to apply to template
38895      * @param {Object} parent (normaly the instance of this object)
38896      */
38897     applySubTemplate : function(id, values, parent)
38898     {
38899         
38900         
38901         var t = this.tpls[id];
38902         
38903         
38904         try { 
38905             if(t.test && !t.test.call(this, values, parent)){
38906                 return '';
38907             }
38908         } catch(e) {
38909             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38910             Roo.log(e.toString());
38911             Roo.log(t.test);
38912             return ''
38913         }
38914         try { 
38915             
38916             if(t.exec && t.exec.call(this, values, parent)){
38917                 return '';
38918             }
38919         } catch(e) {
38920             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38921             Roo.log(e.toString());
38922             Roo.log(t.exec);
38923             return ''
38924         }
38925         try {
38926             var vs = t.target ? t.target.call(this, values, parent) : values;
38927             parent = t.target ? values : parent;
38928             if(t.target && vs instanceof Array){
38929                 var buf = [];
38930                 for(var i = 0, len = vs.length; i < len; i++){
38931                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38932                 }
38933                 return buf.join('');
38934             }
38935             return t.compiled.call(this, vs, parent);
38936         } catch (e) {
38937             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38938             Roo.log(e.toString());
38939             Roo.log(t.compiled);
38940             return '';
38941         }
38942     },
38943
38944     compileTpl : function(tpl)
38945     {
38946         var fm = Roo.util.Format;
38947         var useF = this.disableFormats !== true;
38948         var sep = Roo.isGecko ? "+" : ",";
38949         var undef = function(str) {
38950             Roo.log("Property not found :"  + str);
38951             return '';
38952         };
38953         
38954         var fn = function(m, name, format, args)
38955         {
38956             //Roo.log(arguments);
38957             args = args ? args.replace(/\\'/g,"'") : args;
38958             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38959             if (typeof(format) == 'undefined') {
38960                 format= 'htmlEncode';
38961             }
38962             if (format == 'raw' ) {
38963                 format = false;
38964             }
38965             
38966             if(name.substr(0, 4) == 'xtpl'){
38967                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38968             }
38969             
38970             // build an array of options to determine if value is undefined..
38971             
38972             // basically get 'xxxx.yyyy' then do
38973             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38974             //    (function () { Roo.log("Property not found"); return ''; })() :
38975             //    ......
38976             
38977             var udef_ar = [];
38978             var lookfor = '';
38979             Roo.each(name.split('.'), function(st) {
38980                 lookfor += (lookfor.length ? '.': '') + st;
38981                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38982             });
38983             
38984             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38985             
38986             
38987             if(format && useF){
38988                 
38989                 args = args ? ',' + args : "";
38990                  
38991                 if(format.substr(0, 5) != "this."){
38992                     format = "fm." + format + '(';
38993                 }else{
38994                     format = 'this.call("'+ format.substr(5) + '", ';
38995                     args = ", values";
38996                 }
38997                 
38998                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38999             }
39000              
39001             if (args.length) {
39002                 // called with xxyx.yuu:(test,test)
39003                 // change to ()
39004                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
39005             }
39006             // raw.. - :raw modifier..
39007             return "'"+ sep + udef_st  + name + ")"+sep+"'";
39008             
39009         };
39010         var body;
39011         // branched to use + in gecko and [].join() in others
39012         if(Roo.isGecko){
39013             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
39014                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
39015                     "';};};";
39016         }else{
39017             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
39018             body.push(tpl.body.replace(/(\r\n|\n)/g,
39019                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
39020             body.push("'].join('');};};");
39021             body = body.join('');
39022         }
39023         
39024         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
39025        
39026         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
39027         eval(body);
39028         
39029         return this;
39030     },
39031
39032     applyTemplate : function(values){
39033         return this.master.compiled.call(this, values, {});
39034         //var s = this.subs;
39035     },
39036
39037     apply : function(){
39038         return this.applyTemplate.apply(this, arguments);
39039     }
39040
39041  });
39042
39043 Roo.XTemplate.from = function(el){
39044     el = Roo.getDom(el);
39045     return new Roo.XTemplate(el.value || el.innerHTML);
39046 };/*
39047  * Original code for Roojs - LGPL
39048  * <script type="text/javascript">
39049  */
39050  
39051 /**
39052  * @class Roo.XComponent
39053  * A delayed Element creator...
39054  * Or a way to group chunks of interface together.
39055  * 
39056  * Mypart.xyx = new Roo.XComponent({
39057
39058     parent : 'Mypart.xyz', // empty == document.element.!!
39059     order : '001',
39060     name : 'xxxx'
39061     region : 'xxxx'
39062     disabled : function() {} 
39063      
39064     tree : function() { // return an tree of xtype declared components
39065         var MODULE = this;
39066         return 
39067         {
39068             xtype : 'NestedLayoutPanel',
39069             // technicall
39070         }
39071      ]
39072  *})
39073  *
39074  *
39075  * It can be used to build a big heiracy, with parent etc.
39076  * or you can just use this to render a single compoent to a dom element
39077  * MYPART.render(Roo.Element | String(id) | dom_element )
39078  * 
39079  * @extends Roo.util.Observable
39080  * @constructor
39081  * @param cfg {Object} configuration of component
39082  * 
39083  */
39084 Roo.XComponent = function(cfg) {
39085     Roo.apply(this, cfg);
39086     this.addEvents({ 
39087         /**
39088              * @event built
39089              * Fires when this the componnt is built
39090              * @param {Roo.XComponent} c the component
39091              */
39092         'built' : true
39093         
39094     });
39095     this.region = this.region || 'center'; // default..
39096     Roo.XComponent.register(this);
39097     this.modules = false;
39098     this.el = false; // where the layout goes..
39099     
39100     
39101 }
39102 Roo.extend(Roo.XComponent, Roo.util.Observable, {
39103     /**
39104      * @property el
39105      * The created element (with Roo.factory())
39106      * @type {Roo.Layout}
39107      */
39108     el  : false,
39109     
39110     /**
39111      * @property el
39112      * for BC  - use el in new code
39113      * @type {Roo.Layout}
39114      */
39115     panel : false,
39116     
39117     /**
39118      * @property layout
39119      * for BC  - use el in new code
39120      * @type {Roo.Layout}
39121      */
39122     layout : false,
39123     
39124      /**
39125      * @cfg {Function|boolean} disabled
39126      * If this module is disabled by some rule, return true from the funtion
39127      */
39128     disabled : false,
39129     
39130     /**
39131      * @cfg {String} parent 
39132      * Name of parent element which it get xtype added to..
39133      */
39134     parent: false,
39135     
39136     /**
39137      * @cfg {String} order
39138      * Used to set the order in which elements are created (usefull for multiple tabs)
39139      */
39140     
39141     order : false,
39142     /**
39143      * @cfg {String} name
39144      * String to display while loading.
39145      */
39146     name : false,
39147     /**
39148      * @cfg {String} region
39149      * Region to render component to (defaults to center)
39150      */
39151     region : 'center',
39152     
39153     /**
39154      * @cfg {Array} items
39155      * A single item array - the first element is the root of the tree..
39156      * It's done this way to stay compatible with the Xtype system...
39157      */
39158     items : false,
39159     
39160     /**
39161      * @property _tree
39162      * The method that retuns the tree of parts that make up this compoennt 
39163      * @type {function}
39164      */
39165     _tree  : false,
39166     
39167      /**
39168      * render
39169      * render element to dom or tree
39170      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
39171      */
39172     
39173     render : function(el)
39174     {
39175         
39176         el = el || false;
39177         var hp = this.parent ? 1 : 0;
39178         
39179         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
39180             // if parent is a '#.....' string, then let's use that..
39181             var ename = this.parent.substr(1)
39182             this.parent = false;
39183             el = Roo.get(ename);
39184             if (!el) {
39185                 Roo.log("Warning - element can not be found :#" + ename );
39186                 return;
39187             }
39188         }
39189         
39190         
39191         if (!this.parent) {
39192             
39193             el = el ? Roo.get(el) : false;      
39194             
39195             // it's a top level one..
39196             this.parent =  {
39197                 el : new Roo.BorderLayout(el || document.body, {
39198                 
39199                      center: {
39200                          titlebar: false,
39201                          autoScroll:false,
39202                          closeOnTab: true,
39203                          tabPosition: 'top',
39204                           //resizeTabs: true,
39205                          alwaysShowTabs: el && hp? false :  true,
39206                          hideTabs: el || !hp ? true :  false,
39207                          minTabWidth: 140
39208                      }
39209                  })
39210             }
39211         }
39212         
39213                 if (!this.parent.el) {
39214                         // probably an old style ctor, which has been disabled.
39215                         return;
39216                         
39217                 }
39218                 // The 'tree' method is  '_tree now' 
39219             
39220         var tree = this._tree ? this._tree() : this.tree();
39221         tree.region = tree.region || this.region;
39222         this.el = this.parent.el.addxtype(tree);
39223         this.fireEvent('built', this);
39224         
39225         this.panel = this.el;
39226         this.layout = this.panel.layout;
39227                 this.parentLayout = this.parent.layout  || false;  
39228          
39229     }
39230     
39231 });
39232
39233 Roo.apply(Roo.XComponent, {
39234     /**
39235      * @property  hideProgress
39236      * true to disable the building progress bar.. usefull on single page renders.
39237      * @type Boolean
39238      */
39239     hideProgress : false,
39240     /**
39241      * @property  buildCompleted
39242      * True when the builder has completed building the interface.
39243      * @type Boolean
39244      */
39245     buildCompleted : false,
39246      
39247     /**
39248      * @property  topModule
39249      * the upper most module - uses document.element as it's constructor.
39250      * @type Object
39251      */
39252      
39253     topModule  : false,
39254       
39255     /**
39256      * @property  modules
39257      * array of modules to be created by registration system.
39258      * @type {Array} of Roo.XComponent
39259      */
39260     
39261     modules : [],
39262     /**
39263      * @property  elmodules
39264      * array of modules to be created by which use #ID 
39265      * @type {Array} of Roo.XComponent
39266      */
39267      
39268     elmodules : [],
39269
39270     
39271     /**
39272      * Register components to be built later.
39273      *
39274      * This solves the following issues
39275      * - Building is not done on page load, but after an authentication process has occured.
39276      * - Interface elements are registered on page load
39277      * - Parent Interface elements may not be loaded before child, so this handles that..
39278      * 
39279      *
39280      * example:
39281      * 
39282      * MyApp.register({
39283           order : '000001',
39284           module : 'Pman.Tab.projectMgr',
39285           region : 'center',
39286           parent : 'Pman.layout',
39287           disabled : false,  // or use a function..
39288         })
39289      
39290      * * @param {Object} details about module
39291      */
39292     register : function(obj) {
39293                 
39294         Roo.XComponent.event.fireEvent('register', obj);
39295         switch(typeof(obj.disabled) ) {
39296                 
39297             case 'undefined':
39298                 break;
39299             
39300             case 'function':
39301                 if ( obj.disabled() ) {
39302                         return;
39303                 }
39304                 break;
39305             
39306             default:
39307                 if (obj.disabled) {
39308                         return;
39309                 }
39310                 break;
39311         }
39312                 
39313         this.modules.push(obj);
39314          
39315     },
39316     /**
39317      * convert a string to an object..
39318      * eg. 'AAA.BBB' -> finds AAA.BBB
39319
39320      */
39321     
39322     toObject : function(str)
39323     {
39324         if (!str || typeof(str) == 'object') {
39325             return str;
39326         }
39327         if (str.substring(0,1) == '#') {
39328             return str;
39329         }
39330
39331         var ar = str.split('.');
39332         var rt, o;
39333         rt = ar.shift();
39334             /** eval:var:o */
39335         try {
39336             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
39337         } catch (e) {
39338             throw "Module not found : " + str;
39339         }
39340         
39341         if (o === false) {
39342             throw "Module not found : " + str;
39343         }
39344         Roo.each(ar, function(e) {
39345             if (typeof(o[e]) == 'undefined') {
39346                 throw "Module not found : " + str;
39347             }
39348             o = o[e];
39349         });
39350         
39351         return o;
39352         
39353     },
39354     
39355     
39356     /**
39357      * move modules into their correct place in the tree..
39358      * 
39359      */
39360     preBuild : function ()
39361     {
39362         var _t = this;
39363         Roo.each(this.modules , function (obj)
39364         {
39365             Roo.XComponent.event.fireEvent('beforebuild', obj);
39366             
39367             var opar = obj.parent;
39368             try { 
39369                 obj.parent = this.toObject(opar);
39370             } catch(e) {
39371                 Roo.log("parent:toObject failed: " + e.toString());
39372                 return;
39373             }
39374             
39375             if (!obj.parent) {
39376                 Roo.debug && Roo.log("GOT top level module");
39377                 Roo.debug && Roo.log(obj);
39378                 obj.modules = new Roo.util.MixedCollection(false, 
39379                     function(o) { return o.order + '' }
39380                 );
39381                 this.topModule = obj;
39382                 return;
39383             }
39384                         // parent is a string (usually a dom element name..)
39385             if (typeof(obj.parent) == 'string') {
39386                 this.elmodules.push(obj);
39387                 return;
39388             }
39389             if (obj.parent.constructor != Roo.XComponent) {
39390                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
39391             }
39392             if (!obj.parent.modules) {
39393                 obj.parent.modules = new Roo.util.MixedCollection(false, 
39394                     function(o) { return o.order + '' }
39395                 );
39396             }
39397             if (obj.parent.disabled) {
39398                 obj.disabled = true;
39399             }
39400             obj.parent.modules.add(obj);
39401         }, this);
39402     },
39403     
39404      /**
39405      * make a list of modules to build.
39406      * @return {Array} list of modules. 
39407      */ 
39408     
39409     buildOrder : function()
39410     {
39411         var _this = this;
39412         var cmp = function(a,b) {   
39413             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
39414         };
39415         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
39416             throw "No top level modules to build";
39417         }
39418         
39419         // make a flat list in order of modules to build.
39420         var mods = this.topModule ? [ this.topModule ] : [];
39421                 
39422         // elmodules (is a list of DOM based modules )
39423         Roo.each(this.elmodules, function(e) {
39424             mods.push(e)
39425         });
39426
39427         
39428         // add modules to their parents..
39429         var addMod = function(m) {
39430             Roo.debug && Roo.log("build Order: add: " + m.name);
39431             
39432         mods.push(m);
39433         if (m.modules && !m.disabled) {
39434             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
39435             m.modules.keySort('ASC',  cmp );
39436             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
39437
39438             m.modules.each(addMod);
39439         } else {
39440             Roo.debug && Roo.log("build Order: no child modules");
39441             }
39442             // not sure if this is used any more..
39443             if (m.finalize) {
39444                 m.finalize.name = m.name + " (clean up) ";
39445                 mods.push(m.finalize);
39446             }
39447             
39448         }
39449         if (this.topModule) { 
39450             this.topModule.modules.keySort('ASC',  cmp );
39451             this.topModule.modules.each(addMod);
39452         }
39453         return mods;
39454     },
39455     
39456      /**
39457      * Build the registered modules.
39458      * @param {Object} parent element.
39459      * @param {Function} optional method to call after module has been added.
39460      * 
39461      */ 
39462    
39463     build : function() 
39464     {
39465         
39466         this.preBuild();
39467         var mods = this.buildOrder();
39468       
39469         //this.allmods = mods;
39470         //Roo.debug && Roo.log(mods);
39471         //return;
39472         if (!mods.length) { // should not happen
39473             throw "NO modules!!!";
39474         }
39475         
39476         
39477         var msg = "Building Interface...";
39478         // flash it up as modal - so we store the mask!?
39479         if (!this.hideProgress) {
39480             Roo.MessageBox.show({ title: 'loading' });
39481             Roo.MessageBox.show({
39482                title: "Please wait...",
39483                msg: msg,
39484                width:450,
39485                progress:true,
39486                closable:false,
39487                modal: false
39488               
39489             });
39490         }
39491         var total = mods.length;
39492         
39493         var _this = this;
39494         var progressRun = function() {
39495             if (!mods.length) {
39496                 Roo.debug && Roo.log('hide?');
39497                 if (!this.hideProgress) {
39498                     Roo.MessageBox.hide();
39499                 }
39500                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
39501                 
39502                 // THE END...
39503                 return false;   
39504             }
39505             
39506             var m = mods.shift();
39507             
39508             
39509             Roo.debug && Roo.log(m);
39510             // not sure if this is supported any more.. - modules that are are just function
39511             if (typeof(m) == 'function') { 
39512                 m.call(this);
39513                 return progressRun.defer(10, _this);
39514             } 
39515             
39516             
39517             msg = "Building Interface " + (total  - mods.length) + 
39518                     " of " + total + 
39519                     (m.name ? (' - ' + m.name) : '');
39520                         Roo.debug && Roo.log(msg);
39521             if (!this.hideProgress) { 
39522                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
39523             }
39524             
39525          
39526             // is the module disabled?
39527             var disabled = (typeof(m.disabled) == 'function') ?
39528                 m.disabled.call(m.module.disabled) : m.disabled;    
39529             
39530             
39531             if (disabled) {
39532                 return progressRun(); // we do not update the display!
39533             }
39534             
39535             // now build 
39536             
39537                         
39538                         
39539             m.render();
39540             // it's 10 on top level, and 1 on others??? why...
39541             return progressRun.defer(10, _this);
39542              
39543         }
39544         progressRun.defer(1, _this);
39545      
39546         
39547         
39548     },
39549         
39550         
39551         /**
39552          * Event Object.
39553          *
39554          *
39555          */
39556         event: false, 
39557     /**
39558          * wrapper for event.on - aliased later..  
39559          * Typically use to register a event handler for register:
39560          *
39561          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
39562          *
39563          */
39564     on : false
39565    
39566     
39567     
39568 });
39569
39570 Roo.XComponent.event = new Roo.util.Observable({
39571                 events : { 
39572                         /**
39573                          * @event register
39574                          * Fires when an Component is registered,
39575                          * set the disable property on the Component to stop registration.
39576                          * @param {Roo.XComponent} c the component being registerd.
39577                          * 
39578                          */
39579                         'register' : true,
39580             /**
39581                          * @event beforebuild
39582                          * Fires before each Component is built
39583                          * can be used to apply permissions.
39584                          * @param {Roo.XComponent} c the component being registerd.
39585                          * 
39586                          */
39587                         'beforebuild' : true,
39588                         /**
39589                          * @event buildcomplete
39590                          * Fires on the top level element when all elements have been built
39591                          * @param {Roo.XComponent} the top level component.
39592                          */
39593                         'buildcomplete' : true
39594                         
39595                 }
39596 });
39597
39598 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
39599  //<script type="text/javascript">
39600
39601
39602 /**
39603  * @class Roo.Login
39604  * @extends Roo.LayoutDialog
39605  * A generic Login Dialog..... - only one needed in theory!?!?
39606  *
39607  * Fires XComponent builder on success...
39608  * 
39609  * Sends 
39610  *    username,password, lang = for login actions.
39611  *    check = 1 for periodic checking that sesion is valid.
39612  *    passwordRequest = email request password
39613  *    logout = 1 = to logout
39614  * 
39615  * Affects: (this id="????" elements)
39616  *   loading  (removed) (used to indicate application is loading)
39617  *   loading-mask (hides) (used to hide application when it's building loading)
39618  *   
39619  * 
39620  * Usage: 
39621  *    
39622  * 
39623  * Myapp.login = Roo.Login({
39624      url: xxxx,
39625    
39626      realm : 'Myapp', 
39627      
39628      
39629      method : 'POST',
39630      
39631      
39632      * 
39633  })
39634  * 
39635  * 
39636  * 
39637  **/
39638  
39639 Roo.Login = function(cfg)
39640 {
39641     this.addEvents({
39642         'refreshed' : true
39643     });
39644     
39645     Roo.apply(this,cfg);
39646     
39647     Roo.onReady(function() {
39648         this.onLoad();
39649     }, this);
39650     // call parent..
39651     
39652    
39653     Roo.Login.superclass.constructor.call(this, this);
39654     //this.addxtype(this.items[0]);
39655     
39656     
39657 }
39658
39659
39660 Roo.extend(Roo.Login, Roo.LayoutDialog, {
39661     
39662     /**
39663      * @cfg {String} method
39664      * Method used to query for login details.
39665      */
39666     
39667     method : 'POST',
39668     /**
39669      * @cfg {String} url
39670      * URL to query login data. - eg. baseURL + '/Login.php'
39671      */
39672     url : '',
39673     
39674     /**
39675      * @property user
39676      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
39677      * @type {Object} 
39678      */
39679     user : false,
39680     /**
39681      * @property checkFails
39682      * Number of times we have attempted to get authentication check, and failed.
39683      * @type {Number} 
39684      */
39685     checkFails : 0,
39686       /**
39687      * @property intervalID
39688      * The window interval that does the constant login checking.
39689      * @type {Number} 
39690      */
39691     intervalID : 0,
39692     
39693     
39694     onLoad : function() // called on page load...
39695     {
39696         // load 
39697          
39698         if (Roo.get('loading')) { // clear any loading indicator..
39699             Roo.get('loading').remove();
39700         }
39701         
39702         //this.switchLang('en'); // set the language to english..
39703        
39704         this.check({
39705             success:  function(response, opts)  {  // check successfull...
39706             
39707                 var res = this.processResponse(response);
39708                 this.checkFails =0;
39709                 if (!res.success) { // error!
39710                     this.checkFails = 5;
39711                     //console.log('call failure');
39712                     return this.failure(response,opts);
39713                 }
39714                 
39715                 if (!res.data.id) { // id=0 == login failure.
39716                     return this.show();
39717                 }
39718                 
39719                               
39720                         //console.log(success);
39721                 this.fillAuth(res.data);   
39722                 this.checkFails =0;
39723                 Roo.XComponent.build();
39724             },
39725             failure : this.show
39726         });
39727         
39728     }, 
39729     
39730     
39731     check: function(cfg) // called every so often to refresh cookie etc..
39732     {
39733         if (cfg.again) { // could be undefined..
39734             this.checkFails++;
39735         } else {
39736             this.checkFails = 0;
39737         }
39738         var _this = this;
39739         if (this.sending) {
39740             if ( this.checkFails > 4) {
39741                 Roo.MessageBox.alert("Error",  
39742                     "Error getting authentication status. - try reloading, or wait a while", function() {
39743                         _this.sending = false;
39744                     }); 
39745                 return;
39746             }
39747             cfg.again = true;
39748             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
39749             return;
39750         }
39751         this.sending = true;
39752         
39753         Roo.Ajax.request({  
39754             url: this.url,
39755             params: {
39756                 getAuthUser: true
39757             },  
39758             method: this.method,
39759             success:  cfg.success || this.success,
39760             failure : cfg.failure || this.failure,
39761             scope : this,
39762             callCfg : cfg
39763               
39764         });  
39765     }, 
39766     
39767     
39768     logout: function()
39769     {
39770         window.onbeforeunload = function() { }; // false does not work for IE..
39771         this.user = false;
39772         var _this = this;
39773         
39774         Roo.Ajax.request({  
39775             url: this.url,
39776             params: {
39777                 logout: 1
39778             },  
39779             method: 'GET',
39780             failure : function() {
39781                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
39782                     document.location = document.location.toString() + '?ts=' + Math.random();
39783                 });
39784                 
39785             },
39786             success : function() {
39787                 _this.user = false;
39788                 this.checkFails =0;
39789                 // fixme..
39790                 document.location = document.location.toString() + '?ts=' + Math.random();
39791             }
39792               
39793               
39794         }); 
39795     },
39796     
39797     processResponse : function (response)
39798     {
39799         var res = '';
39800         try {
39801             res = Roo.decode(response.responseText);
39802             // oops...
39803             if (typeof(res) != 'object') {
39804                 res = { success : false, errorMsg : res, errors : true };
39805             }
39806             if (typeof(res.success) == 'undefined') {
39807                 res.success = false;
39808             }
39809             
39810         } catch(e) {
39811             res = { success : false,  errorMsg : response.responseText, errors : true };
39812         }
39813         return res;
39814     },
39815     
39816     success : function(response, opts)  // check successfull...
39817     {  
39818         this.sending = false;
39819         var res = this.processResponse(response);
39820         if (!res.success) {
39821             return this.failure(response, opts);
39822         }
39823         if (!res.data || !res.data.id) {
39824             return this.failure(response,opts);
39825         }
39826         //console.log(res);
39827         this.fillAuth(res.data);
39828         
39829         this.checkFails =0;
39830         
39831     },
39832     
39833     
39834     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
39835     {
39836         this.authUser = -1;
39837         this.sending = false;
39838         var res = this.processResponse(response);
39839         //console.log(res);
39840         if ( this.checkFails > 2) {
39841         
39842             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
39843                 "Error getting authentication status. - try reloading"); 
39844             return;
39845         }
39846         opts.callCfg.again = true;
39847         this.check.defer(1000, this, [ opts.callCfg ]);
39848         return;  
39849     },
39850     
39851     
39852     
39853     fillAuth: function(au) {
39854         this.startAuthCheck();
39855         this.authUserId = au.id;
39856         this.authUser = au;
39857         this.lastChecked = new Date();
39858         this.fireEvent('refreshed', au);
39859         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
39860         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
39861         au.lang = au.lang || 'en';
39862         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
39863         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
39864         this.switchLang(au.lang );
39865         
39866      
39867         // open system... - -on setyp..
39868         if (this.authUserId  < 0) {
39869             Roo.MessageBox.alert("Warning", 
39870                 "This is an open system - please set up a admin user with a password.");  
39871         }
39872          
39873         //Pman.onload(); // which should do nothing if it's a re-auth result...
39874         
39875              
39876     },
39877     
39878     startAuthCheck : function() // starter for timeout checking..
39879     {
39880         if (this.intervalID) { // timer already in place...
39881             return false;
39882         }
39883         var _this = this;
39884         this.intervalID =  window.setInterval(function() {
39885               _this.check(false);
39886             }, 120000); // every 120 secs = 2mins..
39887         
39888         
39889     },
39890          
39891     
39892     switchLang : function (lang) 
39893     {
39894         _T = typeof(_T) == 'undefined' ? false : _T;
39895           if (!_T || !lang.length) {
39896             return;
39897         }
39898         
39899         if (!_T && lang != 'en') {
39900             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39901             return;
39902         }
39903         
39904         if (typeof(_T.en) == 'undefined') {
39905             _T.en = {};
39906             Roo.apply(_T.en, _T);
39907         }
39908         
39909         if (typeof(_T[lang]) == 'undefined') {
39910             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39911             return;
39912         }
39913         
39914         
39915         Roo.apply(_T, _T[lang]);
39916         // just need to set the text values for everything...
39917         var _this = this;
39918         /* this will not work ...
39919         if (this.form) { 
39920             
39921                
39922             function formLabel(name, val) {
39923                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
39924             }
39925             
39926             formLabel('password', "Password"+':');
39927             formLabel('username', "Email Address"+':');
39928             formLabel('lang', "Language"+':');
39929             this.dialog.setTitle("Login");
39930             this.dialog.buttons[0].setText("Forgot Password");
39931             this.dialog.buttons[1].setText("Login");
39932         }
39933         */
39934         
39935         
39936     },
39937     
39938     
39939     title: "Login",
39940     modal: true,
39941     width:  350,
39942     //height: 230,
39943     height: 180,
39944     shadow: true,
39945     minWidth:200,
39946     minHeight:180,
39947     //proxyDrag: true,
39948     closable: false,
39949     draggable: false,
39950     collapsible: false,
39951     resizable: false,
39952     center: {  // needed??
39953         autoScroll:false,
39954         titlebar: false,
39955        // tabPosition: 'top',
39956         hideTabs: true,
39957         closeOnTab: true,
39958         alwaysShowTabs: false
39959     } ,
39960     listeners : {
39961         
39962         show  : function(dlg)
39963         {
39964             //console.log(this);
39965             this.form = this.layout.getRegion('center').activePanel.form;
39966             this.form.dialog = dlg;
39967             this.buttons[0].form = this.form;
39968             this.buttons[0].dialog = dlg;
39969             this.buttons[1].form = this.form;
39970             this.buttons[1].dialog = dlg;
39971            
39972            //this.resizeToLogo.defer(1000,this);
39973             // this is all related to resizing for logos..
39974             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
39975            //// if (!sz) {
39976              //   this.resizeToLogo.defer(1000,this);
39977              //   return;
39978            // }
39979             //var w = Ext.lib.Dom.getViewWidth() - 100;
39980             //var h = Ext.lib.Dom.getViewHeight() - 100;
39981             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
39982             //this.center();
39983             if (this.disabled) {
39984                 this.hide();
39985                 return;
39986             }
39987             
39988             if (this.user.id < 0) { // used for inital setup situations.
39989                 return;
39990             }
39991             
39992             if (this.intervalID) {
39993                 // remove the timer
39994                 window.clearInterval(this.intervalID);
39995                 this.intervalID = false;
39996             }
39997             
39998             
39999             if (Roo.get('loading')) {
40000                 Roo.get('loading').remove();
40001             }
40002             if (Roo.get('loading-mask')) {
40003                 Roo.get('loading-mask').hide();
40004             }
40005             
40006             //incomming._node = tnode;
40007             this.form.reset();
40008             //this.dialog.modal = !modal;
40009             //this.dialog.show();
40010             this.el.unmask(); 
40011             
40012             
40013             this.form.setValues({
40014                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
40015                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
40016             });
40017             
40018             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
40019             if (this.form.findField('username').getValue().length > 0 ){
40020                 this.form.findField('password').focus();
40021             } else {
40022                this.form.findField('username').focus();
40023             }
40024     
40025         }
40026     },
40027     items : [
40028          {
40029        
40030             xtype : 'ContentPanel',
40031             xns : Roo,
40032             region: 'center',
40033             fitToFrame : true,
40034             
40035             items : [
40036     
40037                 {
40038                
40039                     xtype : 'Form',
40040                     xns : Roo.form,
40041                     labelWidth: 100,
40042                     style : 'margin: 10px;',
40043                     
40044                     listeners : {
40045                         actionfailed : function(f, act) {
40046                             // form can return { errors: .... }
40047                                 
40048                             //act.result.errors // invalid form element list...
40049                             //act.result.errorMsg// invalid form element list...
40050                             
40051                             this.dialog.el.unmask();
40052                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
40053                                         "Login failed - communication error - try again.");
40054                                       
40055                         },
40056                         actioncomplete: function(re, act) {
40057                              
40058                             Roo.state.Manager.set(
40059                                 this.dialog.realm + '.username',  
40060                                     this.findField('username').getValue()
40061                             );
40062                             Roo.state.Manager.set(
40063                                 this.dialog.realm + '.lang',  
40064                                 this.findField('lang').getValue() 
40065                             );
40066                             
40067                             this.dialog.fillAuth(act.result.data);
40068                               
40069                             this.dialog.hide();
40070                             
40071                             if (Roo.get('loading-mask')) {
40072                                 Roo.get('loading-mask').show();
40073                             }
40074                             Roo.XComponent.build();
40075                             
40076                              
40077                             
40078                         }
40079                     },
40080                     items : [
40081                         {
40082                             xtype : 'TextField',
40083                             xns : Roo.form,
40084                             fieldLabel: "Email Address",
40085                             name: 'username',
40086                             width:200,
40087                             autoCreate : {tag: "input", type: "text", size: "20"}
40088                         },
40089                         {
40090                             xtype : 'TextField',
40091                             xns : Roo.form,
40092                             fieldLabel: "Password",
40093                             inputType: 'password',
40094                             name: 'password',
40095                             width:200,
40096                             autoCreate : {tag: "input", type: "text", size: "20"},
40097                             listeners : {
40098                                 specialkey : function(e,ev) {
40099                                     if (ev.keyCode == 13) {
40100                                         this.form.dialog.el.mask("Logging in");
40101                                         this.form.doAction('submit', {
40102                                             url: this.form.dialog.url,
40103                                             method: this.form.dialog.method
40104                                         });
40105                                     }
40106                                 }
40107                             }  
40108                         },
40109                         {
40110                             xtype : 'ComboBox',
40111                             xns : Roo.form,
40112                             fieldLabel: "Language",
40113                             name : 'langdisp',
40114                             store: {
40115                                 xtype : 'SimpleStore',
40116                                 fields: ['lang', 'ldisp'],
40117                                 data : [
40118                                     [ 'en', 'English' ],
40119                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
40120                                     [ 'zh_CN', '\u7C21\u4E2D' ]
40121                                 ]
40122                             },
40123                             
40124                             valueField : 'lang',
40125                             hiddenName:  'lang',
40126                             width: 200,
40127                             displayField:'ldisp',
40128                             typeAhead: false,
40129                             editable: false,
40130                             mode: 'local',
40131                             triggerAction: 'all',
40132                             emptyText:'Select a Language...',
40133                             selectOnFocus:true,
40134                             listeners : {
40135                                 select :  function(cb, rec, ix) {
40136                                     this.form.switchLang(rec.data.lang);
40137                                 }
40138                             }
40139                         
40140                         }
40141                     ]
40142                 }
40143                   
40144                 
40145             ]
40146         }
40147     ],
40148     buttons : [
40149         {
40150             xtype : 'Button',
40151             xns : 'Roo',
40152             text : "Forgot Password",
40153             listeners : {
40154                 click : function() {
40155                     //console.log(this);
40156                     var n = this.form.findField('username').getValue();
40157                     if (!n.length) {
40158                         Roo.MessageBox.alert("Error", "Fill in your email address");
40159                         return;
40160                     }
40161                     Roo.Ajax.request({
40162                         url: this.dialog.url,
40163                         params: {
40164                             passwordRequest: n
40165                         },
40166                         method: this.dialog.method,
40167                         success:  function(response, opts)  {  // check successfull...
40168                         
40169                             var res = this.dialog.processResponse(response);
40170                             if (!res.success) { // error!
40171                                Roo.MessageBox.alert("Error" ,
40172                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
40173                                return;
40174                             }
40175                             Roo.MessageBox.alert("Notice" ,
40176                                 "Please check you email for the Password Reset message");
40177                         },
40178                         failure : function() {
40179                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
40180                         }
40181                         
40182                     });
40183                 }
40184             }
40185         },
40186         {
40187             xtype : 'Button',
40188             xns : 'Roo',
40189             text : "Login",
40190             listeners : {
40191                 
40192                 click : function () {
40193                         
40194                     this.dialog.el.mask("Logging in");
40195                     this.form.doAction('submit', {
40196                             url: this.dialog.url,
40197                             method: this.dialog.method
40198                     });
40199                 }
40200             }
40201         }
40202     ]
40203   
40204   
40205 })
40206  
40207
40208
40209